<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>

          Spring 容器中到底有幾個 UserManager 類型的對象?

          共 5003字,需瀏覽 11分鐘

           ·

          2022-06-17 21:00

          不點(diǎn)藍(lán)字關(guān)注,我們哪來故事?

          • 疑慮背景
            • 疑慮描述
            • Spring Boot 版本
          • 結(jié)果驗證
          • 源碼解析
          • Spring 升級優(yōu)化
          • 總結(jié)
            • 補(bǔ)充

          疑慮背景

          疑慮描述

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

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

          那么我們在其他對象中注入 UserManager 對象時,到底注入的是哪個對象?

          因為項目已經(jīng)上線了很長一段時間了,所以這種寫法沒有編譯報錯,運(yùn)行也沒有出問題

          后面去找同事了解下,實際是想讓

          生效,而實際也確實是它生效了

          那么問題來了:Spring 容器中到底有幾個 UserManager 類型的對象?

          Spring Boot 版本

          項目中用的 Spring Boot 版本是:2.0.3.RELEASE

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

          結(jié)果驗證

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

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

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

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

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

          問題又來了:為什么不是 @Component 創(chuàng)建的 userName 為 null 的 UserManager 對象?

          源碼解析

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

          所以@Configuration 能夠被 component scan

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

          它實現(xiàn)了 BeanFactoryPostProcessor 接口和 PriorityOrdered 接口,關(guān)于 BeanFactoryPostProcessor,

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

          此時完成了 com.lee.qsl 包下的 component scan , com.lee.qsl 包及子包下的 UserConfig 、 UserController 和 UserManager 都被掃描出來

          注意,此刻@Bean 的處理還未開始, UserManager 是通過@Component 而被掃描出來的;此時 Spring 容器中 beanDefinitionMap 中的 UserManager 是這樣的

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

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

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

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

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

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

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

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

          自此,答案也就清楚了

          Spring 其實給出了提示

          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]]

          只是日志級別是 info ,太不顯眼了

          Spring 升級優(yōu)化

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

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

          我們來具體看看

          啟動直接報錯,Spring 也給出了提示

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

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

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

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

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

          總結(jié)

          Spring 5.0.7.RELEASESpring Boot 2.0.3.RELEASE ) 支持@Configuration + @Bean@Component 同時作用于同一個類

          啟動時會給 info 級別的日志提示,同時會將@Configuration + @Bean 修飾的 BeanDefinition 覆蓋掉@Component 修飾的 BeanDefinition

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

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

          補(bǔ)充

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

          Spring 1.2 引進(jìn) DefaultListableBeanFactory 的時候就有了 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 之前沒有覆蓋 Spring 的 allowBeanDefinitionOverriding 默認(rèn)值,仍是允許 BeanDefinition 覆蓋的

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

          沒有顯示的指定值,那么默認(rèn)值就是 false ,之后在 Spring Boot 啟動過程中,會用此值覆蓋掉 Spring 中的 allowBeanDefinitionOverriding 的默認(rèn)值

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

          ////// END //////
          ↓ 點(diǎn)擊下方關(guān)注,看更多架構(gòu)分享 ↓
          瀏覽 53
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  国产黄色视频在线免费观看 | 日本国产高清视频 | 91精选久久| wwwjiujiu | 欧美在线视频99 |