SpringBoot中的条件注解底层是如何实现的?
SpringBoot内部提供了特有的注解:条件注解(Conditional Annotation)。比如:
@ConditionalOnBean、
@ConditionalOnClass、
@ConditionalOnExpression、
@ConditionalOnMissingBean等。
条件注解存在的意义在于动态识别(也可以说是代码自动化执行)。比如@ConditionalOnClass会检查类加载器中是否存在对应的类,如果有的话被注解修饰的类就有资格被Spring容器所注册,否则会被skip。
比如FreemarkerAutoConfiguration这个自动化配置类的定义如下:
这个自动化配置类被@ConditionalOnClass条件注解修饰,这个条件注解存在的意义在于判断类加载器中是否存在freemarker.template.Configuration和FreeMarkerConfigurationFactory这两个类,如果都存在的话会在Spring容器中加载这个FreeMarkerAutoConfiguration配置类;否则不会加载。
条件注解内部的一些基础
在分析条件注解的底层实现之前,我们先来看一下这些条件注解的定义。以@ConditionalOnClass注解为例,它的定义如下:
它有2个属性,分别是类数组和字符串数组(作用一样,类型不一样),而且被@Conditional注解所修饰,这个@Conditional注解有个名为values的Class<? extends Condition>[]类型的属性。这个Condition是个接口,用于匹配组件是否有资格被容器注册,定义如下:
也就是说@Conditional注解属性中可以持有多个Condition接口的实现类,所有的Condition接口需要全部匹配成功后这个@Conditional修饰的组件才有资格被注册。
Condition接口有个子接口ConfigurationCondition:
这个子接口是一种特殊的条件接口,多了一个getConfigurationPhase方法,也就是条件注解的生效阶段。只有在ConfigurationPhase中定义的两种阶段下才会生效。
Condition接口有个实现抽象类SpringBootCondition,SpringBoot中所有条件注解对应的条件类都继承这个抽象类。它实现了matches方法:
基于Class的条件注解
SpringBoot提供了两个基于Class的条件注解:@ConditionalOnClass(类加载器中存在指明的类)或者@ConditionalOnMissingClass(类加载器中不存在指明的类)。
@ConditionalOnClass或者@ConditionalOnMissingClass注解对应的条件类是OnClassCondition,定义如下:
比如FreemarkerAutoConfiguration中的@ConditionalOnClass注解中有value属性是freemarker.template.Configuration.class和FreeMarkerConfigurationFactory.class。在OnClassCondition执行过程中得到的最终ConditionalOutcome中的log message如下:
基于Bean的条件注解
@ConditionalOnBean(Spring容器中存在指明的bean)、@ConditionalOnMissingBean(Spring容器中不存在指明的bean)以及ConditionalOnSingleCandidate(Spring容器中存在且只存在一个指明的bean)都是基于Bean的条件注解,它们对应的条件类是ConditionOnBean。
@ConditionOnBean注解定义如下:
OnBeanCondition条件类的匹配代码如下:
SpringBoot还提供了其他比如ConditionalOnJava、ConditionalOnNotWebApplication、ConditionalOnWebApplication、ConditionalOnResource、ConditionalOnProperty、ConditionalOnExpression等条件注解,有兴趣的读者可以自行查看它们的底层处理逻辑。
各种条件注解的总结




SpringBoot条件注解的激活机制
分析完了条件注解的执行逻辑之后,接下来的问题就是SpringBoot是如何让这些条件注解生效的?
SpringBoot使用ConditionEvaluator这个内部类完成条件注解的解析和判断。
在Spring容器的refresh过程中,只有跟解析或者注册bean有关系的类都会使用ConditionEvaluator完成条件注解的判断,这个过程中一些类不满足条件的话就会被skip。这些类比如有AnnotatedBeanDefinitionReader、ConfigurationClassBeanDefinitionReader、ConfigurationClassParse、ClassPathScanningCandidateComponentProvider等。
比如ConfigurationClassParser的构造函数会初始化内部属性conditionEvaluator:
ConditionEvaluator的skip方法:
SpringBoot在条件注解的解析log记录在了ConditionEvaluationReport类中,可以通过BeanFactory获取(BeanFactory是有父子关系的;每个BeanFactory都存有一份ConditionEvaluationReport,互不相干):
打印出条件注解下的类加载信息:
Last updated
Was this helpful?