@Conditional注解的源码分析

缘起

@Conditional 注解我们大家都用过。就是条件化导入bean的手段. 但是它的原理是什么呢? 且听我慢慢道来.

分析

参考的demo为【1】.

其实在【1】中,我们已经知道了一个重要的类——ConfigurationClassPostProcessor ,它是什么时候被初始化(refresh中的this()调用)以及什么时候工作(refresh中的invokeBeanFactoryPostProcessors)的了.

这个类我们一直跟到【1】中的源码4,但是【1】中的源码4没有展示全loadBeanDefinitionsForBeanMethod方法

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(BeanMethod)

1
2
3
4
5
6
7
8
9
10
11
12
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
String methodName = metadata.getMethodName();

// Do we need to mark the bean as skipped by its condition?
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
...
this.registry.registerBeanDefinition(beanName, beanDefToRegister); // registry就是ioc容器——beanFactory

​ 源码1

根据源码1,我们知道只有过了第7行这一关,才会被注册到ioc容器——registry(或者说是beanFactory)中去。

其中第12行的registry和第7行的conditionEvaluator是在【1】的源码2的第8行产生的.那里是直接new了ConfigurationClassBeanDefinitionReader,以构造器的方式传入了registry,而在ConfigurationClassBeanDefinitionReader的构造器中

1
2
3
4
5
6
7
8
9
10
11
12
ConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
ResourceLoader resourceLoader, Environment environment, BeanNameGenerator importBeanNameGenerator,
ImportRegistry importRegistry) {

this.registry = registry;
this.sourceExtractor = sourceExtractor;
this.resourceLoader = resourceLoader;
this.environment = environment;
this.importBeanNameGenerator = importBeanNameGenerator;
this.importRegistry = importRegistry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}

​ 源码2

源码2的第11行直接创建了源码1第7行要用的conditionEvaluator. 跟进源码1的第七行

org.springframework.context.annotation.ConditionEvaluator.shouldSkip(AnnotatedTypeMetadata, ConfigurationPhase)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
List<Condition> conditions = new ArrayList<Condition>();
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
conditions.add(condition);
}
}
...
for (Condition condition : conditions) {
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
if (requiredPhase == null || requiredPhase == phase) {
if (!condition.matches(this.context, metadata)) {
return true; // 不会被加入到bean中去的这里返回true
}
}
}
...

​ 源码3

源码3 的第三行从元数据中获取@Conditional注解中的属性信息并使用反射实例化,加入到conditions中去,然后第16行将调用我们实现的Condition接口的matches方法. 如果matches方法返回true的话,则shouldSkip返回false,即不会被跳过,亦即被加入到ioc容器中去, 否则不会被加入到ioc容器中去,注意,这里的加入其实是源码1 的第12 行——注册bean的定义.

至此,@Conditional 注解的原理分析完毕.

DEMO

【1】https://github.com/yfsyfs/backend/tree/master/spring-annotation-condition

参考

【1】https://yfsyfs.github.io/2019/06/23/Configuration-%E6%B3%A8%E8%A7%A3%E6%BA%90%E7%A0%81/