Spring核心——Bean的定義與控制

在前面兩篇介紹Sring核心與設計模式的文章中,分別介紹了Ioc容器Bean的依賴關係。若是閱讀過前2文就會知道,Spring的整個運起色制就是圍繞着IoC容器以及Bean展開的。IoC就是一個籃子,全部的Bean都向裏面扔。除了提供籃子功能建立並存放Bean以外,IoC還要負責管理Bean與Bean之間的關係——依賴注入。以前也提到Bean是Spring核心容器的最小工做單元,Spring一些更高級的功能(例如切面、代理)都是在Bean的基礎上實現。html

除了管理Bean與Bean之間的關係,IoC還提供了對Bean自身進行控制的各項功能,本文將介紹Bean的生命週期功能以及狀態定義功能。java

前置依賴

Bean與Bean之間存在依賴關係,能夠是強依賴(經過XML和註解直接聲明依賴)、也能夠是弱依賴(ApplicationContextAware等方式獲取)。當一個Bean須要另一個Bean完成初始化後自身才能工做時,例如一個Bean依賴DataSoruce,可是DataSource的初始化須要較長時間。這個時候用depends-on聲明前置依賴便可:spring

<!-- 依賴多個Bean使用,號分割 -->
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

延遲加載

一般狀況下,全部的 singleton 類型的Bean都會在容器建立後進行初始化,簡單的說就是啓動Jvm就開始建立(其實是建立ApplicationContext的某個實現類實例以後)。編程

IoC支持全部的 singleton Bean在使用時再加載,這樣作的好處是能夠大大節省初始化的時間。可是若是你的應用對啓動時間的長短並不敏感,建議讓全部的 singleton 都啓動時加載。這樣能夠在啓動時就發現一些問題,而不是在運行好久直到使用時才由用戶去觸發這個問題。或者能夠根據場景來使用決定是否延遲,例如開發時使用延遲加載,而在集成測試或上生產時關閉。設計模式

能夠設置全局延遲加載,也能夠設置某個Bean延遲加載:框架

<beans default-lazy-init="true">
    <!-- 全部的Bean知道使用的時候纔會進行加載... -->
</beans>
<!-- 只有lazy類延遲加載 -->
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.foo.AnotherBean"/>

須要注意的是,在設置某個單獨的Bean延遲加載時,若是有某個沒有延遲加載的Bean要依賴他,那實際上也會在初始化的時候就加載。測試

還要強調一下,這裏的「加載」僅僅是爲了表示一個類被Ioc創造並放置容器中,和classLoad方法將class文件中的字節碼加載到方法區的加載是兩個概念。ui

延遲加載在設計模式上是單例模式一種延伸,一般也被稱爲懶漢模式。單例一般有雙重鎖+volatile、靜態類和枚舉三種方式實現。在Effective Java一書中對三種模式都有深刻的解析。而對於Spring容器而言,枚舉的方式確定很差用了,靜態類因爲屬於自身代碼級別應該也不會用,因此雙重鎖的實現方式較爲可信。不過我沒去看過源碼,僅屬於猜想。url

生命週期方法

一個Bean的建立、使用再到最後銷燬稱爲"Bean的生命週期"。Spring框架爲Bean的生命週期各個階段提供了多種回掉方法來處理各類狀態或者數據。spa

初始化方法

當一個Bean完成初始化並注入各項參數以後,初始化回掉方法會被調用,簡單的說就是完成建立以後會被調用。實現初始化回調方法有2個路徑:1.繼承org.springframework.beans.factory.InitializingBean接口,而後實現 afterPropertiesSet方法。2.在Bean的XML配置上使用init-method屬性來制定要調用的初始化:

繼承實現:

<bean id="a" class="x.y.A" />
package x.y;
public class A implements InitializingBean {
    public void afterPropertiesSet(){
        // init
    }
}

配置實現:

<bean id="a" class="x.y.A" init-method="init" />
package x.y;
public class A {
    public void init(){}
}

2種方法都等效,實際使用是咱們應該使用哪種方法呢?

InitializingBean是Spring早期實現的一個生命週期回調方法。可是在JCP推出JSR-250和JSR-330規範以後,Spring的大神們開始意識到基於元編程思想和配置手段來實現非侵入式框架(Not Coupled)纔是正道。因此如今都是推薦使用配置文件和JSR-250的@PostConstruct(關於各類Annotation的使用請關注後續的文章)。如今依然保留InitializingBean應該是考慮到兼容問題。

銷燬方法

與建立方法相對應的是銷燬方法。當一個類將要被銷燬以前,對應的銷燬回調方法會被調用。銷燬方法也有一個繼承實現和配置+註解實現:

繼承實現:

<bean id="a" class="x.y.A" />
package x.y;
public class A implements DisposableBean {
    public void destroy(){
        // 銷燬資源
    }
}

配置實現:

<bean id="a" class="x.y.A" destroy-method="cleanUp" />
package x.y;
public class A {
    public void cleanUp(){
        // 銷燬資源
    }
}

依然建議銷燬手段也使用配置或@PreDestroy來設定銷燬方法。

全局配置初始化與銷燬方法

IoC容器還提供了全局配置初始化與銷燬方法的配置:

package x.y;
public class A {
    public void init(){
        // 初始化資源
    }
    public void destroy(){
        // 銷燬資源
    }
}
<beans default-init-method="init" default-destroy-method="destroy">
     <bean id="a" class="x.y.A"/>
     <!-- bean configuration -->
</beans>

經過在<beans>標籤上使用default-init-methoddefault-destroy-method 屬性參數,能夠爲容器中全部的Bean統一指定初始化和銷燬的生命週期方法。

若是在<beans>上設定2個默認的生命週期方法,同時在<bean>上也指定了init-methoddestroy-method,回調方法會以<bean>上的配置爲準。這樣就保證全局配置與單獨配置能夠共存。

使用初始化或銷燬2個生命週期方法注意的要點:

  1. 初始化和銷燬都提供了3種手段:XML配置、註解、以及實現接口。系統的各個部分會交由不一樣的團隊開發,不遵循統一的規範,建議使用知足JSR規範的註解——@PostConstruct、@PreDestroy。若是是統一的團隊,準訓一致的規範,建議使用<beans>的屬性統一名稱使用全局配置。
  2. 若是Bean設計到代理模式時(例如使用了AOP),那麼生命週期方法被調用時,有可能代理類尚未被建立出來。由於生命週期方法是實體類完成對應工做以後就會被調用,而與代理類無關。
相關文章
相關標籤/搜索