天天看點

Spring知識整理(七)—— IoC容器擴充(下)

Bean的生命周期

IoC容器的啟動分為兩個階段,容器啟動階段和Bean執行個體化階段。容器啟動階段上一篇已經介紹過了,下面說下Bean執行個體化階段。對于BeanFactory,對象預設是延遲加載的,而ApplicationContext啟動之後會執行個體化所有的bean定義,隻不過ApplicationContext在啟動後會調用注冊到該容器的所有bean的getBean()方法。不妨根據Bean的生命周期做下了解:

Spring知識整理(七)—— IoC容器擴充(下)

1.       執行個體化bean對象

容器内部采用“政策模式(Strategy Pattern)”來決定采用何種方式初始化bean執行個體。通常,可以通過反射或者CGLIB動态位元組碼生成來初始化相應的bean執行個體或者動态生成其子類。InstantiationStrategy是執行個體化政策的抽象接口,它有以下兩個子類:

SimpleInstantiationStrategy:通過反射執行個體化,不支援注入。

CglibSubclassingInstantiationStrategy:繼承于SimpleInstantiationStrategy,反射執行個體化,并通過CGLIB的動态位元組碼生成功能,可以動态生成某個類的子類,進而實作了方法注入。

容器根據bean定義的BeanDefintion取得執行個體化資訊,結合CglibSubclassingInstantiationStrategy以及bean定義的類型,傳回執行個體化完成的以BeanWrapper進行包裹的對象執行個體。

2.       設定對象屬性

BeanWrapper繼承于PropertyAccessor接口有一實作類BeanWrapperImpl,功能包括設定或者擷取bean的相應屬性值。同時又直接或者間接繼承了PropertyEditorRegistry和TypeConverter接口。在介紹CustomEditorConfigurer時,我們提到了PropertyEditor,而注冊到容器的PropertyEditor就是供BeanWrapper使用的。

在第一步構造完成對象之後,Spring會根據對象執行個體構造一個BeanWrapperImpl執行個體,然後将之前CustomEditorConfigurer注冊的PropertyEditor複制一份給BeanWrapperImpl執行個體(這就是BeanWrapper同時又是PropertyEditorRegistry的原因)。這樣BeanWrapper就可以根據給出的PropertyEditor來轉換類型、設定對象屬性值。

3.       檢查Aware接口并設定依賴

當對象屬性和依賴設定完成之後,Spring容器會檢查目前對象執行個體是否實作了一系列的以Aware命名結尾的接口定義。如果是,則将這些Aware接口定義中規定的依賴注入到目前對象。

Spring定義了如下幾個Aware接口:

BeanNameAware:将beanName設定到目前對象執行個體。

BeanClassLoaderAware:将對應加載目前bean的Classloader注入目前對象執行個體。

BeanFactoryAware:BeanFactory容器會将自身設定到目前對象執行個體。

(以下為ApplicationContext容器會進一步檢查的接口,都是将ApplicationContext自身注入到目前對象執行個體)

ResourceLoaderAware

ApplicationEventPublisherAware

MessageSourceAware

ApplicationContextAware

4.       BeanPostProcessor

在容器的啟動階段,我們提到過BeanFactoryPostProcessor的概念。BeanPostProcessor容易與BeanFactoryPostProcessor混淆,隻要記住這兩個概念分别存在于不同階段就能很好的區分。

BeanPostProcessor接口定義了兩個方法,分别是postProcessBeforeInitialization()和postProcessAfterInitialization(),這兩個方法分别對應BeanPostProcessor的前置處理和後置處理階段,兩個方法的參數都是bean的執行個體和beanName,這樣我們就可以對對象進行任何操作了。

通常比較常見的使用BeanPostProcessor的場景,是處理标記接口實作類,或者為目前對象提供代理實作。ApplicationContext容器定義的Aware接口的處理和Spring中注解的處理就是由BeanPostProcessor完成的。我們可以看下處理Aware的相關代碼: 

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    if (bean instanceof ResourceLoaderAware) { 
        ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
    }
    if (bean instanceof ApplicationEventPublisherAware) {
        ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher (this.applicationContext);
    } 
    if (bean instanceof MessageSourceAware) { 
        ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
    }
    if (bean instanceof ApplicationContextAware) { 
        ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
    }
    return bean; 
}
           

Spring的AOP同樣使用BeanPostProcessor來為對象生成相應的代理對象,如BeanNameAutoProxyCreator等,這個會在介紹AOP的時候詳細說明。

當然,我們可以定義自己的BeanPostProcessor,做法就是實作BeanPostProcessor接口,然後将自定義的BeanPostProcessor注冊到容器中:

ConfigurableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(...));

beanFactory.addBeanPostProcessor(new PasswordDecodePostProcessor());

對于于ApplicationContext,會自動識别并加載,是以隻要在配置檔案中配置下即可:

<bean id="passwordDecodePostProcessor" class="package.name.PasswordDecodePostProcessor">

<!--如果需要,注入必要的依賴-->

</bean>

1.       InitializingBean和init-method

InitializingBean是容器内部廣泛使用的一個對象生

命周期辨別接口,其定義如下:

public interface InitializingBean {

void afterPropertiesSet() throws Exception;

}

該接口定義很簡單,其作用在于,在調用過“BeanPostProcessor的前置處理”之後,會接着檢測目前對象是否實作了InitializingBean接口,如果是,則會調用其afterPropertiesSet()方法進一步調整對象的狀态。比如,在有些情況下,某個業務對象執行個體化完成後,還不能處于可以使用狀态。這個時候就可以讓該業務對象實作該接口,并在方法afterPropertiesSet()中完成對該業務對象的後續處理。

雖然該接口在Spring容器内部廣泛使用,但如果真的讓我們的業務對象實作這個接口,則顯得Spring容器比較具有侵入性。是以,Spring還提供了另一種方式,在XML配置的時候,使用<bean>的init-method屬性:

<bean id="myCar" class=" com.zrabbit.production.Car" init-method="initCar">

至此,容器使用前的初始化工作已經全部完成。

2.       DisposableBean與destroy-method

當一個singleton類型的bean執行個體使用後,容器将檢查其是否實作了DisposableBean接口。或者是否通過<bean>的destroy-method屬性指定了自定義的對象銷毀方法。如果是,就會為該執行個體注冊一個用于對象銷毀的回調(Callback),以便在這些singleton類型的對象執行個體銷毀之前,執行銷毀邏輯。

與InitializingBean和init-method用于對象的自定義初始化相對應,DisposableBean和destroy-method為對象提供了執行自定義銷毀邏輯的機會:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

不過,這些自定義的銷毀邏輯,在對象執行個體初始化完成并注冊了相關的回調方法之後,并不會馬上執行。回調方法注冊後,傳回的對象執行個體即處于使用狀态,隻有該對象執行個體不再被使用的時候,才會執行相關的自定義銷毀邏輯,此時通常也就是Spring容器關閉的時候。但Spring容器在關閉之前,不會聰明到自動調用這些回調方法。是以,需要我們告知容器,在哪個時間點來執行對象的自定義銷毀方法。

對于BeanFactory容器來說,我們需要調用ConfigurableBeanFactory提供的destroySingletons()方法銷毀容器中管理的所有singleton類型的對象執行個體。對于ApplicationContext容器來說,我們可以調用AbstractApplicationContext提供的registerShutdownHook()方法。

public static void main(String[] args) {
    BasicConfigurator.configure();
    BeanFactory container = new XmlBeanFactory(new ClassPathResource("..."));
    BusinessObject bean = (BusinessObject)container.getBean("...");
    bean.doSth();
    ((ConfigurableListableBeanFactory)container).destroySingletons();
    // 應用程式退出,容器關閉
}
           
public static void main(String[] args) {
    BasicConfigurator.configure();
    BeanFactory container = new ClassPathXmlApplicationContext("..."); 
    ((AbstractApplicationContext)container).registerShutdownHook();
    BusinessObject bean = (BusinessObject)container.getBean("...");
    bean.doSth();
    // 應用程式退出,容器關閉
}