目录
1、OnBeanCondition的继承结构
2、Condition的matches方法
1)、创建Spec对象
2)、获取匹配的Bean
3)、判断,组装结果返回
3、AutoConfigurationImportFilter的match方法
在上一篇@Conditional之后,知道了动态判断注册bean的是怎么实现的。执行的时机,回调时传入了什么对象,只是在Spring BootCondition下面会有很多更复杂的情况,所以就使用比较常见的@ConditionalOnBean为例,分析其结构和回调的逻辑。
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
// 指定需要存在的Bean的Class
Class>[] value() default {};
// 指定需要存在的Bean的名称数组
String[] type() default {};
Class extends Annotation>[] annotation() default {};
String[] name() default {};
SearchStrategy search() default SearchStrategy.ALL;
Class>[] parameterizedContainer() default {};
}
当前的Condition类型为OnBeanCondition,先看看其继承关系:
1)、实现了ConfigurationCondition接口,则需要实现getConfigurationPhase,返回REGISTER_BEAN。
2)、OnBeanConditional(与OnWebApplicationCondition、OnClassCondition)共同继承自类FilteringSpringBootCondition类
。
2-1)、FilteringSpringBootCondition其实现了BeanFactoryAware,BeanClassLoaderAware,但是其本身不是Bean,生命周期不会进行回调,而是通过自动装配时回调了invokeAwareMethods。
2-2)、FilteringSpringBootCondition继承自SpringBootCondition,并且SpringBootCondition继承Condition接口,实现了matches方法。
2-3)、FilteringSpringBootCondition实现了AutoConfigurationImportFilter接口,只有一个未实现的方法match。
稍微有点乱,总结起来就是OnBeanConditional(OnWebApplicationCondition、OnClassCondition)作为Spring Boot的实现类,完成了两条线的任务(后面也根据这两条线进行分析,按照Spring Boot的启动过程,会先调用第二部,再调用第一步)。
第一:判断@ComponentScan、ImportSelector等注入的Bean时,会调用Condition的match方法。但是会先调用的SpringBootCondition的matches方法,matches又会调用到自定义的getMatchOutcome方法,最终由OnBeanConditional实现。
第二:作为Spring Boot的实现(并非Spring),需要配合完成自动装配。不仅仅是spring.factories中的自动装配EnableAutoConfiguration配置什么就注册什么。而是需要使用OnBeanConditional(OnWebApplicationCondition、OnClassCondition),配合spring-autoconfigure-metadata.properties中的条件过滤。
SpringBootCondition实现了matches方法:
@Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String classOrMethodName = getClassOrMethodName(metadata);
try {
ConditionOutcome outcome = getMatchOutcome(context, metadata);
logOutcome(classOrMethodName, outcome);
recordEvaluation(context, classOrMethodName, outcome);
return outcome.isMatch();
} // 省略异常代码
}
// 定义抽象方法,让子类实现(相当于钩子)
public abstract ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata);
1)、根据当前的注解类型,获取处理的类或者方法名(比较简单就不看了)
2)、然后会调用定义好的abstract方法,让子类实现,相当于一个钩子。
3)、再打印日志
4)、处理autoConfigurationReport类型的Bean注入。
这些都是Spring Boot关心的步骤,或者模板方法。但是最重要的是子类的不同实现。根据Class是否存在,根据是否为Web项目判断,根据Bean是否存在判断。当然下面还是值分析根据Bean是否存在进行判断:
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage matchMessage = ConditionMessage.empty();
MergedAnnotations annotations = metadata.getAnnotations();
if (annotations.isPresent(ConditionalOnBean.class)) {
Spec spec = new Spec<>(context, metadata, annotations, ConditionalOnBean.class);
MatchResult matchResult = getMatchingBeans(context, spec);
if (!matchResult.isAllMatched()) {
String reason = createOnBeanNoMatchReason(matchResult);
return ConditionOutcome.noMatch(spec.message().because(reason));
}
matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE,
matchResult.getNamesOfAllMatches());
}
if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
Spec spec = new SingleCandidateSpec(context, metadata, annotations);
MatchResult matchResult = getMatchingBeans(context, spec);
if (!matchResult.isAllMatched()) {
return ConditionOutcome.noMatch(spec.message().didNotFind("any beans").atAll());
}
else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matchResult.getNamesOfAllMatches(),
spec.getStrategy() == SearchStrategy.ALL)) {
return ConditionOutcome.noMatch(spec.message().didNotFind("a primary bean from beans")
.items(Style.QUOTE, matchResult.getNamesOfAllMatches()));
}
matchMessage = spec.message(matchMessage).found("a primary bean from beans").items(Style.QUOTE,
matchResult.getNamesOfAllMatches());
}
if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
Spec spec = new Spec<>(context, metadata, annotations,
ConditionalOnMissingBean.class);
MatchResult matchResult = getMatchingBeans(context, spec);
if (matchResult.isAnyMatched()) {
String reason = createOnMissingBeanNoMatchReason(matchResult);
return ConditionOutcome.noMatch(spec.message().because(reason));
}
matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll();
}
return ConditionOutcome.match(matchMessage);
}
由于@ConditionalOnBean、@ConditionalOnSingleCandidate、@ConditionalOnMissingBean三个注解都会回调OnBeanCondition类进行处理。在上一篇博客@Condition的回调时机我们知道,会根据每一个注解类进行回调。
private static class Spec {
private final ClassLoader classLoader;
private final Class> annotationType;
private final Set names;
private final Set types;
private final Set annotations;
private final Set ignoredTypes;
private final Set> parameterizedContainers;
private final SearchStrategy strategy;
}
将@ConditionalOnBean等注解上的属性值,从传入的MergedAnnotations上根据key、value获取出来,并封装成当前的Spec对象,为后续进行准备。
先获取与传入类型匹配的Bean,由于存在继承关系,可能获取到多个值。
protected final MatchResult getMatchingBeans(ConditionContext context, Spec> spec) {
ClassLoader classLoader = context.getClassLoader();
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT;
Set> parameterizedContainers = spec.getParameterizedContainers();
if (spec.getStrategy() == SearchStrategy.ANCESTORS) {
BeanFactory parent = beanFactory.getParentBeanFactory();
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
"Unable to use SearchStrategy.ANCESTORS");
beanFactory = (ConfigurableListableBeanFactory) parent;
}
MatchResult result = new MatchResult();
Set beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy,
spec.getIgnoredTypes(), parameterizedContainers);
for (String type : spec.getTypes()) {
Collection typeMatches = getBeanNamesForType(classLoader, considerHierarchy, beanFactory, type,
parameterizedContainers);
typeMatches.removeAll(beansIgnoredByType);
if (typeMatches.isEmpty()) {
result.recordUnmatchedType(type);
}
else {
result.recordMatchedType(type, typeMatches);
}
}
for (String annotation : spec.getAnnotations()) {
Set annotationMatches = getBeanNamesForAnnotation(classLoader, beanFactory, annotation,
considerHierarchy);
annotationMatches.removeAll(beansIgnoredByType);
if (annotationMatches.isEmpty()) {
result.recordUnmatchedAnnotation(annotation);
}
else {
result.recordMatchedAnnotation(annotation, annotationMatches);
}
}
for (String beanName : spec.getNames()) {
if (!beansIgnoredByType.contains(beanName) && containsBean(beanFactory, beanName, considerHierarchy)) {
result.recordMatchedName(beanName);
}
else {
result.recordUnmatchedName(beanName);
}
}
return result;
}
虽然方法比较长,但是根据在注解上可配置名称,Class,Annotation等方式进行判断。主要还是依赖于BeanFactory的getBeanNamesForType、getBeanNamesForAnnotation、containsBean返回的Bean封装成MatchResult对象。
private static final class MatchResult {
private final Map> matchedAnnotations = new HashMap<>();
private final List matchedNames = new ArrayList<>();
private final Map> matchedTypes = new HashMap<>();
private final List unmatchedAnnotations = new ArrayList<>();
private final List unmatchedNames = new ArrayList<>();
private final List unmatchedTypes = new ArrayList<>();
private final Set namesOfAllMatches = new HashSet<>();
}
ConditionalOnBean或者ConditionalOnSingleCandidate:只要不是一个都不匹配,则返回true。!isAllMatch
boolean isAllMatched() {
return this.unmatchedAnnotations.isEmpty() && this.unmatchedNames.isEmpty()
&& this.unmatchedTypes.isEmpty();
}
ConditionalOnMissBean:只有一个都不存在的情况下才返回true。 !isAnyMatched
boolean isAnyMatched() {
return (!this.matchedAnnotations.isEmpty()) || (!this.matchedNames.isEmpty())
|| (!this.matchedTypes.isEmpty());
}
AutoConfigurationImportFilter的match方法由父类FilteringSpringBootCondition实现。
@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
for (int i = 0; i < outcomes.length; i++) {
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null) {
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null) {
report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
}
}
}
return match;
}
由于父类需要处理ConditionalOnBean,ConditionalOnWeb,ConditionalOnWebApplication、ConditionalOnNotWebApplication等类型,所以具体的判断逻辑需要子类自己实现,父类只处理相同的模板方法部分。同样是定义了抽象方法,然子类实现:
protected abstract ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata);
具体的逻辑比较复杂,但是就是进行filter方法,多层的轮训过滤。
比如:CacheAutoConfiguration为spring.factories中配置的自动装配类,如下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })
@Import({ CacheConfigurationImportSelector.class, CacheManagerEntityManagerFactoryDependsOnPostProcessor.class })
public class CacheAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public CacheManagerCustomizers cacheManagerCustomizers(ObjectProvider> customizers) {
return new CacheManagerCustomizers(customizers.orderedStream().collect(Collectors.toList()));
}
@Bean
public CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties cacheProperties,
ObjectProvider cacheManager) {
return new CacheManagerValidator(cacheProperties, cacheManager);
}
@ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
@ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
static class CacheManagerEntityManagerFactoryDependsOnPostProcessor
extends EntityManagerFactoryDependsOnPostProcessor {
CacheManagerEntityManagerFactoryDependsOnPostProcessor() {
super("cacheManager");
}
}
}
但是,在spring-autoconfigure-metadata.properties中配置了对应的OnBeanCondition等。
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.ConditionalOnBean=\
org.springframework.cache.interceptor.CacheAspectSupport
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.ConditionalOnClass=\
org.springframework.cache.CacheManager
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.AutoConfigureAfter=\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
我自己认为CacheAutoConfiguration其内部也是有条件的,但是这样的话如果不满足条件,就不用加载其内部那么多注解,以提交效率。