<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          @Bean 與 @Component 用在同一個(gè)類上,會(huì)怎么樣?【文末贈(zèng)書(shū)活動(dòng)】

          共 4683字,需瀏覽 10分鐘

           ·

          2022-05-13 21:58

          關(guān)注Java學(xué)習(xí)之道一起成長(zhǎng),一起學(xué)習(xí)~

          作者: 青石路
          來(lái)源: https://www.cnblogs.com/youzhibing/p/15354706.html

          一、疑慮背景

          疑慮描述

          最近,在進(jìn)行開(kāi)發(fā)的過(guò)程中,發(fā)現(xiàn)之前的一個(gè)寫法,類似如下

          以我的理解,@Configuration?加?@Bean?會(huì)創(chuàng)建一個(gè) userName 不為 null 的 UserManager 對(duì)象,而?@Component?也會(huì)創(chuàng)建一個(gè) userName 為 null 的 UserManager 對(duì)象

          那么我們?cè)谄渌麑?duì)象中注入 UserManager 對(duì)象時(shí),到底注入的是哪個(gè)對(duì)象?

          因?yàn)轫?xiàng)目已經(jīng)上線了很長(zhǎng)一段時(shí)間了,所以這種寫法沒(méi)有編譯報(bào)錯(cuò),運(yùn)行也沒(méi)有出問(wèn)題

          后面去找同事了解下,實(shí)際是想讓

          生效,而實(shí)際也確實(shí)是它生效了

          那么問(wèn)題來(lái)了:Spring 容器中到底有幾個(gè) UserManager 類型的對(duì)象?

          Spring Boot 版本

          項(xiàng)目中用的 Spring Boot 版本是:2.0.3.RELEASE

          對(duì)象的 scope 是默認(rèn)值,也就是 singleton

          二、結(jié)果驗(yàn)證

          驗(yàn)證方式有很多,可以 debug 跟源碼,看看 Spring 容器中到底有幾個(gè) UserManager 對(duì)象,也可以直接從 UserManager 構(gòu)造方法下手,看看哪幾個(gè)構(gòu)造方法被調(diào)用,等等

          我們從構(gòu)造方法下手,看看 UserManager 到底實(shí)例化了幾次

          只有有參構(gòu)造方法被調(diào)用了,無(wú)參構(gòu)造方法巋然不動(dòng)(根本沒(méi)被調(diào)用)

          既然 UserManager 構(gòu)造方法只被調(diào)用了一次,那么前面的問(wèn)題:到底注入的是哪個(gè)對(duì)象

          答案也就清晰了,沒(méi)得選了呀,只能是?@Configuration?加?@Bean?創(chuàng)建的 userName 不為 null 的 UserManager 對(duì)象

          問(wèn)題又來(lái)了:為什么不是?@Component?創(chuàng)建的 userName 為 null 的 UserManager 對(duì)象?

          三、源碼解析

          @Configuration?與?@Component?關(guān)系很緊密

          所以@Configuration?能夠被?component scan

          其中?ConfigurationClassPostProcessor?與@Configuration?息息相關(guān),其類繼承結(jié)構(gòu)圖如下:

          它實(shí)現(xiàn)了?BeanFactoryPostProcessor?接口和?PriorityOrdered?接口,關(guān)于?BeanFactoryPostProcessor,可以看看:

          https://www.cnblogs.com/youzhibing/p/10559337.html

          那么我們從?AbstractApplicationContext?的 refresh 方法調(diào)用的?invokeBeanFactoryPostProcessors(beanFactory)開(kāi)始,來(lái)跟下源碼

          此時(shí)完成了?com.lee.qsl?包下的?component scan?,?com.lee.qsl?包及子包下的 UserConfig 、 UserController 和 UserManager 都被掃描出來(lái)

          注意,此刻@Bean?的處理還未開(kāi)始, UserManager 是通過(guò)@Component?而被掃描出來(lái)的;此時(shí) Spring 容器中?beanDefinitionMap?中的 UserManager 是這樣的

          接下來(lái)一步很重要,與我們想要的答案息息相關(guān)

          循環(huán)遞歸處理 UserConfig 、 UserController 和 UserManager ,把它們都封裝成?ConfigurationClass?,遞歸掃描?BeanDefinition

          循環(huán)完之后,我們來(lái)看看?configClasses

          UserConfig bean?定義信息中 beanMethods 中有一個(gè)元素 [BeanMethod:name=userManager,declaringClass=com.lee.qsl.config.UserConfig]

          然后我們接著往下走,來(lái)仔細(xì)看看答案出現(xiàn)的環(huán)節(jié)

          是不是有什么發(fā)現(xiàn)?@Component?修飾的 UserManager 定義直接被覆蓋成了?@Configuration + @Bean?修飾的 UserManager 定義

          Bean 定義類型也由?ScannedGenericBeanDefinition?替換成了?ConfigurationClassBeanDefinition

          后續(xù)通過(guò)?BeanDefinition?創(chuàng)建實(shí)例的時(shí)候,創(chuàng)建的自然就是?@Configuration + @Bean?修飾的 UserManager ,也就是會(huì)反射調(diào)用 UserManager 的有參構(gòu)造方法

          自此,答案也就清楚了。歡迎關(guān)注公眾號(hào)"Java學(xué)習(xí)之道",查看更多干貨!

          Spring 其實(shí)給出了提示:

          2021-10-03?20:37:33.697??INFO?13600?---?[???????????
          main]?o.s.b.f.s.DefaultListableBeanFactory?????:?Overriding?bean?definition?for?bean?'userManager'?with?a?different?definition:?replacing?[Generic?bean:?class?[com.lee.qsl.manager.UserManager];?scope=singleton;?abstract=false;?lazyInit=false;?autowireMode=0;?dependencyCheck=0;?autowireCandidate=true;?primary=false;?factoryBeanName=null;?factoryMethodName=null;?initMethodName=null;?destroyMethodName=null;?defined?in?file?[D:\qsl-project\spring-boot-bean-component\target\classes\com\lee\qsl\manager\UserManager.class]]?with?[Root?bean:?class?[null];?scope=;?abstract=false;?lazyInit=false;?autowireMode=3;?dependencyCheck=0;?autowireCandidate=true;?primary=false;?factoryBeanName=userConfig;?factoryMethodName=userManager;?initMethodName=null;?destroyMethodName=(inferred);?defined?in?class?path?resource?[com/lee/qsl/config/UserConfig.class]]

          只是日志級(jí)別是 info ,太不顯眼了

          四、Spring 升級(jí)優(yōu)化

          可能 Spring 團(tuán)隊(duì)意識(shí)到了 info 級(jí)別太不顯眼的問(wèn)題,或者說(shuō)意識(shí)到了直接覆蓋的處理方式不太合理

          所以在?Spring 5.1.2.RELEASE?(Spring Boot 則是 2.1.0.RELEASE )做出了優(yōu)化處理

          我們來(lái)具體看看

          啟動(dòng)直接報(bào)錯(cuò),Spring 也給出了提示

          The?bean?'userManager',?defined?in?class?path?resource?[com/lee/qsl/config/UserConfig.class],?could?not?be?registered.?A?bean?with?that?name?has?already?been?defined?in?file?[D:\qsl-project\spring-boot-bean-component\target\classes\com\lee\qsl\manager\UserManager.class]?and?overriding?is?disabled.

          我們來(lái)跟下源碼,主要看看與?Spring 5.0.7.RELEASE?的區(qū)別

          新增了配置項(xiàng)?allowBeanDefinitionOverriding?來(lái)控制是否允許?BeanDefinition?覆蓋,默認(rèn)情況下是不允許的

          我們可以在配置文件中配置:spring.main.allow-bean-definition-overriding=true?,允許?BeanDefinition?覆蓋

          這種處理方式是更優(yōu)的,將選擇權(quán)交給開(kāi)發(fā)人員,而不是自己偷偷的處理,已達(dá)到開(kāi)發(fā)者想要的效果

          五、總結(jié)

          Spring 5.0.7.RELEASE?(?Spring Boot 2.0.3.RELEASE?) 支持@Configuration + @Bean?與@Component?同時(shí)作用于同一個(gè)類

          啟動(dòng)時(shí)會(huì)給 info 級(jí)別的日志提示,同時(shí)會(huì)將@Configuration + @Bean?修飾的 BeanDefinition 覆蓋掉@Component?修飾的 BeanDefinition

          也許 Spring 團(tuán)隊(duì)意識(shí)到了上述處理不太合適,于是在?Spring 5.1.2.RELEASE?做出了優(yōu)化處理

          增加了配置項(xiàng):allowBeanDefinitionOverriding?,將主動(dòng)權(quán)交給了開(kāi)發(fā)者,由開(kāi)發(fā)者自己決定是否允許覆蓋

          六、補(bǔ)充

          關(guān)于?allowBeanDefinitionOverriding?,前面講的不對(duì),后面特意去翻了下源碼,補(bǔ)充如下

          Spring 1.2 引進(jìn)?DefaultListableBeanFactory?的時(shí)候就有了?private boolean allowBeanDefinitionOverriding = true;,默認(rèn)是允許?BeanDefinition?覆蓋

          Spring 4.1.2 引進(jìn)了?isAllowBeanDefinitionOverriding()方法

          Spring 自始至終默認(rèn)都是允許?BeanDefinition?覆蓋的,變的是 Spring Boot , Spring Boot 2.1.0 之前沒(méi)有覆蓋 Spring 的?allowBeanDefinitionOverriding?默認(rèn)值,仍是允許?BeanDefinition?覆蓋的

          Spring Boot 2.1.0 中 SpringApplication 定義了私有屬性:allowBeanDefinitionOverriding

          沒(méi)有顯示的指定值,那么默認(rèn)值就是 false ,之后在 Spring Boot 啟動(dòng)過(guò)程中,會(huì)用此值覆蓋掉 Spring 中的?allowBeanDefinitionOverriding?的默認(rèn)值

          關(guān)于?allowBeanDefinitionOverriding??,我想大家應(yīng)該已經(jīng)清楚了。


          送書(shū)活動(dòng)

          本次贈(zèng)送的書(shū)籍有以下三本可選擇:

          ???免費(fèi)獲取方法:

          開(kāi)獎(jiǎng)日期 5月15日 12:00,開(kāi)獎(jiǎng)前均可公眾號(hào)后臺(tái)回復(fù)?【?java學(xué)習(xí)?關(guān)鍵詞參與活動(dòng)?。?!

          點(diǎn)擊進(jìn)入回復(fù)「java學(xué)習(xí)」抽獎(jiǎng)品

          沒(méi)加小編微信的建議先加一下小編微信,方便中獎(jiǎng)之后安排發(fā)貨

          瀏覽 48
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  小姐操逼视频 | 天天操天天日天天操 | 高清国产视频在线观看 | 韩国色五月婷婷 | 影音先锋久久精品视频 |