Spring Boot源码 - @ConditionalOnBean实现分析

目录

1、OnBeanCondition的继承结构

2、Condition的matches方法

1)、创建Spec对象

2)、获取匹配的Bean

3)、判断,组装结果返回

3、AutoConfigurationImportFilter的match方法


1、OnBeanCondition的继承结构

    在上一篇@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中的条件过滤。

    

2、Conditionmatches方法

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<ConditionalOnBean> 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<ConditionalOnSingleCandidate> 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<ConditionalOnMissingBean> 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的回调时机我们知道,会根据每一个注解类进行回调。

1)、创建Spec<A extends Annotation>对象

private static class Spec<A extends Annotation> {

    private final ClassLoader classLoader;

    private final Class<?> annotationType;

    private final Set<String> names;

    private final Set<String> types;

    private final Set<String> annotations;

    private final Set<String> ignoredTypes;

    private final Set<Class<?>> parameterizedContainers;

    private final SearchStrategy strategy;
}

    将@ConditionalOnBean等注解上的属性值,从传入的MergedAnnotations上根据key、value获取出来,并封装成当前的Spec对象,为后续进行准备。

2)、获取匹配的Bean

    先获取与传入类型匹配的Bean,由于存在继承关系,可能获取到多个值。

protected final MatchResult getMatchingBeans(ConditionContext context, Spec<?> spec) {
    ClassLoader classLoader = context.getClassLoader();
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT;
    Set<Class<?>> 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<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy,
            spec.getIgnoredTypes(), parameterizedContainers);
    for (String type : spec.getTypes()) {
        Collection<String> 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<String> 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<String, Collection<String>> matchedAnnotations = new HashMap<>();

    private final List<String> matchedNames = new ArrayList<>();

    private final Map<String, Collection<String>> matchedTypes = new HashMap<>();

    private final List<String> unmatchedAnnotations = new ArrayList<>();

    private final List<String> unmatchedNames = new ArrayList<>();

    private final List<String> unmatchedTypes = new ArrayList<>();

    private final Set<String> namesOfAllMatches = new HashSet<>();
}

3)、判断,组装结果返回

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());
}

 

3、AutoConfigurationImportFiltermatch方法

    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<CacheManagerCustomizer<?>> customizers) {
        return new CacheManagerCustomizers(customizers.orderedStream().collect(Collectors.toList()));
    }

    @Bean
    public CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties cacheProperties,
                                                                 ObjectProvider<CacheManager> 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其内部也是有条件的,但是这样的话如果不满足条件,就不用加载其内部那么多注解,以提交效率。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章