@Import 和 @Conditional 在 Spring Boot 实现 auto-configuratin 过程中有着至关重要的作用。今天就来梳理一下这两个基础注解。
@Import 注解用来导入更多的类,这个注解等价于XML配置中的 <import/>。典型的就是导入一个 @Configuration 标记的配置类。
注解参数可以是:
-
@Configuration注解标记的类,@Configuration注解本质上是个@Component,源码的处理上也是判断是否注解了@Component,因此标记了@Componnet也是可以的。 -
ImportSelector接口的子类,此接口重点实现selectImports方法 返回需要倒入类的名称。 -
ImportBeanDefinitionRegistrar接口的子类,此接口重点实现registerBeanDefinitions方法 通过方法参数BeanDefinitionRegistry registry直接注册bean定义。
以ImportSelector为例写个测试Demo,源码分析一下:
public static void main(String[] args) {
//这里需要 Spring 托管 @Import 注解的类
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(WithImportAnnotationClass.class);
System.out.println(applicationContext.getBean(BeanA.class));
}
@Import({CustomBeanImportSelector.class})
static class WithImportAnnotationClass {
}
static class CustomBeanImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{BeanA.class.getName()};
}
}
static class BeanA {
}
简单几行代码,启动调试,直接公布调用过程答案:
- Spring Ioc 加载
@Import注解的类定义,Spring Boot也是类似逻辑,通过 -
ApplicationContext在创建过程中,通过refresh()方法 完成 Ioc 容器的初始化工作。这个方法基本包含了 Ioc 的生命周期和重要扩张点。 - 在
refresh方法中,invokeBeanFactoryPostProcessors()方法实现中会先后调用BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)和BeanFactoryPostProcesser#postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)两个扩展点方法。本质上BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcesser的子类,但他们的侧重点不同,前者侧重BeanDefinition的注册,后者侧重对BeanFactory处理。 - 针对
AnnotationConfigApplicationContext它在启动时默认就会注册了一个BeanDefinitionRegistryPostProcessor,具体对应实现类ConfigurationClassPostProcessor - 通过
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry方法,1. 明确需要额外处理的BeanDefinition,2. 解释这些BeanDefinition产生额外配置的BeanDefinition。3. 将这些额外配置的BeanDefinition注册到BeanDefinitionRegistry中,后期产生实例。
第五步中,有三点强调:
- 明确需要额外处理的
BeanDefinition,是通过ConfigurationClassUtils#isConfigurationCandidate方法,判断是否被[@Component, @ComponentScan, @Import, @ImportResource]标记,然后给BeanDefinition增加attribution来实现的(后续会判断这点)。 - 解释这些
BeanDefinition产生额外配置的BeanDefinition,是通过ConfigurationClassParser#parser方法,它根据不通注解和注解配置,来实现产生需要被倒入的BeanDefinition。当然这个过程涉及到递归,比如说我 Import了一个新的 Import注解的类。 -
ConfigurationClassParser#parser会对@Conditional注解进行处理。它在处理开始时,会通过ConditionEvaluator来判断配置类上是否有Conditional注解,并根据注解的参数,来判断是否跳过这个配置类。

852F3B5B-ED0A-445B-8876-863718B75F0E.png
以上讲完了@Import注解,有了上面的基础,接下来直接搞 @Conditional。
- 首先,使用
@Conditional注解需要指明一个实现了Condition接口的类,如@Conditional(OnBeanCondition.class)这样子。 - ApplicationContext 在初始化时,会创建一个
ConditionEvaluator,它负责对注解的解释工作。 - 在关键点(比如说上面讲的配置类解析过程),
ConditionEvaluator#shouldSkip返回的Boolean值来跳过某些Bean定义。
第三步中,具体流程其实就是:
获取到Bean定义中的@Conditional注解,得到配置的Conditon的实现类,执行实现类的matches方法。
像Spring Boot中有一些自带的 Condition 的实现类如:
OnBeanCondition => @ConditionalOnBean
OnClassCondition => @ConditionalOnClass