Spring Bean 的生命週期

C_nullptr發表於2021-03-03

目錄

一、BeanFactory例項化Bean相關介面

二、BeanFactory的bean生命週期相關程式碼

三、關於BeanFactory的Bean生命週期介面的總結

四、ApplicationContext的Bean生命週期

五、ApplicationContext的Bean生命週期程式碼演示

六、總結


Spring的生命週期是指例項化Bean時所經歷的一系列階段,即通過getBean()獲取bean物件及設定物件屬性時,Spring框架做了哪些事。Bean的生命週期從Spring容器例項化Bean到銷燬Bean。
本文分別對 BeanFactory 和 ApplicationContext 中的生命週期進行分析。

一、BeanFactory例項化Bean相關介面

Bean級生命週期介面:(4個)

1、BeanNameAware

    //待物件例項化並設定屬性之後呼叫該方法設定BeanName
    void setBeanName(String beanName);

2、BeanFactoryAware

    //待呼叫setBeanName之後呼叫該方法設定BeanFactory,BeanFactory物件預設實現類是DefaultListableBeanFactory
    void setBeanFactory(BeanFactory var1) throws BeansException;

3、InitializingBean

    //例項化完成之後呼叫(呼叫了BeanPostProcessor.postProcessBeforeInitialization方法之後呼叫該方法)
    void afterPropertiesSet() throws Exception;

4、DisposableBean

    //關閉容器時呼叫
    void destroy() throws Exception;

這4個介面都在包 org.springframework.beans.factory 下,它們是Bean級生命週期介面,這些介面由Bean類直接實現。

容器級Bean生命週期介面:(2個)

1、抽象類:InstantiationAwareBeanPostProcessorAdapter

例項化前/後,及框架設定Bean屬性時呼叫該介面。可覆蓋的常用方法有:

    //在Bean物件例項化前呼叫
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;

    //在Bean物件例項化後呼叫(如呼叫構造器之後呼叫)
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;

    /**
     * 在設定某個屬性前呼叫,然後再呼叫設定屬性的方法
     * 注意:這裡的設定屬性是指通過配置設定屬性,直接呼叫物件的setXX方法不會呼叫該方法,如bean配置中配置了屬性address/age屬性,將會呼叫該方法
     * @param pvs 如 PropertyValues: length=2; bean property 'address'; bean property 'age'
     */
    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException;

2、介面BeanPostProcessor

例項化完成之後呼叫該介面。可實現的介面方法有:

    //例項化完成,setBeanName/setBeanFactory完成之後呼叫該方法
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException;

    //全部是例項化完成以後呼叫該方法
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException;

這兩個介面都在包 org.springframework.beans.factory.config 下,一般稱它們的實現類為“後處理器”。後處理器介面一般不由Bean本身實現,實現類以容器附加裝置的形式註冊到Spring容器中。
當Sprig容器建立任何Bean的時候,這兩個後處理器都會發生作用,所以這兩個後處理器的影響是全域性的。使用者可以通過合理的程式碼控制後處理器只對固定的Bean建立進行處理。
Bean級生命週期介面解決Bean個性化處理的問題,Bean容器級生命週期介面解決容器中某些Bean共性化處理的問題。

二、BeanFactory的bean生命週期相關程式碼

1、xml bena 配置

  <bean id="person" class="demo02.bean.Person"
          p:address="上海市"
          p:age="25"
          init-method="myInit"
          destroy-method="myDestroy"
  />

p:address/p:age 在例項化的時候會呼叫Bean的對應setXX方法設定屬性。

2、java相關程式碼

InstantiationAwareBeanPostProcessorAdapter實現類相關程式碼

/**
 * 例項化前/後,及框架設定Bean屬性時呼叫該介面
 */
public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {

    //在Bean物件例項化前呼叫
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        //僅對容器中的person bean處理
        if ("person".equals(beanName)) {
            System.out.println("例項化前呼叫:InstantiationAwareBeanPostProcessorAdapter.postProcessBeforeInstantiation");
        }
        return null;
    }

    //在Bean物件例項化後呼叫(如呼叫構造器之後呼叫)
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        //僅對容器中的person bean處理
        if ("person".equals(beanName)) {
            System.out.println("例項化後呼叫:InstantiationAwareBeanPostProcessorAdapter.postProcessAfterInstantiation");
        }
        return true;
    }

    /**
     * 在設定某個屬性前呼叫,然後再呼叫設定屬性的方法
     * 注意:這裡的設定屬性是指通過配置設定屬性,直接呼叫物件的setXX方法不會呼叫該方法,如bean配置中配置了屬性address/age屬性,將會呼叫該方法
     * @param pvs 如 PropertyValues: length=2; bean property 'address'; bean property 'age'
     */
    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        //僅對容器中的person bean處理
        if ("person".equals(beanName)) {
            System.out.println("在Bean設定屬性時呼叫:InstantiationAwareBeanPostProcessorAdapter.postProcessPropertyValues,設定值為" + pvs);
        }
        return pvs;
    }

}

BeanPostProcessor實現類相關程式碼

/**
 * 例項化完成之後呼叫該介面
 */
public class MyBeanPostProcessor implements BeanPostProcessor {

    //例項化完成,setBeanName/setBeanFactory完成之後呼叫該方法
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        if ("person".equals(s)) {
            Person person = (Person) o;
            if (person.getName() == null) {
                System.out.println("呼叫BeanPostProcessor.postProcessBeforeInitialization,name為空,設定預設名為無名氏");
                person.setName("無名氏");
            }
        }
        return o;
    }

    //全部是例項化完成以後呼叫該方法
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
        if ("person".equals(s)) {
            Person person = (Person) o;
            if (person.getAge()>20) {
                System.out.println("呼叫BeanPostProcessor.postProcessAfterInitialization,age大於20,調整為20");
                person.setAge(20);
            }
        }
        return o;
    }

}

BeanFactoryLifeCycleMain 容器裝載配置檔案並啟動

/**
 * BeanFactory中的Bean的生命週期
 * 容器裝載配置檔案,註冊 BeanPostProcessor 和 InstantiationAwareBeanPostProcessorAdapter 後置處理器
 */
public class BeanFactoryLifeCycleMain {

    @Test
    public void lifeCycleInBeanFactory() {
        //裝載配置檔案並啟動容器
        Resource resource = new ClassPathResource("beans/beans.xml");
        BeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((DefaultListableBeanFactory) beanFactory);
        reader.loadBeanDefinitions(resource);

        //向容器中註冊後處理器 MyBeanPostProcessor
        ((DefaultListableBeanFactory) beanFactory).addBeanPostProcessor(new MyBeanPostProcessor());

        //向容器中註冊後處理器 MyInstantiationAwareBeanPostProcessor
        //注意:後處理器呼叫順序和註冊順序無關。在處理多個後處理器的情況下,必需通過實現Ordered介面來確定順序
        ((DefaultListableBeanFactory) beanFactory).addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor());

        //第一次從容器中獲取Person,將觸發容器例項化該bean,這將引發Bean生命週期方法的呼叫
        Person person = (Person) beanFactory.getBean("person");
        person.introduce();
        person.setName("zhangsan");

        //第二次從容器中獲取,直接從快取中獲取(預設就是單例)
        Person person2 = (Person) beanFactory.getBean("person");
        System.out.println(person == person2);//true

        //關閉容器
        ((DefaultListableBeanFactory) beanFactory).destroySingletons();
    }

}

執行結果:(DEBUG資訊為Spring框架輸出的,說明了底層呼叫方法和作用)
例項化前呼叫:InstantiationAwareBeanPostProcessorAdapter.postProcessBeforeInstantiation
呼叫了Person的無參構造器
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DefaultListableBeanFactory.(523)doCreateBean] - Eagerly caching bean 'person' to allow for resolving potential circular references
例項化後呼叫:InstantiationAwareBeanPostProcessorAdapter.postProcessAfterInstantiation
在Bean設定屬性時呼叫:InstantiationAwareBeanPostProcessorAdapter.postProcessPropertyValues,設定值為PropertyValues: length=2; bean property 'address'; bean property 'age'
呼叫了setAddress方法設定了屬性
呼叫了setAge方法設定了屬性
呼叫了BeanNameAware.setBeanName, value is person
呼叫了BeanFactoryAware.setBeanFactory, value is org.springframework.beans.factory.support.DefaultListableBeanFactory@44c8afef: defining beans [car,person]; root of factory hierarchy
呼叫BeanPostProcessor.postProcessBeforeInitialization,name為空,設定預設名為無名氏
呼叫了setName方法設定了屬性
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DefaultListableBeanFactory.(1595)invokeInitMethods] - Invoking afterPropertiesSet() on bean with name 'person'
呼叫InitializingBean.afterPropertiesSet
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DefaultListableBeanFactory.(1653)invokeCustomInitMethod] - Invoking init method  'myInit' on bean with name 'person'
呼叫bean配置的init-method
呼叫BeanPostProcessor.postProcessAfterInitialization,age大於20,調整為20
呼叫了setAge方法設定了屬性
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DefaultListableBeanFactory.(477)createBean] - Finished creating instance of bean 'person'
呼叫了introduce方法--> name:無名氏,age:20,address:中國
呼叫了setName方法設定了屬性
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DefaultListableBeanFactory.(249)doGetBean] - Returning cached instance of singleton bean 'person'
true
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DefaultListableBeanFactory.(474)destroySingletons] - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@44c8afef: defining beans [car,person]; root of factory hierarchy
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DisposableBeanAdapter.(244)destroy] - Invoking destroy() on bean with name 'person'
呼叫DisposableBean.destroy
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DisposableBeanAdapter.(322)invokeCustomDestroyMethod] - Invoking destroy method 'myDestroy' on bean with name 'person'
呼叫bean配置的destroy-method

BeanFactory的Bean生命週期執行步驟如下:

三、關於BeanFactory的Bean生命週期介面的總結

1、通過實現BeanFactory的Bean生命週期相關介面,雖然讓Bean具有了更詳細的生命週期階段,但是也讓Spring框架和Bean緊密聯絡在了一起,同時也增加了程式碼複雜度。因此,如果使用者希望在Bean的生命週期中實現自己的業務,不需要和特定框架關聯,可以通過 bean 的 init-method 和 destroy-method 配置方法來進行業務實現。
2、Spring還擁有一個Bean後置處理器InitDestroyAnnotationBeanPostProcessor,它負責對標註了 @PostConstruct 和 @PreDestroy 的 Bean 進行處理,在 Bean 初始化及銷燬前執行相應的業務邏輯。
3、通常情況下,可以拋棄Bean級的生命週期的4個介面,用更加方便的方法進行代替(如1、2點中的方法)。但是容器級Bean生命週期介面可以合理的使用,處理一些共同的業務。
4、當bean的Scope為非單例,物件銷燬的時候,Spring容器不會幫我們呼叫任何方法,因為是非單例,這個型別的物件有很多個,Spring容器一旦把這個物件交給你之後,就不再管理這個物件了。如果bean的scope設為prototype時,當容器關閉時,destroy方法不會被呼叫。對於prototype作用域的bean,Spring不能對一個該bean的整個生命週期負責:容器在初始化、配置、裝飾或者是裝配完一個prototype例項後,將它交給客戶端,隨後就對該prototype例項不聞不問了。


四、ApplicationContext的Bean生命週期

1、Bean在應用上下文中的生命週期和在BeanFactory中生命週期類似,可以實現所有的BeanFactory的Bean級和容器級生命週期介面。但是,如果Bean實現了org.springframework.context.ApplicationContextAware介面,會增加一個呼叫該介面方法setApplicationContext()的步驟。
2、如果配置檔案中宣告瞭工廠後處理器介面BeanFactoryPostProcessor的實現類,則應用上下文在裝載配置檔案之後初始化Bean例項之前將呼叫這些BeanFactoryPostProcessor對配置資訊進行加工處理。Spring框架提供了多個工廠後處理器如CustomEditorConfigurer、PopertyPlaceholderConfigurer等。如果配置檔案中定義了多個工廠後處理器,最好讓它們實現org.springframework.core.Ordered介面,以便Spring以確定的順序呼叫它們。工廠後處理器是容器級的,僅在應用上下文初始化時呼叫一次,其目的是完成一些配置檔案的加工處理工作。
注意:BeanFactoryPostProcessor的介面方法引數是BeanFactory,而在BeanFactory級Bean生命週期中提到的BeanPostProcessor介面方法引數是Bean物件和Bean的ID值。所以前者是針對應用上下文的,後者是針對BeanFactory的。
3、ApplicationContext和BeanFactory另一個最大的不同之處在於:前者會利用Java反射機制自動識別出配置檔案中定義的BeanPostProcessor、InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,並自動將它們註冊到應用上下文中;而後者需要在程式碼中通過手工呼叫addBeanPostProcessor()方法進行註冊。這也是為什麼在應用開發時,我們普遍使用ApplicationContext而很少使用BeanFactory的原因之一。
4、在ApplicationContext中,我們只需要在配置檔案中通過<bean>定義工廠後處理器和Bean後處理器,它們就會按預期的方式執行。

五、ApplicationContext的Bean生命週期程式碼演示

1、java程式碼

實現BeanFactoryPostProcessor介面方法

/**
 * ApplicationContext bean生命週期演示
 */
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    //先呼叫本方法,將所有bean生成BeanDefinition物件並設定相關屬性。
    //本方法執行完之後,會呼叫bean構造器,並呼叫set相關屬性方法
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("person");
        beanDefinition.getPropertyValues().addPropertyValue("address", "王二麻子");
        System.out.println("呼叫了BeanFactoryPostProcessor.postProcessBeanFactory");
    }

}

ApplicationContext在啟動時,將首先為配置檔案中每個<bean>生成一個BeanDefinition物件,BeanDefinition是<bean>在Spring容器中的內部表示。當配置檔案中所有的<bean>都被解析成BeanDefinition時,ApplicationContext將呼叫工廠後處理器的方法,因此我們有機會通過程式的方式調整Bean的配置資訊。如上對person的屬性address進行更改設定。

2、xml 相關配置

<!--1、註冊Bean-->
    <bean id="person" class="demo02.bean.Person"
          p:address="上海市"
          p:age="25"
          init-method="myInit"
          destroy-method="myDestroy"
    />

    <!--下面註冊bean後處理器是為了演示ApplicationContext bean生命週期的。-->
    <!--ApplicationContext和BeanFactory另一個最大的不同之處在於:
    前者會利用Java反射機制自動識別出配置檔案中定義的BeanPostProcessor、InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,並自動將它們註冊到應用上下文中;
    而後者需要在程式碼中通過手工呼叫addBeanPostProcessor()方法進行註冊。-->
    <!--2、註冊Bean後處理器-->
    <bean id="myBeanPostProcessor"
          class="demo02.BeanFactoryLifeCycle.MyBeanPostProcessor"/>
    <!--3、註冊Bean後處理器-->
    <bean id="myBeanFactoryPostProcessor"
          class="demo02.BeanFactoryLifeCycle.MyBeanFactoryPostProcessor"/>

②和③處定義的BeanPostProcessor和BeanFactoryPostProcessor會自動被ApplicationContext識別並註冊到容器中。②處註冊的工廠後處理器將會對①處配置的屬性值進行調整。在③處,我們還宣告瞭一個Bean後處理器,它也可以對Bean的屬性進行調整。啟動容器並檢視car Bean的資訊,我們將發現car Bean的brand屬性成功被工廠後處理器更改了。

3、測試類

/**
 * ApplicationContext bean生命演示
 */
public class ApplicationContexBeanLifeCycleMain {
    public static void main(String[] args) {
        //ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans/beans.xml");
        //ApplicationContext 介面中沒有實現close方法,所以可以用該類(該類是實現了ConfigurableApplicationContext介面和DisposableBean介面)
        AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans/beans.xml");
        Person person = (Person) applicationContext.getBean("person");
        person.introduce();
        applicationContext.close();
    }
}

4、執行結果

最後輸出:
呼叫了BeanFactoryPostProcessor.postProcessBeanFactory
呼叫了Person的無參構造器
呼叫了setAddress方法設定了屬性,上海市
呼叫了setAge方法設定了屬性,25
呼叫了setName方法設定了屬性,王二麻子
呼叫了BeanNameAware.setBeanName, value is person
呼叫了BeanFactoryAware.setBeanFactory, value ……
呼叫InitializingBean.afterPropertiesSet
呼叫bean配置的init-method
呼叫BeanPostProcessor.postProcessAfterInitialization,age大於20,調整為20
呼叫了setAge方法設定了屬性,20
呼叫了introduce方法--> name:王二麻子,age:20,address:中國
呼叫DisposableBean.destroy
呼叫bean配置的destroy-method


六、總結

1、Spring的生命週期可以從 BeanFactory 和 ApplicationContext 中的生命週期進行分析;

2、BeanFactory例項化Bean相關介面分為Bean級和容器級。bean級介面我們一般不實現,容器級如果需要處理一些共有特性,可以考慮實現;

3、ApplicationContext中bean的生命週期與BeanFactory類似。beanFactory中的後處理器介面需要呼叫addBeanPostProcessor方法進行註冊,而ApplicationContext中的後處理器可以通過配置和反射進行呼叫;

4、從上可以看到Bean的生命週期相關介面很多,如果都實現很複雜。通常我們的業務都可以通過 init-method 和 destory-method 方法配置來解決,這樣做簡單粗暴。


參考書籍:《精通Spring4.x企業應用開發實戰》

 

 

 

 

 

 

 

 

 

 

 

相關文章