> 文章列表 > Spring @Conditional注解源码分析

Spring @Conditional注解源码分析

Spring @Conditional注解源码分析

概述

Import注解Java doc

指示要导入的一个或多个组件类,通常为@Configuration类。

提供与Spring XML中的<import/>元素等效的功能。允许导入@Configuration类、ImportSelector和ImportBeanDefinitionRegistrar实现,以及常规组件类(自4.2起;类似于AnnotationConfigApplicationContext.register(java.lang.Class<?> …))。

导入的@Configuration类中声明的@Bean定义应该使用@Autowired注入来访问。要么bean本身可以是自动装配的,要么声明bean的配置类实例可以是自动装配的。

可以在类级别和MetaAnnotation声明。

如果是XML或其他非@Configuration类需要导入@Bean定义资源,可以使用@ImportResource注解。

这个注解需要传一个Class<?>[]参数:@Configuration、ImportSelector、ImportBeanDefinitionRegistrar或者其他常规组件类。

ImportSelector接口

实现类需要根据给定的Select条件(通常是一个或多个Annotation Attribute)确定应导入哪些@Configuration类。

实现类可以实现以下Aware接口,在调用selectImports之前,将会调用它们各自的Aware逻辑:

  • EnvironmentAware
  • BeanFactoryAware
  • BeanClassLoaderAware
  • ResourceLoaderAware

或者,该类可以为单个构造函数提供一个或多个以下支持的参数类型:

  • Environment
  • BeanFactory
  • ClassLoader
  • ResourceLoader

ImportSelector实现类通常以与常规@Import注解相同的方式进行处理,但是,也可以推迟Selector导入,即直到处理完所有@Configuration类,DeferredImportSelector接口可以实现这个功能。

该接口提供一个方法

// Select and return the names of which class(es) should be imported based on 
// the AnnotationMetadata of the importing @Configuration class.
String[] selectImports(AnnotationMetadata importingClassMetadata)

DeferredImportSelector接口

继承ImportSelector接口。

ImportSelector的变体,在处理完所有@Configuration Bean之后运行。当导入为@Conditional时,这种类型的选择器可能特别有用。

实现类可以扩展Ordered接口或使用Order注释来指示相对于其他DeferredImportSelector的优先级。

getImportGroup方法需要返回一个DeferredImportSelector.Group的实现类Class对象,用于确定哪些类需要导入。

Group接口提供两个方法:

// Process the AnnotationMetadata of the importing @Configuration class 
// using the specified DeferredImportSelector.
process(AnnotationMetadata metadata, DeferredImportSelector selector)// Return the entries of which class(es) should be imported for this group.
selectImports()

ImportBeanDefinitionRegistrar接口

该接口的实现类会在处理@Configuration类时向容器注册额外的Bean定义,当需要在Bean Definition阶段进行一些操作时,这个接口的实现类就很有用。

与@Configuration和ImportSelector一起使用,可以用ImportSelector的返回值向@Import注解提供此接口的实现类。

// Register bean definitions as necessary based on the given annotation metadata 
// of the importing @Configuration class.
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry,BeanNameGenerator importBeanNameGenerator) {registerBeanDefinitions(importingClassMetadata, registry);
}// Register bean definitions as necessary based on the given annotation metadata 
// of the importing @Configuration class.
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
}

基本流程

ConfigurationClassPostProcessor类

在@Bean注解解析中,介绍了AnnotationConfigApplicationContext创建时会注册几个注解驱动处理器,其中就包括ConfigurationClassPostProcessor类。

他是BeanDefinitionRegistryPostProcessor实现,用于解析@Configuration类,他是按优先级排序的,因为在@Configuration类中声明的任何Bean方法都必须在任何其他BeanFactoryPostProcessor执行之前注册其对应的BeanDefinition。

他在invokeBeanFactoryPostProcessors(beanFactory)阶段被调用,通过postProcessBeanDefinitionRegistry(registry)方法解析@Configuration类。

这个过程的源码在@Bean注解解析中记录过,此处不再记录。

调用到processConfigBeanDefinitions(registry)方法,其中有一段使用ConfigurationClassParser解析@Configuration类的代码,如下:

// 1. Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment,this.resourceLoader, this.componentScanBeanNameGenerator, registry);Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());// 此处是一个do while循环
// 因为解析一遍之后,容器里面可能会有新的被注入的@Configuration Class定义,需要进一步解析
// 比如@Import注解就有可能注入新的@Configuration Class定义
do {parser.parse(candidates);parser.validate();Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// 2. Read the model and create bean definitions based on its contentthis.reader.loadBeanDefinitions(configClasses);// ...
} while (!candidates.isEmpty());// ...

以上代码做了两件事:

  • Parse @Configuration class
  • 解析ConfigurationClass集注册BeanDefinition

Parse @Configuration class

public void parse(Set<BeanDefinitionHolder> configCandidates) {for (BeanDefinitionHolder holder : configCandidates) {BeanDefinition bd = holder.getBeanDefinition();try {if (bd instanceof AnnotatedBeanDefinition) {parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());} else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());} else {parse(bd.getBeanClassName(), holder.getBeanName());}} catch (BeanDefinitionStoreException ex) {throw ex;} catch (Throwable ex) {throw new BeanDefinitionStoreException("", ex);}}// DeferredImportSelector处理this.deferredImportSelectorHandler.process();
}

三个分支最终都会调用到processConfigurationClass方法,该方法会递归的分析一个ConfigurationClass及其父类,核心源码在以下这几行:

// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass, filter);
do {// 分析ConfigurationClasssourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
} while (sourceClass != null);this.configurationClasses.put(configClass, configClass);

doProcessConfigurationClass方法:

  1. 递归解析内部类

  2. 解析@PropertySource注解

  3. 解析@ComponentScan注解

  4. 解析@Import注解

    // Process any @Import annotations
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
    
  5. 解析@ImportResource注解

  6. 解析@Bean注解

  7. 获取父类用于递归解析

processImports方法解析@Import注解

该方法需要以下参数:

  1. ConfigurationClass configClass - 当前分析的@Configuration类
  2. SourceClass currentSourceClass - 这个参数与configClass不一样,configClass始终不变,是当前@Configuration类,而这个参数会随着递归解析父类而改变
  3. Collection<SourceClass> importCandidates - @Import注解导入类
  4. Predicate<String> exclusionFilter - 会过滤掉java.lang.annotation和org.springframework.stereotype下面的类
  5. boolean checkForCircularImports - 检测循环Import

代码:

for (SourceClass candidate : importCandidates) {if (candidate.isAssignable(ImportSelector.class)) {// 这个分支支持ImportSelector// Candidate class is an ImportSelector -> delegate to it to determine importsClass<?> candidateClass = candidate.loadClass();ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,this.environment, this.resourceLoader, this.registry);Predicate<String> selectorFilter = selector.getExclusionFilter();if (selectorFilter != null) {exclusionFilter = exclusionFilter.or(selectorFilter);}if (selector instanceof DeferredImportSelector) {// 这个分支支持DeferredImportSelectorthis.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);} else {String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);}} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {// 这个分支支持ImportBeanDefinitionRegistrar// Candidate class is an ImportBeanDefinitionRegistrar ->// delegate to it to register additional bean definitionsClass<?> candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar =ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,this.environment, this.resourceLoader, this.registry);configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());} else {// 这个分支支持@Configuration class// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->// process it as an @Configuration classthis.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);}
}

ImportSelector支持

以下的代码片段是解析ImportSelector的逻辑:

// 调用selectImports方法获取导入的组件集
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 这里又递归调用了processImports方法
// 所以selectImports返回的类型可以是:普通@Configuration类、ImportSelector或者ImportBeanDefinitionRegistrar
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);

DeferredImportSelector支持

if (selector instanceof DeferredImportSelector) {this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();/*** Handle the specified DeferredImportSelector.* If deferred import selectors are being collected, this registers this instance to the list.* If they are being processed,* the DeferredImportSelector is also processed immediately according to its DeferredImportSelector.Group.*/
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);if (this.deferredImportSelectors == null) {DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();handler.register(holder);handler.processGroupImports();} else {// If deferred import selectors are being collected, this registers this instance to the list.// 等待解析了所有的@Configuration类之后再处理DeferredImportSelectorthis.deferredImportSelectors.add(holder);}
}

process核心逻辑:

public void processGroupImports() {for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {Predicate<String> exclusionFilter = grouping.getCandidateFilter();grouping.getImports().forEach(entry -> {ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());try {processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),exclusionFilter, false);} catch (BeanDefinitionStoreException ex) {throw ex;} catch (Throwable ex) {throw new BeanDefinitionStoreException("", ex);}});}
}// getImports方法
public Iterable<Group.Entry> getImports() {for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {this.group.process(deferredImport.getConfigurationClass().getMetadata(),deferredImport.getImportSelector());}return this.group.selectImports();
}

ImportBeanDefinitionRegistrar支持

// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
// 实例化ImportBeanDefinitionRegistrar对象
ImportBeanDefinitionRegistrar registrar =ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,this.environment, this.resourceLoader, this.registry);// 添加到ConfigurationClass的importBeanDefinitionRegistrars集合
// 等待后续loadBeanDefinitions时会调用registerBeanDefinitions方法让开发者注册BeanDefinition
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());

普通@Configuration类

// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());// 调用processConfigurationClass方法解析@Configuration class
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);

EL-ADMIN 在线文档