品Spring:對@Autowired和@Value註解的處理方法
- 2019 年 10 月 9 日
- 筆記
在Spring中能夠完成依賴注入的註解有JavaSE提供的@Resource註解,就是上一篇文章介紹的。
還有JavaEE提供的@javax.inject.Inject註解,這個用的很少,因為一般都不會去引用JavaEE的jar包。
編程新說注:JavaEE早已經被Oracle拋棄了。JavaEE這個名字已經成為歷史。
還有兩個就是@Value和@Autowired註解,這可是Spring自己的親孩子。所以這兩個使用的最多。
雖然註解不一樣,但是目的一樣,都是用來進行依賴注入,而且Spring處理依賴注入都是用的一個套路。
在上一篇文章中已經說的很詳細了,這裡再說一遍,就當鞏固了。
從最通俗的角度來說,依賴注入就是這樣的:
首先,要知道誰需要被注入。用A表示。
其次,要知道把誰注入。用B表示。
再者,從容器中找到它。用find(B)表示。
最後,完成注入動作。用<-表示。
因此,整個過程可以用一個式子表示:
A <- find(B)
Spring採用的統一方案如下:
1)找出一個類中需要被注入的元素,其實就是字段和方法,然後使用InjectedElement和InjectionMetadata來表示。
2)找出需要注入什麼類型的對象,其實就是字段類型和方法參數類型,然後使用DependencyDescriptor來表示。
3)根據第2步的描述,從容器中找到(解析出)這個對象,這由容器負責,beanFactory.resolveDependency(..)。
4)完成具體注入動作,就是把第3步的值應用到第1步里,是字段的話就是設置一下,是方法的話就調用一下。
這是依賴主人的宏觀過程,但是對不同的註解,有些細節性的東西是不同的。下面一起來看下。
被注入元素
如果被注入的是一個字段,如下圖01:
表示字段的值是否必須被注入,還有緩存字段的值。
如果被注入的是一個方法,如下圖02:
表示方法的參數值是否必須被注入,還有緩存方法的參數值。
編程新說注:這裡緩存的字段值和方法參數值,並不是一個具體的值,只是一個依賴描述(DependencyDescriptor)。稍後會看到。
依賴的描述
Spring對@Autowired的處理是按類型進行的,所以必須按類型過濾容器中的所有bean,這樣效率會低一些。
所以當過濾到這樣的bean之後,就會緩存下bean的名稱,下次就直接用這個bean名稱去找,就會很快了。
這就涉及到對依賴描述的擴展,如下圖03:
這裡把bean的名稱和類型都記錄下來,下次直接進行短路操作,使用getBean(..),不用再遍歷了。
從容器中解析出依賴
對字段來說,如下圖04:
使用Field生成一個依賴描述,然後去容器中解析出能夠和字段類型兼容的所有bean,並把bean名稱放入autowiredBeanNames這個Set中去。
然後進行緩存,如下圖05:
主要緩存了bean名稱,在下次再裝配時,進行短路操作。
對於方法來說,如下圖06:
由於方法有多個參數,每個參數都需要依賴,所以就按參數逐個處理。
使用Method和參數索引生成一個MethodParameter,然後再用它生成依賴描述。
然後去容器中解析出能夠和該方法參數類型兼容的所有bean,並把bean名稱放入autowiredBeans這個Set中去。
然後再進行緩存,如下圖07:
依賴的注入
對於字段,調用set方法,如下圖08:
對於方法,調用invoke方法,如下圖09:
以上就是具體的處理過程和對通用處理的擴展。
這裏面似乎有一個問題,就是對@Autowired的處理和@Value的處理應該是不太相同的,但是上述過程中並沒有體現。
這一部分其實是交給了容器去處理了,在beanFactory.resolveDependency(..)這個解析依賴的方法里進行了處理。
對構造方法的處理
構造方法是很特殊的,在容器準備實例化一個bean時,就會去找出一個最合適的構造方法,然後通過反射調用它來生成一個對象。
如果一個類定義了多個構造方法,Spring會通過一個複雜方式從中選出一個最合適的。
由於@Autowired註解可以作用於構造方法上,所以可以用它來適當改變Spring的這種選擇方式。
把@Autowired註解標到其中幾個構造方法上,這樣Spring只會在這些標註解的之間進行選擇,相當於起一個收窄作用。
此時還會把默認無參的構造方法也作為備選,因為標註解的都不能滿足的話,就只能使用默認的。
如果一個類有多個構造方法,但我們想非常明確的使用某一個,那就把@Autowired標在它上,並把註解的required屬性設置為true。
這樣只會把這一個選出來,此時不會再考慮默認無參的構造方法。相當於起一個特指作用。
編程新說注:這個特指作用的用法和上面那個收窄作用的用法不能混合使用,只能二選一,否則報錯。
這就是@Autowired註解對構造方法的作用。
和bean後處理器的結合
構造方法的處理邏輯是在determineCandidateConstructors這個方法里調用的,目的是給我們一個決定構造方法的機會,如果決定不出來也不要緊,Spring還會自己決定。
剩下的就和之前的一樣了,在postProcessMergedBeanDefinition方法里準備好和依賴注入相關的元數據。
在postProcessProperties方法里,根據元數據從容器中解析出依賴並完成注入動作。
>>> 品Spring系列文章 <<<
品Spring:SpringBoot和Spring到底有沒有本質的不同?
品Spring:SpringBoot輕鬆取勝bean定義註冊的“第一階段”
品Spring:SpringBoot發起bean定義註冊的“二次攻堅戰”
品Spring:註解之王@Configuration和它的一眾“小弟們”
品Spring:對@PostConstruct和@PreDestroy註解的處理方法
>>> 熱門文章集錦 <<<
爸爸又給Spring MVC生了個弟弟叫Spring WebFlux
【面試】吃透了這些Redis知識點,面試官一定覺得你很NB(乾貨 | 建議珍藏)
【面試】如果你這樣回答“什麼是線程安全”,面試官都會對你刮目相看(建議珍藏)
【面試】迄今為止把同步/異步/阻塞/非阻塞/BIO/NIO/AIO講的這麼清楚的好文章(快快珍藏)
【面試】一篇文章幫你徹底搞清楚“I/O多路復用”和“異步I/O”的前世今生(深度好文,建議珍藏)
作者是工作超過10年的碼農,現在任架構師。喜歡研究技術,崇尚簡單快樂。追求以通俗易懂的語言解說技術,希望所有的讀者都能看懂並記住。下面是公眾號和知識星球的二維碼,歡迎關注!