Spring依賴注入(IOC)那些事

     小菜使用Spring有幾個月了,可是對於它的內部原理,倒是一頭霧水,此次藉着工做中遇到的一個小問題,來總結一下Spring。html

     Spring依賴注入的思想,就是把對象交由Spring容器管理,使用者只需聲明何時須要對象 ,這個能夠說是常識,在這就很少說啦。java

     小菜的項目中,爲了提升代碼運行效率,須要在類實例化的時候初始化一個列表,避免重複查詢,因而小菜想固然的寫了以下代碼:spring

 1 @Component
 2 public class ApplyStatusHandler{
 3   @Autowired
 4   private DictMgr dictMgr;
 5   @Autowired
 6   private ApplyMgr applyMgr;
 7     
 8   public ApplyStatusHandler(){
 9       //這裏初始化列表,使用了dictMgr、applyMgr
10   }
11 }

 

     但實際測時,發現列表是空的。。。小菜剛開始還覺得是構造方法沒有執行,但經過異常捕獲發現原來是出現了空指針。app

     接下來分析一下爲啥會出現空指針。函數

     @Component註解,意思大體就是告訴Spring,要把ApplyStatusHandler類的對象放到容器裏,之後能夠方便的使用@Autowired進行注入。this

     @Autowired註解,有如下兩個重要特色:spa

        

          +能夠對成員變量方法構造函數進行標註,來完成自動注入.net

          +根據類型進行自動注入的,若是spring配置文件中存在多個相同類型的bean時,或者不存在指定類型的bean,都會拋出異常。代理

 

     其中,對成員變量的註解,就如上例所示,能夠直接從Spring容器中拿到此類型的對象,注入到成員變量中。指針

     對方法的註解,小菜的理解就是對方法的參數進行初始化。例如:

1 @Autowired
2 public void initXXXX(DictMgr dictMgr){
3   //這裏能夠拿到DictMgr類的對象dictMgr
4 }

 

     此方法由於有@Autowired標識,因此Spring會自動執行此方法,而且在執行的時候,去本身的容器找尋找和該方法參數類型一致的對象,進行注入,這樣在方法中就能夠拿到須要的對象了,其實和成員變量的註解大同小異,只不過把變量換了一個地方而已。

     對於以上兩種方法,有一個必要的前提:對象必須是存在的(ApplyStatusHandler類的對象)!

     很容易理解,不管是對成員變量的注入,仍是對方法參數的注入,都必須保證變量所在的對象是存在的,不然無從注入。

     到這,讀者應該能明白爲何會出現空指針,由於Spring先調用的構造方法,此時尚未進行注入。

     幸虧還有構造方法注入(和方法注入同樣的道理),既然是構造方法注入,那麼在Spring調用構造方法時,應該就能夠拿到對象,而後再使用,就不會出現空指針,因而小菜把代碼改爲以下形式:

 1 @Component
 2 public class ApplyStatusHandler{
 3   
 4   private DictMgr dictMgr;
 5   private ApplyMgr applyMgr;
 6     
 7   @Autowired
 8   public ApplyStatusHandler(DictMgr dictMgr,ApplyMgr applyMgr){
 9       this.dictMgr=dictMgr;
10       this.applyMgr=applyMgr;
11     
12       //這裏初始化列表,使用了dictMgr、applyMgr
13   }
14     
15 }

 

     小菜滿懷信心的啓動項目,的確是沒報空指針異常,但卻報了不少Spring內部的異常。。。

     通過一番搜索,原來是因爲小菜聲明瞭一個帶參數的構造方法,致使默認的無參數構造方法被抹掉,而這種狀況下Spring實例化ApplyStatusHandler類,必需要有無參數的構造方法,所以加上便可(方法中能夠什麼也不作,但必需要有):

1 public ApplyStatusHandler(){}

 

     這下再啓動項目,完美運行,說明對象已經成功注入到了構造方法中。

     若是咱們不繼續思考,事情可能就到此結束了,可是:既然這個無參構造方法是必須的,就說明Spring必然要調用這個方法,但調用了無參的構造方法,小菜寫的有參構造方法是怎麼調用的呢?總不會同時調用兩個吧?

     其實,這和Spring底層的實例化方式有關。

     讀者可能很是瞭解什麼依賴注入,交由Spring容器管理,但底層到底是怎麼實現的呢?

     據小菜不徹底瞭解,應該是有兩種實現方式:JDK動態代理和Cglib動態代理。

     JDK動態代理,須要實現InvocationHandler 接口,也就是說若是想使用這種代理方式建立對象,須要讓類先實現InvocationHandler 接口才行,最終建立的對象是一個新類的對象。

     Cglib動態代理,採用的是繼承方式,它會在底層建立一個類,來繼承原有的類,可是這個子類全部的方法都是直接調用父類去實現,至關於父類的一個代理、封裝(封裝的目的是支持事務處理),實際上咱們在程序中使用的是這個子類的對象,並非ApplyStatusHandler的對象。

     經過這兩種代理方式,才讓Spring能夠支持事務、管理對象。

     本例中,小菜的這個類並無實現InvocationHandler 接口,也就是說,不會使用JDK動態代理,而是使用Cglib動態代理來實例化對象,所以Spring會建立一個類來繼承ApplyStatusHandler,而後根據ApplyStatusHandler類的構造方法實例化ApplyStatusHandler,再把子類實例化,讓子類持有這個父類的引用,最終注入到變量中的是子類。

     由此能夠看出,咱們經過在構造方法上使用@Autowired注入對象是正確的,ApplyStatusHandler類能成功實例化,但因爲有子類須要繼承ApplyStatusHandler,所以ApplyStatusHandler中必須有一個空的構造方法,不然子類是沒法實例化的(java基礎。。。)。

     總之,ApplyStatusHandler類中的無參構造方法,是用來實例化Cglib生成的代理子類;有參構造方法是爲了完成注入。

     好啦,小菜的分享到此結束~~

     水平有限,高手勿噴

 

     爲了方便讀者研究,小菜貼出一些連接供讀者參考:

 

          +經過CGLIB實現AOP的淺析

          +java 動態代理proxy VS cglib的動態代理的區別

          +Spring註解注入

          +能不能在spring中首先用構造函數方式注入,而後再用setter注入

相關文章
相關標籤/搜索