理解自动配置Bean(auto-configuration)

自动配置类是标准的@Configuration类,附加用来限制是否生效的条件注解(@Conditional)。如
@ConditionalOnClass @ConditionalOnMissingBean 注解. 这个可以确认在相关的类存在或未申明某Bean时才生效这个自动配置类。

自动配置类放置在类路径classpash下的META-INF/spring.factories文件里。SpringBoot自带的spring.factories在spring-boot-autoconfiguration模块jar里,默认的配置内容有(当前版本:2.30):

  1. # Initializers
  2. org.springframework.context.ApplicationContextInitializer=\
  3. org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
  4. org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
  5. # Application Listeners
  6. org.springframework.context.ApplicationListener=\
  7. org.springframework.boot.autoconfigure.BackgroundPreinitializer
  8. # Auto Configuration Import Listeners
  9. org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
  10. org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
  11. # Auto Configuration Import Filters
  12. org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
  13. org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
  14. org.springframework.boot.autoconfigure.condition.OnClassCondition,\
  15. org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
  16. # Auto Configure
  17. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  18. org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
  19. org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
  20. org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
  21. ....

查找自己的自动配置候选类

在自己的Maven工程里,新建一份spring.factories文件放到recourses/META-INF/目录下:

  1. app
  2. --src
  3. --main
  4. --resources
  5. -- META-INF
  6. --spring.factories
  7. --test

配置内容格式为,属性键值对,Key=org.springframework.boot.autoconfigure.EnableAutoConfiguration

  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=xxx.emoAutoConfiguration

自动配置只能用这种方式存储并加载,需要确保这个路径不会被自动扫描,同时不能配置类似@ComponentScan这样的注解,但是可以配置@Import来导入额外的配置类。

可以使用@AutoConfigureAfter,@AutoConfigureBefore注解来应用配置类在特定的顺序下。例如如果是Web应用,你一般需要再WebMvcAutoConfiguration生效只会才加载你自己的配置类。同时你还可以使用@AutoConfigureOrder来指定你的配置类的生效顺序。

使用条件注解(@Conditional)

Class Conditions

@ConditionalOnClass

通过查找Name是否存在,如果存在配置类会被注册为Bean,同时生效配置类中的@Bean.

  1. @Configuration(proxyBeanMethods = true)
  2. static class OnClassCondition {
  3. // Auto-configured beans
  4. @Bean
  5. public List<String> list() {
  6. return Lists.newArrayList("A", "B");
  7. }
  8. @Bean("userService")
  9. public UserService userService1() {
  10. return new UserService();
  11. }
  12. @Bean
  13. @ConditionalOnMissingBean(name = "userService2")
  14. public UserService userService2() {
  15. return new UserService();
  16. }
  17. @Configuration(proxyBeanMethods = false)
  18. @ConditionalOnClass(name = "org.test.EmbeddedAcmeService")
  19. static class EmbeddedConfiguration {
  20. @Bean
  21. public String hello() {
  22. return "Hello";
  23. }
  24. @Bean
  25. @ConditionalOnMissingBean
  26. public UserService userService() {
  27. return new UserService();
  28. }
  29. }
  30. }
  31. @Test
  32. public void testOnClassCondition() {
  33. ApplicationContextRunner contextRunner = new ApplicationContextRunner()
  34. .withConfiguration(AutoConfigurations.of(OnClassCondition.class));
  35. contextRunner.withUserConfiguration(OnClassCondition.class).run((context) -> {
  36. System.out.println(Arrays.toString(context.getBeanDefinitionNames()));
  37. assertThat(context).getBean(OnClassCondition.class).isNotNull();
  38. List<String> list = (List<String>) context.getBean("list");
  39. assertThat(list).hasSize(2);
  40. assertThat(context).getBean(UserService.class).isNull();
  41. assertThat(context).getBean("hello").isNull();
  42. });
  43. }

@ConditionalOnMissingClass

如果Name指定的类不存在,则配置类生效,会被注册为Bean,同时生效配置类里的@Bean

  1. @Configuration(proxyBeanMethods = false)
  2. static class OnClassMissingCondition {
  3. // Auto-configured beans
  4. @Configuration(proxyBeanMethods = false)
  5. @ConditionalOnMissingClass("org.test.EmbeddedAcmeService")
  6. static class EmbeddedConfiguration {
  7. @Bean
  8. public String hello() {
  9. return "world";
  10. }
  11. @Bean
  12. @ConditionalOnMissingBean
  13. public UserService userService() {
  14. return new UserService();
  15. }
  16. }
  17. }
  18. @Test
  19. public void testOnClassMissingCondition() {
  20. ApplicationContextRunner contextRunner = new ApplicationContextRunner()
  21. .withConfiguration(AutoConfigurations.of(OnClassMissingCondition.class));
  22. contextRunner.withUserConfiguration(OnClassMissingCondition.class).run((context) -> {
  23. System.out.println(Arrays.toString(context.getBeanDefinitionNames()));
  24. assertThat(context).getBean(OnClassMissingCondition.class).isNotNull();
  25. assertThat(context).getBean(OnClassMissingCondition.EmbeddedConfiguration.class).isNotNull();
  26. assertThat(context).getBean(UserService.class).isNotNull();
  27. assertThat(context).getBean("hello").hasToString("world");
  28. });
  29. }

Bean Conditions

@ConditionalOnMissingBean

Bean不存在时则生效配置类

@ConditionalOnBean

Bean存在时则生效配置类

Property Conditions

@ConditionalOnProperty

属性文件里配置了某属性=true,才生效。matchIfMission=true,表示如没有这个属性,默认生效。
如:spring.example.enble=true

  1. @Configuration(proxyBeanMethods = false)
  2. @ConditionalOnProperty(prefix = "spring.example", name = "enable", matchIfMissing = true)
  3. static class OnPropertyCondition {
  4. @Bean
  5. public UserService userService() {
  6. return new UserService();
  7. }
  8. }
  9. @Test
  10. public void testOnPropertyCondition() {
  11. ApplicationContextRunner contextRunner = new ApplicationContextRunner()
  12. .withConfiguration(AutoConfigurations.of(OnPropertyCondition.class));
  13. contextRunner
  14. .withUserConfiguration(OnPropertyCondition.class)
  15. .withPropertyValues("spring.example.enable=false")
  16. .run((context) -> {
  17. System.out.println(Arrays.toString(context.getBeanDefinitionNames()));
  18. assertThat(context).getBean(OnPropertyCondition.class).isNull();
  19. assertThat(context).getBean(UserService.class).isNull();
  20. });
  21. }

SpringBoot内置AutoConfiguration示例

AopAutoConfiguration

ConditionalOnProperty+ConditionalOnClass

  1. /**
  2. * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
  3. * Auto-configuration} for Spring's AOP support. Equivalent to enabling
  4. * {@link EnableAspectJAutoProxy @EnableAspectJAutoProxy} in your configuration.
  5. * <p>
  6. * The configuration will not be activated if {@literal spring.aop.auto=false}. The
  7. * {@literal proxyTargetClass} attribute will be {@literal true}, by default, but can be
  8. * overridden by specifying {@literal spring.aop.proxy-target-class=false}.
  9. *
  10. * @author Dave Syer
  11. * @author Josh Long
  12. * @since 1.0.0
  13. * @see EnableAspectJAutoProxy
  14. */
  15. @Configuration(proxyBeanMethods = false)
  16. @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
  17. public class AopAutoConfiguration {
  18. @Configuration(proxyBeanMethods = false)
  19. @ConditionalOnClass(Advice.class)
  20. static class AspectJAutoProxyingConfiguration {
  21. @Configuration(proxyBeanMethods = false)
  22. @EnableAspectJAutoProxy(proxyTargetClass = false)
  23. @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
  24. matchIfMissing = false)
  25. static class JdkDynamicAutoProxyConfiguration {
  26. }
  27. @Configuration(proxyBeanMethods = false)
  28. @EnableAspectJAutoProxy(proxyTargetClass = true)
  29. @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
  30. matchIfMissing = true)
  31. static class CglibAutoProxyConfiguration {
  32. }
  33. }
  34. @Configuration(proxyBeanMethods = false)
  35. @ConditionalOnMissingClass("org.aspectj.weaver.Advice")
  36. @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
  37. matchIfMissing = true)
  38. static class ClassProxyingConfiguration {
  39. ClassProxyingConfiguration(BeanFactory beanFactory) {
  40. if (beanFactory instanceof BeanDefinitionRegistry) {
  41. BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
  42. AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
  43. AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
  44. }
  45. }
  46. }
  47. }

PropertyPlaceholderAutoConfiguration

AutoConfigureOrder+ConditionalOnMissingBean

  1. @Configuration(proxyBeanMethods = false)
  2. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
  3. public class PropertyPlaceholderAutoConfiguration {
  4. @Bean
  5. @ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
  6. public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
  7. return new PropertySourcesPlaceholderConfigurer();
  8. }
  9. }

DispatcherServletAutoConfiguration

ConditionalOnClass+EnableConfigurationProperties

  1. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
  2. @Configuration(proxyBeanMethods = false)
  3. @ConditionalOnWebApplication(type = Type.SERVLET)
  4. @ConditionalOnClass(DispatcherServlet.class)
  5. @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
  6. public class DispatcherServletAutoConfiguration {
  7. /*
  8. * The bean name for a DispatcherServlet that will be mapped to the root URL "/"
  9. */
  10. public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
  11. /*
  12. * The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
  13. */
  14. public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
  15. @Configuration(proxyBeanMethods = false)
  16. @Conditional(DefaultDispatcherServletCondition.class)
  17. @ConditionalOnClass(ServletRegistration.class)
  18. @EnableConfigurationProperties(WebMvcProperties.class)
  19. protected static class DispatcherServletConfiguration {
  20. @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
  21. public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
  22. DispatcherServlet dispatcherServlet = new DispatcherServlet();
  23. dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
  24. dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
  25. dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
  26. dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
  27. dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
  28. return dispatcherServlet;
  29. }
  30. @Bean
  31. @ConditionalOnBean(MultipartResolver.class)
  32. @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
  33. public MultipartResolver multipartResolver(MultipartResolver resolver) {
  34. // Detect if the user has created a MultipartResolver but named it incorrectly
  35. return resolver;
  36. }
  37. }
  38. }