天天看點

Spring中管理Bean依賴注入之後和Bean銷毀之前的行為

    對于singleton作用域的bean,spring容器将會跟蹤它們的生命周期,容器知道何時執行個體化結束、何時銷毀。spring可以管理bean在執行個體化結束之後和bean銷毀之前的行為。

bean依賴關系注入之後的行為:

    spring提供了兩種方式在bean全部屬性設定成功後執行特定的行為:

在spring配置檔案中使用init-method屬性:這個屬性指定某個方法在bean全部依賴關系設定結束後自動執行。這個方法寫在bean裡面。使用這種方法不需要将代碼與spring耦合在一起,代碼污染小,推薦使用。

讓bean實作initializingbean接口:該接口提供了一個afterpropertiesset() throwsexception方法,在bean裡面實作它。

    spring容器會在為該bean注入依賴關系後,調用該bean實作的afterpropertiesset方法。先看例子:

<a href="http://my.oschina.net/itblog/blog/205816#">?</a>

1

2

3

<code>public</code> <code>interface</code> <code>animal {</code>

<code>    </code><code>public</code> <code>void</code> <code>eatfood();</code>

<code>}</code>

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

<code>public</code> <code>class</code> <code>dog </code><code>implements</code> <code>animal, initializingbean {</code>

<code>    </code><code>private</code> <code>food food;</code>

<code>    </code><code>public</code> <code>dog() {</code>

<code>        </code><code>system.out.println(</code><code>"spring執行個體化主調bean...dog執行個體"</code><code>);</code>

<code>    </code><code>}</code>

<code>    </code><code>public</code> <code>void</code> <code>setfood(food food) {</code>

<code>        </code><code>system.out.println(</code><code>"spring執行依賴關系注入..."</code><code>);</code>

<code>        </code><code>this</code><code>.food = food;</code>

<code>    </code> 

<code>    </code><code>//animal的方法</code>

<code>    </code><code>@override</code>

<code>    </code><code>public</code> <code>void</code> <code>eatfood() {</code>

<code>        </code><code>system.out.println(food.getname() + </code><code>"真好吃"</code><code>);</code>

<code>    </code><code>//自定義的用于在spring中配置的當bean初始化完成時調用的方法</code>

<code>    </code><code>public</code> <code>void</code> <code>init() {</code>

<code>        </code><code>system.out.println(</code><code>"正在執行初始化:init方法..."</code><code>);</code>

<code>    </code><code>//實作initializingbean接口中的方法</code>

<code>    </code><code>public</code> <code>void</code> <code>afterpropertiesset() </code><code>throws</code> <code>exception {</code>

<code>        </code><code>system.out.println(</code><code>"正在執行初始化:afterpropertiesset方法..."</code><code>);</code>

<code>public</code> <code>class</code> <code>food {</code>

<code>    </code><code>private</code> <code>string name;</code>

<code>    </code><code>public</code> <code>void</code> <code>setname(string name) {</code>

<code>        </code><code>this</code><code>.name = name;</code>

<code>    </code><code>public</code> <code>string getname() {</code>

<code>        </code><code>return</code> <code>name;</code>

<code>    </code><code>public</code> <code>food() {</code>

<code>        </code><code>system.out.println(</code><code>"spring執行個體化依賴bean..."</code><code>);</code>

    上面的程式中定義了一個普通的init方法,實際上這個方法名是任意的,并不一定叫init,spring也不會對這個init方法進行任何特别的處理。隻是接下來會在spring配置檔案中使用init-method屬性指定該方法是一個“生命周期”方法。

    增加init-method="init" 來指定init方法應在bean的全部屬性設定結束後自動執行,如果它不實作initializingbean接口,上面的dog類沒有實作任何spring接口,隻是增加了一個普通的init方法。它依然是一個普通的java檔案,代碼沒有污染。下面是spring配置檔案:

<code>&lt;!-- 使用init-method="init"來指定bean的全部屬性設定結束後執行的方法 --&gt;</code>

<code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"dog"</code> <code>class</code><code>=</code><code>"com.abc.dog"</code> <code>init-method</code><code>=</code><code>"init"</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"food"</code> <code>ref</code><code>=</code><code>"food"</code> <code>/&gt;</code>

<code>&lt;/</code><code>bean</code><code>&gt;</code>

<code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"food"</code> <code>class</code><code>=</code><code>"com.abc.food"</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"name"</code> <code>value</code><code>=</code><code>"骨頭"</code> <code>/&gt;</code>

    使用主程式擷取、并調用dog類的eatfood方法:

<code>public</code> <code>class</code> <code>test {</code>

<code>    </code><code>public</code> <code>static</code> <code>void</code> <code>main(string args[]) {</code>

<code>        </code><code>applicationcontext context = </code><code>new</code> <code>classpathxmlapplicationcontext(</code><code>"applicationcontext.xml"</code><code>);</code>

<code>        </code><code>dog d = context.getbean(</code><code>"dog"</code><code>, dog.</code><code>class</code><code>);</code>

<code>        </code><code>d.eatfood();</code>

    輸出結果:

<code>spring執行個體化依賴bean...food執行個體</code>

<code>spring執行個體化主調bean...dog執行個體</code>

<code>spring執行依賴關系注入...</code>

<code>正在執行初始化:afterpropertiesset方法...</code>

<code>正在執行初始化:init方法...</code>

<code>骨頭真好吃</code>

    通過上面的例子可以看出:當spring将food注入到dog之後——也就是完成依賴之後,程式先調用afterpropertiesset方法進行初始化,在調用init-method中指定的方法進行初始化。

    對于實作initializingbean接口的bean,無需使用init-method屬性來指定初始化方法,配置該bean執行個體與普通bean執行個體完全一樣,spring容器會自動檢測bean執行個體是否實作了特定生命周期接口,并決定是否需要執行生命周期方法。spring在為bean完成注入所有依賴關系後,會自動調用該bean實作的afterproperties方法。但initializingbean接口污染了代碼,是侵入式設計,是以不推薦使用。

    另外,從上面的執行結果可以看出,如果既實作了initializingbean接口又使用了init-method來指定初始化方法,那麼兩個初始化方法都會被執行,先執行接口中的afterproperties方法,再執行自定義的初始化方法。

bean銷毀之前的行為:

    與定制初始化行為相似,spring也提供了兩種方式定制在bean銷毀之前的特定行為:

使用destroy-method屬性:指定某個方法在bean銷毀之前被自動執行。使用這種方法,不需要将代碼與spring的接口耦合在一起,代碼污染小,推薦使用。

實作disposablebean接口:該接口提供了一個destroy() throws exception的方法。在bean裡面實作它,這個方法将在bean銷毀之前被spring調用。

    例子與前文相似,這裡不贅述。

    singleton作用域的bean通常會随着容器的關閉而銷毀,但問題是:applicationcontext容器在什麼時候關閉呢?在基于web的applicationcontext實作中,系統已經提供了相應的代碼保證關閉web應用時恰當的關閉spring容器。但對于一個非web應用的環境下,為了讓spring容器優雅的關閉,并自動調用singleton上的相應回調方法,則需要在jvm裡面注冊一個關閉鈎子(shutdown hook),這樣就可以保證spring容器被恰當關閉,并自動執行singleton的bean裡面的相應回調方法。例如:

<code>        </code><code>//為spring容器注冊關閉鈎子</code>

<code>        </code><code>context.registershutdownhook();</code>

    除此之外,如果spring容器中很多bean都需要指定特定的生命周期行為,則可以考慮使用&lt;beans&gt;的default-init-method屬性和default-destroy-method屬性,這兩個屬性的作用類似于&lt;bean&gt;的init-method和destroy-method屬性的作用。但由于前兩個屬性是&lt;beans&gt;标簽的,是以對标簽中的所有bean都有效。即:如果&lt;beans&gt;标簽中的bean配置了default-init-method="init",那麼如果&lt;beans&gt;标簽中的bean配置了init方法,則該方法會被自動調用。

    下圖顯示了spring容器中bean執行個體完整的生命周期行為:

Spring中管理Bean依賴注入之後和Bean銷毀之前的行為

    需要指出的是,當bean實作了applicationaware、beannameaware接口之後,spring容器會在該bean初始化完成之後——也就是init-method屬性指定的方法(如果有)之後,再來回調setapplicationcontext(applicationcontext context)和setbeanname(string beanname)方法。