獲取自動(dòng)裝配條件匹配結(jié)果

以條件裝配注解ConditionalOnMissingBean為例娃豹,該注解基于OnBeanCondition實(shí)現(xiàn)

@Conditional(OnBeanCondition.class)
public @interface ConditionalOnMissingBean {
    // ...
}

查看OnBeanCondition類繼承結(jié)構(gòu),關(guān)注從接口Condition派生出來的分支

image.png

重點(diǎn)關(guān)注SpringBootCondition购裙,以下為該類的主要代碼

public abstract class SpringBootCondition implements Condition {
    @Override
    public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String classOrMethodName = getClassOrMethodName(metadata);
        try {
            ConditionOutcome outcome = getMatchOutcome(context, metadata);  // 匹配結(jié)果
            logOutcome(classOrMethodName, outcome);                         // 輸出匹配結(jié)果懂版,日志級(jí)別trace
            recordEvaluation(context, classOrMethodName, outcome);          // 存儲(chǔ)匹配結(jié)果
            return outcome.isMatch();                                       // 返回匹配結(jié)果,判斷某個(gè)bean是否應(yīng)該被Spring容器加載
        }
        // ...
    }
    
    public abstract ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata);
}

再來看OnBeanCondition繼承SpringBootCondition之后躏率,做了什么躯畴?

class OnBeanCondition extends FilteringSpringBootCondition implements ConfigurationCondition {
    // 判斷當(dāng)前Spring容器是否可以加在當(dāng)前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());
        }
        // 省略其他基于@ConditionOnXXXX注解屬性的解析判斷
        return ConditionOutcome.match(matchMessage);    // 將匹配結(jié)果返回
    }
}    

接著,回到上面提到的SpringBootCondition薇芝,看看它獲取了匹配結(jié)果之后蓬抄,做了什么操作?

public abstract class SpringBootCondition implements Condition {
    @Override
    public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String classOrMethodName = getClassOrMethodName(metadata);
        try {
            ConditionOutcome outcome = getMatchOutcome(context, metadata);  // 匹配結(jié)果
            logOutcome(classOrMethodName, outcome);                         // 輸出匹配結(jié)果恩掷,日志級(jí)別trace
            recordEvaluation(context, classOrMethodName, outcome);          // 存儲(chǔ)匹配結(jié)果
            return outcome.isMatch();                                       // 返回匹配結(jié)果倡鲸,判斷某個(gè)bean是否應(yīng)該被Spring容器加載
        }
        // ...
    }
    
    /**
     * 基于Trace級(jí)別,輸出匹配結(jié)果
     */
    protected final void logOutcome(String classOrMethodName, ConditionOutcome outcome) {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace(getLogMessage(classOrMethodName, outcome));
        }
    }
    
    /**
     * 存儲(chǔ)條件裝配結(jié)果
     */
    private void recordEvaluation(ConditionContext context, String classOrMethodName, ConditionOutcome outcome) {
        if (context.getBeanFactory() != null) {
            ConditionEvaluationReport.get(context.getBeanFactory()).recordConditionEvaluation(classOrMethodName, this,
                    outcome);
        }
    }
}

上面出現(xiàn)了ConditionEvaluationReport黄娘,這個(gè)類將會(huì)嘗試從Spring容器中獲取一個(gè)名為autoConfigurationReport的bean峭状,若不存在則創(chuàng)建并注冊(cè)到Spring容器中

public final class ConditionEvaluationReport {

    private static final String BEAN_NAME = "autoConfigurationReport";
    
    public static ConditionEvaluationReport get(ConfigurableListableBeanFactory beanFactory) {
        synchronized (beanFactory) {
            ConditionEvaluationReport report;
            // 判斷是否存在名為autoConfigurationReport,類型為ConditionEvaluationReport的單例bean對(duì)象
            if (beanFactory.containsSingleton(BEAN_NAME)) { 
                report = beanFactory.getBean(BEAN_NAME, ConditionEvaluationReport.class);
            }
            // 如果上面判斷不存在逼争,則創(chuàng)建一個(gè)該類型的對(duì)象优床,并注冊(cè)到Spring容器中
            else {
                report = new ConditionEvaluationReport();
                beanFactory.registerSingleton(BEAN_NAME, report);
            }
            locateParent(beanFactory.getParentBeanFactory(), report);
            return report;
        }
    }
    
    /**
     * 存儲(chǔ)條件裝配結(jié)果
     */
    public void recordConditionEvaluation(String source, Condition condition, ConditionOutcome outcome) {
        // 省略入?yún)嘌?..
        this.unconditionalClasses.remove(source);
        if (!this.outcomes.containsKey(source)) {
            this.outcomes.put(source, new ConditionAndOutcomes());  // 如果當(dāng)前類還沒有建立一個(gè)條件匹配結(jié)果對(duì)象先創(chuàng)建
        }
        this.outcomes.get(source).add(condition, outcome);         // ConditionAndOutcomes是一個(gè)迭代對(duì)象,添加一個(gè)匹配結(jié)果
        this.addedAncestorOutcomes = false;
    }
}

知道了以上的原理誓焦,我們就可以獲取Spring注冊(cè)的autoConfigurationReport對(duì)象胆敞,并從中解析bean的條件裝配結(jié)果

public class AutoConfigurationReportDemo {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.scan("com.holybell");
        applicationContext.refresh();

        ConditionEvaluationReport report = applicationContext.getBean("autoConfigurationReport", ConditionEvaluationReport.class);
        Map<String, ConditionEvaluationReport.ConditionAndOutcomes> conditionAndOutcomesBySource = report.getConditionAndOutcomesBySource();

        // 以獲取StringRedisTemplate類的裝配結(jié)果為例
        conditionAndOutcomesBySource.forEach((key, value) -> {
            if (key.toLowerCase().contains("stringredistemplate")) {
                System.out.println("類: " + key);
                for (ConditionEvaluationReport.ConditionAndOutcome outcome : value) {
                    System.out.println("條件裝配結(jié)果 : " + outcome.getOutcome().isMatch() + ",原因: " + outcome.getOutcome().getMessage());
                }
            }
        });

        applicationContext.close();
    }
}

輸出入如下結(jié)果:

類:

org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration#stringRedisTemplate

條件裝配結(jié)果 : true ,

原因:

@ConditionalOnSingleCandidate (types: org.springframework.data.redis.connection.RedisConnectionFactory; SearchStrategy: all) found a primary bean from beans 'redisConnectionFactory';

@ConditionalOnMissingBean (types: org.springframework.data.redis.core.StringRedisTemplate; SearchStrategy: all) did not find any beans

結(jié)合StringRedisTemplate的裝配條件,必須有且僅有一個(gè)RedisConnectionFactory類型的Bean杂伟,必須沒有類型為StringRedisTemplate的bean移层,兩個(gè)條件都符合,因此這個(gè)自動(dòng)裝配的StringRedisTemplate會(huì)被Spring容器加載

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赫粥,一起剝皮案震驚了整個(gè)濱河市观话,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌越平,老刑警劉巖频蛔,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件灵迫,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡晦溪,警方通過查閱死者的電腦和手機(jī)瀑粥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來三圆,“玉大人狞换,你說我怎么就攤上這事∠邮酰” “怎么了哀澈?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長度气。 經(jīng)常有香客問我割按,道長,這世上最難降的妖魔是什么磷籍? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任适荣,我火速辦了婚禮,結(jié)果婚禮上院领,老公的妹妹穿的比我還像新娘弛矛。我一直安慰自己,他們只是感情好比然,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布丈氓。 她就那樣靜靜地躺著,像睡著了一般强法。 火紅的嫁衣襯著肌膚如雪万俗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天饮怯,我揣著相機(jī)與錄音闰歪,去河邊找鬼。 笑死蓖墅,一個(gè)胖子當(dāng)著我的面吹牛库倘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播论矾,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼教翩,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了贪壳?” 一聲冷哼從身側(cè)響起饱亿,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后路捧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡传黄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年杰扫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膘掰。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡章姓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出识埋,到底是詐尸還是另有隱情凡伊,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布窒舟,位于F島的核電站系忙,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏惠豺。R本人自食惡果不足惜银还,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望洁墙。 院中可真熱鬧蛹疯,春花似錦、人聲如沸热监。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽孝扛。三九已至列吼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疗琉,已是汗流浹背冈欢。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盈简,地道東北人凑耻。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像柠贤,于是被迫代替她去往敵國和親香浩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容