目錄
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、Condition的matches方法
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、AutoConfigurationImportFilter的match方法
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其內部也是有條件的,但是這樣的話如果不滿足條件,就不用加載其內部那麼多註解,以提交效率。