成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

disconf問(wèn)題引發(fā)對(duì)spring boot 配置加載的探究

W4n9Hu1 / 2952人閱讀

摘要:?jiǎn)栴}今天小伙伴跑過(guò)來(lái)說(shuō),搭建框架的時(shí)候出現(xiàn)配置好的信息不能夠及時(shí)注入到實(shí)體類(lèi)中的情況。他通過(guò)實(shí)踐發(fā)現(xiàn),加載的時(shí)候,通過(guò)注入的實(shí)體類(lèi)里面沒(méi)有值。等到容器加載完成后,在層注入的是有數(shù)據(jù)的,搞了接近一天。表明當(dāng)前依賴(lài)于另外一個(gè),可以用來(lái)控制順序。

問(wèn)題

今天小伙伴跑過(guò)來(lái)說(shuō),搭建框架的時(shí)候出現(xiàn)disconf配置好的信息不能夠及時(shí)注入到實(shí)體類(lèi)中的情況。他通過(guò)實(shí)踐發(fā)現(xiàn),spring 加載Configuration 的時(shí)候,通過(guò)@Autowired注入的RedisProperties 實(shí)體類(lèi)里面沒(méi)有值。等到容器加載完成后,在Controller 層注入的RedisProperties是有數(shù)據(jù)的,搞了接近一天。我在他控制臺(tái)看到了如下信息(簡(jiǎn)化):

**** DISCONF START FIRST SCAN ****
//此處省略
**** DISCONF END FIRST SCAN ****
//@configuration 注冊(cè)bean的信息(可以自己添加日志)
**** DISCONF START SECOND SCAN ****
//此處省略
**** DISCONF END SECOND SCAN ****

通過(guò)信息可以看出,關(guān)鍵問(wèn)題出現(xiàn)在了第二次掃描在Bean注冊(cè)之后。第二次掃描負(fù)責(zé)將配置注入實(shí)體類(lèi)中,詳細(xì)可以參考disconf-client設(shè)計(jì)

那么第二次掃描在什么時(shí)候進(jìn)行的呢,打開(kāi)DisconfMgrBeanSecond 類(lèi)

public class DisconfMgrBeanSecond{
    public void init(){
        DisconfMgr.getInstance().secondScan(); //此處進(jìn)行第二次掃描
    }
    public void destroy(){
        DisconfMgr.getInstance().close();
    }
}

現(xiàn)在的問(wèn)題一下明了了,我們需要做的也就是將 DisconfMgrBeanSecond 的Bean注冊(cè)提前,提前至@Configuration之前。我這里用的是@DependsOn注解,將其放在Properties實(shí)體類(lèi)上。表明當(dāng)前Bean依賴(lài)于另外一個(gè)Bean,可以用來(lái)控制順序。

思考

上面的方法只是使用技巧解決了實(shí)際問(wèn)題,我們不禁要思考了,spring加載的順序到底是怎么樣的?為什么有的項(xiàng)目沒(méi)有加載順序問(wèn)題,有的就會(huì)出bug。接下來(lái)我們就來(lái)深入擼一下spring的源碼。(本文基于的源碼為 spring boot 2.0.0.RELEASE)

調(diào)試方法

很多人不太會(huì)調(diào)試源碼,一上手就從入口函數(shù)開(kāi)始,點(diǎn)幾下就自己犯暈了。還有些人習(xí)慣看類(lèi)圖,從全局去看,也會(huì)很累。這里不是說(shuō)類(lèi)圖方式不好,而是分情況而定。比如你讀 Java 集合框架,類(lèi)圖就是一個(gè)不錯(cuò)的選擇,一來(lái)集合類(lèi)功能相對(duì)獨(dú)立,二來(lái)集合本身很符合面向?qū)ο蟮乃枷?。面?duì)spring這種名字很相似,代碼龐大的大型框架時(shí),建議還是以點(diǎn)入面,有目的的去看。這里介紹一下我自己使用的方法:

編寫(xiě)測(cè)試工程,比如我要理解spring @Configuration的加載過(guò)程,先用spring boot 快速搭建一個(gè)可以運(yùn)行的工程

在自己需要了解的地方打斷點(diǎn)

觀察調(diào)用棧,找到關(guān)鍵方法

如下圖

Debugger 菜單欄中我們很容易找到調(diào)用棧的信息,觀察這些方法,我們可以看到這三個(gè)方法的方法名很像我們想知道的加載過(guò)程

在仔細(xì)點(diǎn)開(kāi)源碼會(huì)發(fā)現(xiàn) refresh()方法下的如下代碼

                this.postProcessBeanFactory(beanFactory); //上下文子類(lèi)對(duì)beanFactory進(jìn)行后置處理
                this.invokeBeanFactoryPostProcessors(beanFactory);//調(diào)用工廠處理器,對(duì)bean進(jìn)行注冊(cè)
                this.registerBeanPostProcessors(beanFactory); // 注冊(cè)bean的攔截處理器
                this.initMessageSource(); //初始化消息源
                this.initApplicationEventMulticaster(); //初始化上下文事件多播器
                this.onRefresh(); //初始化其他子類(lèi)上下文的特殊beans
                this.registerListeners(); //檢查監(jiān)聽(tīng)類(lèi)的bean,并注冊(cè)他們
                this.finishBeanFactoryInitialization(beanFactory); //實(shí)例化剩余非懶加載的bean單利
                this.finishRefresh(); //完成后刷新,發(fā)布相應(yīng)的事件

如果你通過(guò)idea把源碼下載下來(lái)的話,可以看到光標(biāo)停在 this.finishBeanFactoryInitialization(beanFactory)處,表明此時(shí)具體進(jìn)入的方法。好了,調(diào)試方法暫時(shí)就說(shuō)到這里,還是來(lái)看源碼吧。

源碼分析

上面提了一下@Configuration注解的bean 入口在finishBeanFactoryInitialization(beanFactory)方法中,接著往下走到preInstantiateSingletons()方法中

我們發(fā)現(xiàn)這個(gè)方法里有一個(gè)特別顯眼的屬性,beanDefinitionNames,這個(gè)就是容器的注冊(cè)順序。

我們端點(diǎn)是打在了Test類(lèi)初始化的地方,但通過(guò)debugger 可以發(fā)現(xiàn)入口方法加載的反而是TestController類(lèi),并且中間方法的調(diào)用并沒(méi)有出現(xiàn)HelloServiceimpl類(lèi)和TestServiceImpl類(lèi)的加載??梢?jiàn)真實(shí)bean初始化的順序并不是這樣的。

回頭去找 beanDefinitionNames在哪里初始化的,可以發(fā)現(xiàn)在registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法中,循環(huán)添加的,接下來(lái)再去找registerBeanDefinition 在什么地方調(diào)用。

再次打斷點(diǎn)定位到 ClassPathBeanDefinitionScanner.doscan() 方法上

protected Set doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set beanDefinitions = new LinkedHashSet<>();
        for (String basePackage : basePackages) {
            //掃描package,尋找候選組件
            Set candidates = findCandidateComponents(basePackage);
            //候選組件進(jìn)行處理,處理其他注解
            for (BeanDefinition candidate : candidates) {
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                }
                if (candidate instanceof AnnotatedBeanDefinition) {
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                }
                if (checkCandidate(beanName, candidate)) {
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    definitionHolder =
                            AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }
        return beanDefinitions;
    }

首先通過(guò)掃描找出候選組件,掃描的范圍包含basePackages目錄下的所有class文件,如果符合條件,將其放在LinkedHashSet中,使其保證唯一有序。判斷條件在ClassPathScanningCandidateComponentProvider.isCandidateComponent()方法中。這個(gè)類(lèi)有兩個(gè)屬性,excludeFilters和includeFilters,分別控制著候選類(lèi)的排除鏈和包含鏈。我debugger不進(jìn)行設(shè)置的話,默認(rèn)選取下面三種接口子類(lèi)作為候選加載類(lèi),org.springframework.stereotype.Component,javax.annotation.ManagedBean,javax.inject.Named,而@Configuration,@Controller,@Service,@Repository,都是基于Component的注解。

真實(shí)bean的加載

上面只是說(shuō)明白了類(lèi)文件的注冊(cè)順序,他是通過(guò)掃描包名,類(lèi)名這樣排下來(lái)的,只是一個(gè)初步順序。

先來(lái)看一下之前調(diào)試的初步順序 testConfig-->helloController-->testController-->helloServiceImpl-->testServiceImpl-->test

整體看下來(lái),他是按照包名和類(lèi)型排序的,只不過(guò)有一點(diǎn)需要注意 test 所在的包實(shí)際上是在Impl 前面的,且Test類(lèi)上沒(méi)有任何注解,這表明他們的注冊(cè)順序其實(shí)是:先掃描Component,在掃描@Bean注解。

當(dāng)bean真正加載的時(shí)候是這樣加載的,每加載一個(gè)類(lèi),看他有沒(méi)有依賴(lài),有的話同時(shí)加載依賴(lài)bean。這也就解釋了為什么testController為什么跳過(guò)impl 直接加載test。

如何控制加載順序

其實(shí)有很多方法控制順序,依賴(lài)注入提前,@DepensOn 和 @Order注解,實(shí)現(xiàn)Ordered接口等等。像面對(duì)disconf這種第三方框架類(lèi)的bean,最好是使用@DepensOn 來(lái)控制加載順序

總結(jié)

bean的加載還有很多其他的細(xì)節(jié),這里就不一一展開(kāi)了。本文主要專(zhuān)注加載順序,順便聊一下初學(xué)如何去看源碼。總結(jié)起來(lái)就是一句話,小目標(biāo),不拓展。

寫(xiě)到最后才發(fā)現(xiàn)上面的問(wèn)題,加載順序并不是主要原因!!(°?°?) 好吧,下次一定搞清楚了再動(dòng)筆,這里也買(mǎi)一個(gè)關(guān)子,感興趣的童鞋可以自己Debugger找一下原因。這里給個(gè)小提示,是跟代理有關(guān)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74477.html

相關(guān)文章

  • Spring-Boot自定義Starter實(shí)踐

    摘要:此文已由作者王慎為授權(quán)網(wǎng)易云社區(qū)發(fā)布。歡迎訪問(wèn)網(wǎng)易云社區(qū),了解更多網(wǎng)易技術(shù)產(chǎn)品運(yùn)營(yíng)經(jīng)驗(yàn)。網(wǎng)易云免費(fèi)體驗(yàn)館,成本體驗(yàn)款云產(chǎn)品更多網(wǎng)易技術(shù)產(chǎn)品運(yùn)營(yíng)經(jīng)驗(yàn)分享請(qǐng)點(diǎn)擊。文章來(lái)源網(wǎng)易云社區(qū) 此文已由作者王慎為授權(quán)網(wǎng)易云社區(qū)發(fā)布。 歡迎訪問(wèn)網(wǎng)易云社區(qū),了解更多網(wǎng)易技術(shù)產(chǎn)品運(yùn)營(yíng)經(jīng)驗(yàn)。 disconf-spring-boot-starter使用方法:引入maven依賴(lài): com.netease.hai...

    goji 評(píng)論0 收藏0
  • 源碼解讀 Spring Boot Profiles

    摘要:有了配置文件之后,啟動(dòng)程序,我們首先可以看到日志輸入,由此可以看出程序讀取了的配置。首先,根據(jù)的全局查找功能,直接搜索這些詞出現(xiàn)的位置,進(jìn)行定位,可以找到這個(gè)日志出現(xiàn)于方法之中。由于我們的配置文件在下,所以只要留意當(dāng)為的程序執(zhí)行情況即可。 前言 上文《一文掌握 Spring Boot Profiles》 是對(duì) Spring Boot Profiles 的介紹和使用,因此本文將從源碼角度...

    Dionysus_go 評(píng)論0 收藏0
  • 從零到百億互聯(lián)網(wǎng)金融架構(gòu)發(fā)展史

    摘要:總體介紹在互聯(lián)網(wǎng)金融行業(yè)一百多億其實(shí)也算不上大平臺(tái),也就是二級(jí)陣營(yíng)吧,其實(shí)每次的架構(gòu)升級(jí)都是隨著業(yè)務(wù)重大推進(jìn)而伴隨的,在前一代系統(tǒng)架構(gòu)上遇到的問(wèn)題,業(yè)務(wù)開(kāi)發(fā)過(guò)程中積累一些優(yōu)秀的開(kāi)發(fā)案例,在下一代系統(tǒng)開(kāi)發(fā)中就會(huì)大力推進(jìn)架構(gòu)升級(jí)。 回想起從公司成立敲出的第一行代碼算起到現(xiàn)在也快三年了,平臺(tái)的技術(shù)架構(gòu),技術(shù)體系也算是經(jīng)歷了四次比較重大的升級(jí)轉(zhuǎn)化(目前第四代架構(gòu)體系正在進(jìn)行中),臨近年底也想抽...

    mrcode 評(píng)論0 收藏0
  • 從零到百億互聯(lián)網(wǎng)金融架構(gòu)發(fā)展史

    摘要:總體介紹在互聯(lián)網(wǎng)金融行業(yè)一百多億其實(shí)也算不上大平臺(tái),也就是二級(jí)陣營(yíng)吧,其實(shí)每次的架構(gòu)升級(jí)都是隨著業(yè)務(wù)重大推進(jìn)而伴隨的,在前一代系統(tǒng)架構(gòu)上遇到的問(wèn)題,業(yè)務(wù)開(kāi)發(fā)過(guò)程中積累一些優(yōu)秀的開(kāi)發(fā)案例,在下一代系統(tǒng)開(kāi)發(fā)中就會(huì)大力推進(jìn)架構(gòu)升級(jí)。 回想起從公司成立敲出的第一行代碼算起到現(xiàn)在也快三年了,平臺(tái)的技術(shù)架構(gòu),技術(shù)體系也算是經(jīng)歷了四次比較重大的升級(jí)轉(zhuǎn)化(目前第四代架構(gòu)體系正在進(jìn)行中),臨近年底也想抽...

    U2FsdGVkX1x 評(píng)論0 收藏0
  • 如何優(yōu)雅關(guān)閉 Spring Boot 應(yīng)用

    摘要:除了,還有十余種,有的是特定操作,比如轉(zhuǎn)儲(chǔ)內(nèi)存日志有的是信息展示,比如顯示應(yīng)用健康狀態(tài)。 showImg(http://ww1.sinaimg.cn/large/006tNc79gy1g5qb2coyfoj30u00k0tan.jpg); 前言 隨著線上應(yīng)用逐步采用 SpringBoot 構(gòu)建,SpringBoot應(yīng)用實(shí)例越來(lái)多,當(dāng)線上某個(gè)應(yīng)用需要升級(jí)部署時(shí),常常簡(jiǎn)單粗暴地使用 kil...

    xiyang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<