理解自动配置Bean(auto-configuration)
自动配置类是标准的@Configuration类,附加用来限制是否生效的条件注解(@Conditional)。如@ConditionalOnClass  @ConditionalOnMissingBean 注解. 这个可以确认在相关的类存在或未申明某Bean时才生效这个自动配置类。
自动配置类放置在类路径classpash下的META-INF/spring.factories文件里。SpringBoot自带的spring.factories在spring-boot-autoconfiguration模块jar里,默认的配置内容有(当前版本:2.30):
# Initializersorg.springframework.context.ApplicationContextInitializer=\org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener# Application Listenersorg.springframework.context.ApplicationListener=\org.springframework.boot.autoconfigure.BackgroundPreinitializer# Auto Configuration Import Listenersorg.springframework.boot.autoconfigure.AutoConfigurationImportListener=\org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener# Auto Configuration Import Filtersorg.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\org.springframework.boot.autoconfigure.condition.OnBeanCondition,\org.springframework.boot.autoconfigure.condition.OnClassCondition,\org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\....
查找自己的自动配置候选类
在自己的Maven工程里,新建一份spring.factories文件放到recourses/META-INF/目录下:
app--src--main--resources-- META-INF--spring.factories--test
配置内容格式为,属性键值对,Key=org.springframework.boot.autoconfigure.EnableAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=xxx.emoAutoConfiguration
自动配置只能用这种方式存储并加载,需要确保这个路径不会被自动扫描,同时不能配置类似@ComponentScan这样的注解,但是可以配置@Import来导入额外的配置类。
可以使用@AutoConfigureAfter,@AutoConfigureBefore注解来应用配置类在特定的顺序下。例如如果是Web应用,你一般需要再WebMvcAutoConfiguration生效只会才加载你自己的配置类。同时你还可以使用@AutoConfigureOrder来指定你的配置类的生效顺序。
使用条件注解(@Conditional)
Class Conditions
@ConditionalOnClass
通过查找Name是否存在,如果存在配置类会被注册为Bean,同时生效配置类中的@Bean.
@Configuration(proxyBeanMethods = true)static class OnClassCondition {// Auto-configured beans@Beanpublic List<String> list() {return Lists.newArrayList("A", "B");}@Bean("userService")public UserService userService1() {return new UserService();}@Bean@ConditionalOnMissingBean(name = "userService2")public UserService userService2() {return new UserService();}@Configuration(proxyBeanMethods = false)@ConditionalOnClass(name = "org.test.EmbeddedAcmeService")static class EmbeddedConfiguration {@Beanpublic String hello() {return "Hello";}@Bean@ConditionalOnMissingBeanpublic UserService userService() {return new UserService();}}}@Testpublic void testOnClassCondition() {ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OnClassCondition.class));contextRunner.withUserConfiguration(OnClassCondition.class).run((context) -> {System.out.println(Arrays.toString(context.getBeanDefinitionNames()));assertThat(context).getBean(OnClassCondition.class).isNotNull();List<String> list = (List<String>) context.getBean("list");assertThat(list).hasSize(2);assertThat(context).getBean(UserService.class).isNull();assertThat(context).getBean("hello").isNull();});}
@ConditionalOnMissingClass
如果Name指定的类不存在,则配置类生效,会被注册为Bean,同时生效配置类里的@Bean
@Configuration(proxyBeanMethods = false)static class OnClassMissingCondition {// Auto-configured beans@Configuration(proxyBeanMethods = false)@ConditionalOnMissingClass("org.test.EmbeddedAcmeService")static class EmbeddedConfiguration {@Beanpublic String hello() {return "world";}@Bean@ConditionalOnMissingBeanpublic UserService userService() {return new UserService();}}}@Testpublic void testOnClassMissingCondition() {ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OnClassMissingCondition.class));contextRunner.withUserConfiguration(OnClassMissingCondition.class).run((context) -> {System.out.println(Arrays.toString(context.getBeanDefinitionNames()));assertThat(context).getBean(OnClassMissingCondition.class).isNotNull();assertThat(context).getBean(OnClassMissingCondition.EmbeddedConfiguration.class).isNotNull();assertThat(context).getBean(UserService.class).isNotNull();assertThat(context).getBean("hello").hasToString("world");});}
Bean Conditions
@ConditionalOnMissingBean
@ConditionalOnBean
Bean存在时则生效配置类
Property Conditions
@ConditionalOnProperty
属性文件里配置了某属性=true,才生效。matchIfMission=true,表示如没有这个属性,默认生效。
如:spring.example.enble=true
@Configuration(proxyBeanMethods = false)@ConditionalOnProperty(prefix = "spring.example", name = "enable", matchIfMissing = true)static class OnPropertyCondition {@Beanpublic UserService userService() {return new UserService();}}@Testpublic void testOnPropertyCondition() {ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OnPropertyCondition.class));contextRunner.withUserConfiguration(OnPropertyCondition.class).withPropertyValues("spring.example.enable=false").run((context) -> {System.out.println(Arrays.toString(context.getBeanDefinitionNames()));assertThat(context).getBean(OnPropertyCondition.class).isNull();assertThat(context).getBean(UserService.class).isNull();});}
SpringBoot内置AutoConfiguration示例
AopAutoConfiguration
ConditionalOnProperty+ConditionalOnClass
/*** {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration* Auto-configuration} for Spring's AOP support. Equivalent to enabling* {@link EnableAspectJAutoProxy @EnableAspectJAutoProxy} in your configuration.* <p>* The configuration will not be activated if {@literal spring.aop.auto=false}. The* {@literal proxyTargetClass} attribute will be {@literal true}, by default, but can be* overridden by specifying {@literal spring.aop.proxy-target-class=false}.** @author Dave Syer* @author Josh Long* @since 1.0.0* @see EnableAspectJAutoProxy*/@Configuration(proxyBeanMethods = false)@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)public class AopAutoConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass(Advice.class)static class AspectJAutoProxyingConfiguration {@Configuration(proxyBeanMethods = false)@EnableAspectJAutoProxy(proxyTargetClass = false)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",matchIfMissing = false)static class JdkDynamicAutoProxyConfiguration {}@Configuration(proxyBeanMethods = false)@EnableAspectJAutoProxy(proxyTargetClass = true)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",matchIfMissing = true)static class CglibAutoProxyConfiguration {}}@Configuration(proxyBeanMethods = false)@ConditionalOnMissingClass("org.aspectj.weaver.Advice")@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",matchIfMissing = true)static class ClassProxyingConfiguration {ClassProxyingConfiguration(BeanFactory beanFactory) {if (beanFactory instanceof BeanDefinitionRegistry) {BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}}}}
PropertyPlaceholderAutoConfiguration
AutoConfigureOrder+ConditionalOnMissingBean
@Configuration(proxyBeanMethods = false)@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)public class PropertyPlaceholderAutoConfiguration {@Bean@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {return new PropertySourcesPlaceholderConfigurer();}}
DispatcherServletAutoConfiguration
ConditionalOnClass+EnableConfigurationProperties
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)@Configuration(proxyBeanMethods = false)@ConditionalOnWebApplication(type = Type.SERVLET)@ConditionalOnClass(DispatcherServlet.class)@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)public class DispatcherServletAutoConfiguration {/** The bean name for a DispatcherServlet that will be mapped to the root URL "/"*/public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";/** The bean name for a ServletRegistrationBean for the DispatcherServlet "/"*/public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";@Configuration(proxyBeanMethods = false)@Conditional(DefaultDispatcherServletCondition.class)@ConditionalOnClass(ServletRegistration.class)@EnableConfigurationProperties(WebMvcProperties.class)protected static class DispatcherServletConfiguration {@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {DispatcherServlet dispatcherServlet = new DispatcherServlet();dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());return dispatcherServlet;}@Bean@ConditionalOnBean(MultipartResolver.class)@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)public MultipartResolver multipartResolver(MultipartResolver resolver) {// Detect if the user has created a MultipartResolver but named it incorrectlyreturn resolver;}}}
