Spring Boot 最核心的功能就是自动配置,那 Spring Boot 是如何约定,又是如何实现自动配置功能的呢?实际上,@EnableAutoConfiguration 是开启自动配置的注解,但在创建的 Spring Boot 项目中并不能直接看到此注解,它是由组合注解 @SpringBootApplication 引入的。下面我们先来了解一下 @SpringBootApplication 注解的功能,然后再深入了解 @EnableAutoConfiguration 注解的构成与作用。

@SpringBootApplication 注解

@SpringBootApplication 是 Spring Boot 提供的一个复合注解,其中包含了三个重要的注解:

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Inherited
  4. @SpringBootConfiguration
  5. @EnableAutoConfiguration
  6. @ComponentScan(excludeFilters = {
  7. @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
  8. @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
  9. })
  10. public @interface SpringBootApplication {
  11. // 排除指定自动配置类,该属性覆盖了@EnableAutoConfiguration注解中定义的exclude成员属性
  12. @AliasFor(annotation = EnableAutoConfiguration.class)
  13. Class<?>[] exclude() default {};
  14. // 排除指定自动配置类名,该属性覆盖了@EnableAutoConfiguration注解中定义的excludeName成员属性
  15. @AliasFor(annotation = EnableAutoConfiguration.class)
  16. String[] excludeName() default {};
  17. // 指定扫描的基础包,用于激活@Component等注解类的初始化
  18. @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
  19. String[] scanBasePackages() default {};
  20. // 指定扫描的类
  21. @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
  22. Class<?>[] scanBasePackageClasses() default {};
  23. // 指定是否代理@Bean方法以强制执行bean的生命周期行为
  24. @AliasFor(annotation = Configuration.class)
  25. boolean proxyBeanMethods() default true;
  26. }

可以看到,Spring Boot 中大量使用了 @AliasFor 注解,该注解用于桥接到其他注解,该注解的属性中指定了所桥接的注解类。实际上 @SpringBootApplication 注解中定义的属性在其他注解中已经定义过了。之所以使用 @AliasFor 注解并重新在 @SpringBootApplication 中定义,是为了减少用户使用多注解带来的麻烦。

@SpringBootApplication 注解中组合了 @SpringBootConfiguration、@EnableAutoConfiguration 和
@ComponentScan。我们忽略掉一些基础注解和元注解,@SpringBootApplication 注解的组合结构可以参考下图所示:
image.png
从图中可以看到,@SpringBootApplication 除了组合元注解之外,其核心作用还包括:激活 Spring Boot 自动配置的 @EnableAutoContiguration、激活 @Component 扫描的 @ComponentScan 以及激活配置类的 @Configuration。

1. @SpringBootConfiguration

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Configuration
  5. public @interface SpringBootConfiguration {}

@SpringBootConfiguration 继承自 @Configuration,二者功能也一致,标注当前类是配置类,并会将当前类内声明的一个或多个以 @Bean 注解标记的方法的实例注入到 Spring 容器中,并且实例名就是方法名。

2. @ComponentScan

Spring 里有四大注解:@Service、@Repository、@Component、@Controller 用来定义一个 Bean。@ComponentScan 注解就是用来自动扫描被这些注解标识的类,最终生成 IOC 容器里的 Bean。可以通过设置 basePackages、includeFilters、excludeFilters 属性来动态确定自动扫描的范围,类型以及不扫描的类型。默认情况下:它扫描所有类型,并且扫描范围是 @ComponentScan 注解所在配置类包及其子包的类。

3. @EnableAutoConfiguration

在未使用 Spring Boot 的情况下,Bean 的生命周期由 Spring 来管理,然而 Spring 无法自动配置 @Configuration 注解的类。而 Spring Boot 的核心功能之一就是根据约定自动管理该注解标注的类。用来实现该功能的组件之一便是 @EnableAutoConfiguration 注解。

@EnableAutoConfiguration 的主要功能是启动 Spring 应用程序上下文时进行自动配置,它会尝试猜测并配置项目可能需要的 Bean。自动配置通常是基于项目 classpath 中引入的类和已定义的 Bean 来实现的。在此过程中,被自动配置的组件来自项目自身和项目依赖的 jar 包中。下面看下 @EnableAutoConfiguration 注解源码:

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @AutoConfigurationPackage
  6. @Import(AutoConfigurationImportSelector.class)
  7. public @interface EnableAutoConfiguration {
  8. // 用来开启、关闭自动配置功能的配置项
  9. String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
  10. // 排除特定的自动配置类
  11. Class<?>[] exclude() default {};
  12. // 排除特定的自动配置类名
  13. String[] excludeName() default {};
  14. }

@EnableAutoConfiguration 注解是 Spring Boot 实现自动配置的核心,它可以帮助 Spring Boot 将所有符合条件的 Bean 定义自动加载到 IOC 容器中。它会从 classpath 中搜索所有的 META-INF/spring.factories 配置文件,然后将其中 key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的 value 通过反射实例化为对应标注了 @Configuration 的形式的配置类,然后注入到 IOC 容器中。

@EnableAutoConfiguration 原理

@EnableAutoConfiguration 的关键功能是通过 @lmport 注解导入的 ImportSelector 来完成的。从源代码中得知 @lmport(AutoConfigurationlmportSelector.class) 是 @EnableAutoConfiguration 注解的组成部分,也是自动配置功能的核心实现。下面讲解下 @Import 的基本使用和 AutoConfigurationImportSelector。

1. @Import

@lmport 注解位于 spring-context 项目内,主要提供导入配置类的功能。

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Import {
  5. /**
  6. * {@link Configuration @Configuration}, {@link ImportSelector},
  7. * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
  8. */
  9. Class<?>[] value();
  10. }

@lmport 的作用和 xml 配置中 标签的作用一样,我们可以通过 @Import 引入 @Configuration 注解的类,也可以导入实现了 ImportSelector 或 ImportBeanDefinitionRegistrar 的类,还可以通过 @Import 导入普通的 POJO 以将其注册成 Spring Bean。

2. AutoConfigurationImportSelector

AutoConfigurationImportSelector 实现了 ImportSelector 接口,ImportSelector 接口用来决定可引入哪些 @Configuration 配置类。ImportSelector 接口源码如下:

  1. public interface ImportSelector {
  2. /**
  3. * Select and return the names of which class(es) should be imported based on
  4. * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
  5. */
  6. String[] selectImports(AnnotationMetadata importingClassMetadata);
  7. }

ImportSelector 接口只提供了一个参数为 AnnotationMetadata 的方法,返回的结果为一个字符串数组。其中参数 AnnotationMetadata 内包含了被 @Import 注解的类的注解信息。在 selectImports 方法内可根据具体实现决定返回哪些配置类的全限定名,将结果以字符串数组的形式返回。

如果实现了接口 ImportSelector 的类的同时又实现了以下四个 Aware 接口,那么 Spring 保证在调用 ImportSelector 之前会先调用 Aware 接口的方法。这四个接口为:EnvironmentAware、BeanFactoryAware、BeanClassLoaderAware 和 ResourceLoaderAware。在 AutoConfigurationlmportSelector 的源码中就实现了这四个接口:

  1. public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
  2. ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {}

可以看到,AutoConfigurationImportSelector 并没有直接实现 ImportSelector 接口,而是实现了它的子接口 DeferredImportSelector。DeferredImportSelector 接口与 ImportSelector 接口的区别是,前者会在所有的 @Configuration 类加载完成后再加载返回的配置类,而 ImportSelector 是在加载完 @Configuration 类之前先去加载返回的配置类。

下面我们先从整体上了解 AutoConfigurationImportSelector 的核心功能及流程,然后再对照代码看具体的功能实现。具体流程如下图所示:
image.png
当 AutoConfigurationImportSelector 被 @lmport 注解引入之后,它的 selectImports 方法会被调用并执行其实现的自动装配逻辑。AutoConfigurationImportSelector 的 selectImports 方法源码如下:

  1. @Override
  2. // 此处的annotationMetadata为@EnableAutoConfiguration注解的元信息
  3. public String[] selectImports(AnnotationMetadata annotationMetadata) {
  4. // 检查自动配置功能是否开启,默认开启
  5. if (!isEnabled(annotationMetadata)) {
  6. return NO_IMPORTS;
  7. }
  8. // 加载自动配置的元信息,配置文件为ClassPath下的META-INF/spring-autoconfigure-metadata.properties文件
  9. AutoConfigurationMetadata autoConfigurationMetadata =
  10. AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
  11. // 获取所有的自动配置类列表
  12. AutoConfigurationEntry autoConfigurationEntry =
  13. getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
  14. // 返回符合条件的配置类的全限定名数组
  15. return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  16. }

2.1 检查自动配置开关

检查自动配置是否开启的代码位于 isEnabled() 方法中,如果开启自动配置功能,就继续执行后续操作;如果未开启,就返回空数组。其实现如下:

  1. protected boolean isEnabled(AnnotationMetadata metadata) {
  2. if (getClass() == AutoConfigurationImportSelector.class) {
  3. return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
  4. }
  5. return true;
  6. }

可以看到,如果当前类为 AutoConfigurationImportSelector,程序会从上下文中获取指定属性的配置,该属性的值为 spring.boot.enableautoconfiguration。如果获取不到该属性值,则默认返回 true。如果当前类为其他类,直接返回 true。如果想关闭自动配置,可以在 application.propertics 配置文件中针对此参数进行配置:

  1. spring.boot.enableautoconfiguration=false

2.2 加载元数据配置

加载元数据配置主要是为后续操作提供数据支持,这一过程用到了 AutoConfigurationMetadataLoader 类提供的 loadMetadata 方法,该方法默认加载类路径下 META-INF/spring-autoconfigure-metadata.properties 文件内的配置:

  1. final class AutoConfigurationMetadataLoader {
  2. // 默认加载元数据的路径
  3. protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
  4. // 默认调用该方法,传入默认PATH
  5. static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
  6. return loadMetadata(classLoader, PATH);
  7. }
  8. static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
  9. try {
  10. // 读取PATH路径下的资源
  11. Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
  12. : ClassLoader.getSystemResources(path);
  13. Properties properties = new Properties();
  14. while (urls.hasMoreElements()) {
  15. // 读取元数据配置文件中的内容存储于Properties中
  16. properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
  17. }
  18. return loadMetadata(properties);
  19. }
  20. catch (IOException ex) {
  21. throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
  22. }
  23. }
  24. }

spring-autoconfigure-metadata.properties 文件内的配置格式如下:

  1. 自动配置类的全限定名.注解名称=值

为什么要加载此元数据呢?加载元数据主要是为了后续过滤自动配置使用。Spring Boot 使用一个 Annotation 的处理器来收集自动加载的条件,这些条件可以在元数据文件中进行配置。Spring Boot 会将收集好的 @Configuration 进行一次过滤,进而剔除不满足条件的配置类。通过使用这种方式来过滤自动配置类,可以有效缩短 Spring Boot 的启动时间,减少 @Configuration 类的数最,从而减少初始化 Bean 的耗时。

2.3 加载自动配置组件

加载自动配置组件是自动配置的核心方法,这些自动配置组件在类路径下的 META-INF/spring.factories 文件中进行注册。通过 getAutoConfigurationEntry 方法来获取所有的自动配置类列表,源码如下:

  1. protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
  2. AnnotationMetadata annotationMetadata) {
  3. if (!isEnabled(annotationMetadata)) {
  4. return EMPTY_ENTRY;
  5. }
  6. // 将注解元信息封装成注解属性对象
  7. AnnotationAttributes attributes = getAttributes(annotationMetadata);
  8. // 加载ClassPath下META-INF/spring.factories文件,获取自动配置类列表
  9. List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  10. // 去掉重复项,防止多个项目引入同样的配置类
  11. configurations = removeDuplicates(configurations);
  12. // 获取@EnableAutoConfiguration注解里设置的exclude,excludeName属性所排除的类的集合
  13. Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  14. // 检查被排除类是否可实例化,是否被自动注册配置所使用,不符合条件则抛出异常
  15. checkExcludedClasses(configurations, exclusions);
  16. // 去除被排除的配置类
  17. configurations.removeAll(exclusions);
  18. // 根据配置类标注的@Conditional注解,过滤掉不满足条件的自动配置类
  19. configurations = filter(configurations, autoConfigurationMetadata);
  20. // 将筛选完成的配置类和排除掉的配置类构建成一个事件,并传入监听器
  21. fireAutoConfigurationImportEvents(configurations, exclusions);
  22. return new AutoConfigurationEntry(configurations, exclusions);
  23. }

其中 getCandidateConfigurations 方法用来获取所有的自动配置类。该方法通过 SpringFactoriesLoader 类提供的 loadFactoryNames 方法来读取 META-INF/spring.factories 中的配置,而 loadFactoryNames 方法的第一个参数为 getSpringFactoriesLoaderFactoryClass 方法返回的 EnableAutoConfiguration.class,也就是说 loadFactoryNames 只会读取配置文件中针对自动配置的注册类。

  1. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  2. List<String> configurations = SpringFactoriesLoader.
  3. loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
  4. ......
  5. return configurations;
  6. }
  7. protected Class<?> getSpringFactoriesLoaderFactoryClass() {
  8. return EnableAutoConfiguration.class;
  9. }

分析 loadFactoryNames 方法,该方法通过读取 ClassPath 路径下的 META-INF/spring.factories 文件来获取所有自动配置类。而 spring.factories 配置文件的内容其实就是一个个键值对结构。当读取完成后,获取其中 key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值(代表这些类是需要进行自动配置的类)。

  1. public final class SpringFactoriesLoader {
  2. public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
  3. public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
  4. // 此处的factoryType为上一步中的入参:EnableAutoConfiguration.class
  5. String factoryTypeName = factoryType.getName();
  6. // 加载指定位置的配置文件,从中获取指定key值对应的value值
  7. return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
  8. }
  9. }

当然,spring.factories 文件内还有其他的配置,比如用于监听的 Listeners 和用于过滤的 Filters 等。很显然,在加载自动配置组件时,此方法只用到了 EnableAutoConfiguration 对应的配置。因为程序默认加载的是 ClassLoader 下面的所有 META-INF/spring.factories 文件中的配置,所以难免在不同的 jar 包中出现重复的配置,因此 Spring Boot 使用了 Set 集合数据不可重复的特性进行去重操作:

  1. protected final <T> List<T> removeDuplicates(List<T> list) {
  2. return new ArrayList<>(new LinkedHashSet<>(list));
  3. }

2.4 排除指定组件

如果在实际应用中不需要自动配置中的某个或某些组件,可通过 @EnableAutoConfiguration 注解中的 exclude 或 excludeName 属性进行有针对性的排除,当然也可以通过配置文件进行排除。

  1. private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
  2. protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  3. Set<String> excluded = new LinkedHashSet<>();
  4. excluded.addAll(asList(attributes, "exclude"));
  5. excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
  6. excluded.addAll(getExcludeAutoConfigurationsProperty());
  7. return excluded;
  8. }
  9. private List<String> getExcludeAutoConfigurationsProperty() {
  10. if (getEnvironment() instanceof ConfigurableEnvironment) {
  11. Binder binder = Binder.get(getEnvironment());
  12. return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList)
  13. .orElse(Collections.emptyList());
  14. }
  15. String[] excludes = getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
  16. return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
  17. }

在 getExclusions 方法中会收集 @EnableAutoConfiguration 注解中配置的 exclude 属性值、excludeName 属性值,并获取在配置文件中 key 为 spring.autoconfigure.exclude 的配置值。获取到被排除组件的集合之后,首先是对待排除类进行检查操作,代码如下:

  1. private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
  2. List<String> invalidExcludes = new ArrayList<>(exclusions.size());
  3. // 遍历并判断是否存在对应的配置类
  4. for (String exclusion : exclusions) {
  5. if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
  6. invalidExcludes.add(exclusion);
  7. }
  8. }
  9. // 如果不为空,则进行异常处理
  10. if (!invalidExcludes.isEmpty()) {
  11. handleInvalidExcludes(invalidExcludes);
  12. }
  13. }

以上代码中,checkExcludedClasses 方法用来确保被排除的类存在于当前的 ClassLoader 中,并且包含在 spring.factories 注册的集合中。如果不满足以上条件,调用 handleInvalidExcludes 方法抛出异常。如果被排除类都符合条件,调用 configurations.removeAll(exclusions) 方法从自动配置集合中移除被排除集合的类,至此完成初步的自动配置组件排除。

2.5 过滤自动配置组件

当完成初步的自动配置组件排除工作之后,AutoConfigurationImportSelector 会结合在此之前获取的 AutoConfigurationMetadata 对象,对组件进行再次过滤。

  1. private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
  2. String[] candidates = StringUtils.toStringArray(configurations);
  3. boolean[] skip = new boolean[candidates.length];
  4. boolean skipped = false;
  5. for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
  6. invokeAwareMethods(filter);
  7. boolean[] match = filter.match(candidates, autoConfigurationMetadata);
  8. for (int i = 0; i < match.length; i++) {
  9. if (!match[i]) {
  10. skip[i] = true;
  11. candidates[i] = null;
  12. skipped = true;
  13. }
  14. }
  15. }
  16. ......
  17. return new ArrayList<>(result);
  18. }
  • configurations:经过初次过滤后的自动配置组件列表。
  • autoConfigurationMetadata:元数据文件 META-INF/spring-autoconfigure-metadata.properties 中配置的对应实体类。
  • getAutoConfigurationImportFilters 方法是通过 SpringFactoriesLoader 的 loadFactories 方法将 META-INF/spring factories 中配置的 key 为 AutoConfigurationImportFilter 的值进行加载。

下面为 META-INF/spring.factories 中相关配置的具体内容:

  1. # Auto Configuration Import Filters
  2. org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
  3. org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
  4. org.springframework.boot.autoconfigure.condition.OnClassCondition,\
  5. org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

在 spring-boot-autoconfigure 模块中默认配置了三个筛选条件:OnBeanCondition、OnClassCondition 和 OnWebApplicationCondition,它们均实现了 AutoConfigurationImportFilter 接口,且每个实现类都支持一些 @ConditionalXXX 注解用以过滤自动配置类。
image.png
下面我们来分析下 match 方法:

  1. @Override
  2. public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
  3. ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
  4. // 过滤核心功能,由子类实现
  5. ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
  6. boolean[] match = new boolean[outcomes.length];
  7. for (int i = 0; i < outcomes.length; i++) {
  8. match[i] = (outcomes[i] == null || outcomes[i].isMatch());
  9. if (!match[i] && outcomes[i] != null) {
  10. logOutcome(autoConfigurationClasses[i], outcomes[i]);
  11. if (report != null) {
  12. report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
  13. }
  14. }
  15. }
  16. return match;
  17. }

match 方法接收两个参数,一个是待过滤的自动配置类数组,另一个是自动配置的元数据信息。match 返回的结果为匹配过滤后的结果布尔数组,数组的大小与 autoConfigurationClasses 一致,如果需排除,则设置对应数组下标的值为 false。

AutoConfigurationImportFilter 接口的 match 方法主要在其抽象子类 FilteringSpringBootCondition 中实现,在其实现 match 方法的同时又定义了新的抽象方法 getOutcomes,继承该抽象类的其他三个子类均实现了 getOutcomes 方法。下面以实现类 OnClassCondition 来具体说明执行过程:

  1. @Order(Ordered.HIGHEST_PRECEDENCE)
  2. class OnClassCondition extends FilteringSpringBootCondition {
  3. @Override
  4. protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
  5. ......
  6. OutcomesResolver outcomesResolver = new StandardOutcomesResolver(autoConfigurationClasses, 0, autoConfigurationClasses.length, autoConfigurationMetadata, getBeanClassLoader());
  7. return outcomesResolver.resolveOutcomes();
  8. }
  9. }

在 resolveOutcomes 方法中,会根据元数据配置(AutoConfigurationMetadata)获取自动配置类关联的过滤条件配置类,之后再通过 ClassNameFilter 来判断对应的类是否存在。

2.6 事件通知

在完成以上步骤的过滤、筛选之后,我们最终得到了要进行自动配置的类的集合,在将该集合返回之前,还需要完成的最后一步操作就是相关事件的封装和广播,代码如下:

  1. private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
  2. List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
  3. if (!listeners.isEmpty()) {
  4. AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
  5. for (AutoConfigurationImportListener listener : listeners) {
  6. invokeAwareMethods(listener);
  7. listener.onAutoConfigurationImportEvent(event);
  8. }
  9. }
  10. }
  11. protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
  12. return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
  13. }

以上代码首先通过 SpringFactoriesLoader 类提供的 loadFactories 方法将 spring.factories 文件中配置的接口 AutoConfigurationImportListener 的实现类加载出来。然后,将筛选出的自动配置类集合和被排除的自动配置类集合封装成 AutoConfigurationImportEvent 事件对象,然后触发监听器的回调方法进行事件广播。

3. @Conditional

@Conditional 注解是由 Spring 4.0 版本引入的新特性,可根据是否满足指定的条件来决定是否进行 Bean 的实例化及装配。比如,可以设定当类路径下包含某个 jar 包时才会对注解的类进行实例化操作。总之,就是根据一些特定条件来控制 Bean 实例化的行为,@Conditional 注解代码如下:

  1. @Target({ElementType.TYPE, ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Conditional {
  5. Class<? extends Condition>[] value();
  6. }

@Conditional 注解唯一的元素属性是 Condition 接口数组,只有在数组中指定的所有 Condition 的 matches 方法都返回 true 的情况下,被注解的类才会被加载。Condition 接口定义如下:

  1. public interface Condition {
  2. // 决定条件是否匹配
  3. boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
  4. }

matches 方法的第一个参数为 ConditionContext,可通过该接口提供的方法来获得 Spring 应用的上下文信息;第二个参数为 AnnotatedTypeMetadata,该接口提供了访问指定自动配置类及其方法的注解功能,并且不需要加载类,可以用来检查带有 @Bean 注解的方法上是否还有其他注解。

在 Spring Boot 的 autoconfigure 项目中提供了各类基于 @Conditional 注解的衍生注解,它们适用不同的场景并提供了不同的功能,一些常用的衍生注解如下所示:

  • @ConditionalOnBean:容器中有指定 Bean
  • @ConditionalOnMissingBean:容器中没有指定 Bean
  • @ConditionalOnClass:在 ClassPath 类路径下有指定类
  • @ConditionalOnMissingClass:在 ClassPath 类路径下没有指定类
  • @ConditionalOnProperty:指定的属性有指定值
  • @ConditionalOnResource:类路径有指定的值
  • @ConditionalOnWebApplication:项目是一个 Web 项目
  • @ConditionalOnNotWebApplication:项目不是一个 Web 项目
  • @ConditionalOnExpression:基于 SpEL 表达式的条件判断
  • @ConditionalOnJava:基于 JVM 版本作为判断条件
  • @ConditionalOnJndi:在 JNDI 存在的条件下查找指定的位置
  • @ConditionalOnSingleCandidate:当指定 Bean 在容器中只有一个或有多个但指定了首选 Bean 时

这些衍生注解其实都组合了 @Conditional 注解,不同之处是它们在注解中指定的条件(Condition)不同。