Spring Bean各階段生命週期的介紹

尋覓beyond發表於2020-06-27

一.xml方式配置bean

二.Aware介面

  2.1 BeanNameAware

  2.2 BeanFactoryAware

  2.3 ApplicationContextAware

  2.4 Aware各介面的執行順序

  2.4 Aware介面總結

三.BeanPostProcessor介面

四.InitializingBean介面

五.init-method方法

六.DestructionAwareBeanPostProcessor介面

七.DisposableBean介面

八.destory-method方法

九.生命週期大雜燴

  9.1 實現多介面的Student類

  9.2 BeanPostProcessor前後置處理

  9.3 DestructionAwareBeanPostPrecessor介面

  9.4 配置xml檔案

  9.5 測試程式碼

  9.6 輸出結果

十.總結

 

 

 

 

  Spring Bean的生命週期是一個老生常談的問題了,網上一搜一大把,無非就是畫一幅流程圖(比如下面這幅圖),然後用語言介紹建立bean後執行各Aware介面,然後BeanPostProcessor.....最終Bean建立成功了,就可以使用這個Bean了,然後在容器銷燬的時候,又會執行一些操作。

  其實對於上面的提到的流程圖,注意上面的圖只是Spring Bean的大概流程(省略了一部分),主要涉及到了5個介面,分別是XxxAware、BeanPostProcessor、InitiailizingBean、Destruction、DisposableBean介面,本文將會對這幾個介面,以及init-method、destroy-method做相關的使用介紹,在明白怎麼使用後,再把他們串起來,這樣的話,對於Spring Bean的生命週期就差不多知道咋回事了,而不用死記硬背。

 

一. xml方式配置Bean

  在說Aware、BeanPostProcessor、InitiailizingBean、Destruction、DisposableBean這些介面前,先簡單回顧一下使用xml配置並獲取一個Student類的bean過程,後面介紹各個介面的使用方式時時,也是按照這個形式;

1.1 建立Student類

  平淡無奇的Student類:

package cn.ganlixin.entity;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Data
@Slf4j
public class Student {

    private Integer id;
    private String name;
}

  

1.2 建立配置檔案

  平淡無奇的applicationContext.xml配置檔案,建立一個student bean,利用setter方式設定初始值:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>
</beans>

  

1.3 測試

  建立一個Main類,用於測試

package cn.ganlixin;

import cn.ganlixin.entity.Student;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

@Slf4j
public class Test {

    public static void main(String[] args) {
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

        Student student = beanFactory.getBean("student", Student.class);
        log.info("測試程式獲取到的student bean:{}", student);
    }
}

  下面是執行程式的輸出,可以看到和預期相符,建立一個Student的bean,id和name預設值為99、張三;

INFO  [main] cn.ganlixin.Test - 測試程式獲取到的student bean:Student(id=99, name=張三)

   

二.Aware介面

  Aware介面有很多實現類,本文只介紹BeanNameAware、BeanFactoryAware、ApplicationContextAware,關係如下:

  

 

2.1 BeanNameAware

  建立一個Student類,讓該類實現BeanNameAware介面,並且重寫setBeanName方法

@Data
@Slf4j
public class Student implements BeanNameAware {

    private Integer id;
    private String name;

    /**
     * 實現了BeanNameAware介面後,需重寫setBeanName方法,接收的引數就是bean的id
     *
     * @param s bean的id
     */
    @Override
    public void setBeanName(String s) {
        log.info("beanName:{}, student bean:{}", s, this);
        this.id = 100;
        log.info("將beanName:{}的id改為100", s);
    }
}

  配置檔案和測試程式都不改變,執行測試程式,輸出內容如下:

INFO  [main] cn.ganlixin.entity.Student - beanName:student, student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - 將beanName:student的id改為100
INFO  [main] cn.ganlixin.Test - 測試程式獲取到的student bean:Student(id=100, name=張三)

  可以看到,實現BeanNameAware介面後,重寫setBeanName的方法中,獲取到的student bean,是已經初始化的bean(屬性都已經有值了),並且setBeanName方法中可以對當前的bean進行各種操作,包括修改bean的某些屬性,最後獲取到的bean是已經修改後的bean。

  這裡只是簡單介紹了一下BeanNameAware介面的用法,使用BeanNameAware介面,可以對當前Bean進行操作

 

2.2 BeanFactoryAware

  建立Student類,實現BeanFactoryAware介面,並且重寫setBeanFactory方法

@Data
@Slf4j
public class Student implements BeanFactoryAware {

    private Integer id;
    private String name;

    /**
     * 實現BeanFactoryAware介面後,需重寫setBeanFactroy方法
     *
     * @param beanFactory 建立該bean的beanFactory
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        // 可以在setBeanFactory方法中獲取、修改beanFactory中的所有bean
        
        log.info("student this bean:{}", this);
        Student student = beanFactory.getBean("student", Student.class);
        log.info("通過beanFactory獲取student bean:{}", student);

        // 將name設定為李四
        this.name = "李四";
    }
}

  執行輸出如下:

INFO  [main] cn.ganlixin.entity.Student - student this bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - 通過beanFactory獲取student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程式獲取到的student bean:Student(id=99, name=李四)

  通過上面的程式碼輸出結果可以看出,實現BeanFactoryAware介面後,可以在setBeanFactory方法中操作BeanFactory的所有bean,操作的範圍要比BeanNameAware要大。

 

2.3 ApplicationContextAware

  ApplicationContext,有多種稱呼,比如“應用容器”、“環境”、“上線文”...

  建立Student類,實現ApplicationContextAware介面,並且重寫setApplicationContext介面:

@Data
@Slf4j
public class Student implements ApplicationContextAware {

    private Integer id;
    private String name;

    /**
     * 實現ApplicationContextAware介面後,徐重寫setApplicationContext方法
     *
     * @param applicationContext 該bean所在的上下文(applicationContext、容器)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("Student this:{}", this);

        final Student student = applicationContext.getBean("student", Student.class);
        final Environment environment = applicationContext.getEnvironment();
        log.info("student bean:{}", student);
        log.info("env -> user.dir:{}", environment.getProperty("user.dir"));
    }
}

  需要修改一下測試程式,測試程式中載入配置時使用的XmlBeanFactory,而XmlBeanFactory不會回撥ApplicationContextAware介面的setApplicationContext方法,下面使用ClassPathXmlApplicationContext類來載入配置:

@Slf4j
public class Test {

    public static void main(String[] args) {
        //BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

        // 使用ApplicationContext來載入配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);
        log.info("測試程式獲取到的student bean:{}", student);
    }
}

  執行測試程式:

INFO  [main] cn.ganlixin.entity.Student - Student this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - env -> user.dir:/Users/ganlixin/code/java-code-all/spring
INFO  [main] cn.ganlixin.Test - 測試程式獲取到的student bean:Student(id=99, name=張三)

  實現ApplicationContextAware介面後,在setApplicationContext方法中,入參是當前的applicationContext,也就是說,可以在該方法中對Spring容器進行設定,操作的範圍又要比BeanFactoryAware的setBeanFactory要廣得多。

 

2.4 Aware各介面執行的先後順序

  既然有這幾個Aware介面,如果一個類同時實現了這3個介面,那麼執行順序是怎樣的呢?下面就來測試一下。

  建立Student類,分別實現BeanNameAware、BeanFactoryAware、ApplicationContextAware介面,並重寫其介面的方法:

@Data
@Slf4j
public class Student implements BeanNameAware, BeanFactoryAware, ApplicationContextAware {

    private Integer id;
    private String name;

    /**
     * 實現了BeanNameAware介面後,需重寫setBeanName方法,接收的引數就是bean的id
     *
     * @param s bean的id
     */
    @Override
    public void setBeanName(String s) {
        log.info("call BeanNameAware.setBeanName()");
    }

    /**
     * 實現BeanFactoryAware介面後,需重寫setBeanFactroy
     *
     * @param beanFactory 建立該bean的bean工廠
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("call BeanFactoryAware.setBeanFactory()");
    }

    /**
     * 實現ApplicationContextAware介面後,徐重寫setApplicationContext方法
     *
     * @param applicationContext 該bean所在的上下文(applicationContext、容器)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("call ApplicationContextAware.setApplicationContext()");
    }
}

  仍舊使用ClassPathXmlApplicationContext類來載入配置,執行輸出結果如下:

INFO  [main] cn.ganlixin.entity.Student - call BeanNameAware.setBeanName()
INFO  [main] cn.ganlixin.entity.Student - call BeanFactoryAware.setBeanFactory()
INFO  [main] cn.ganlixin.entity.Student - call ApplicationContextAware.setApplicationContext()
INFO  [main] cn.ganlixin.Test - 測試程式獲取到的student bean:Student(id=99, name=張三)

  

2.4 Aware介面總結

  上面演示了Spring中幾個Aware介面的用法和特點,下面總結一下:

  1.實現BeanNameAware介面後,重寫setBeanName方法,可以對單個Bean進行擴充套件修改;

  2.實現BeanFactoryAware介面後,重寫setBeanFactory方法,可以對bean工廠中的所有Bean進行擴充套件修改;

  3.實現ApplicationContextAware介面後,重寫setApplicationContext方法後,可以對整個容器進行擴充套件修改;

  4.這幾個介面的執行順序分別是BeanNameAware->BeanFactoryAware->ApplicationContextAware;

 

三.BeanPostProcessor介面

  BeanPostProcessor和前面的Aware介面有些區別,通過下面的例子就能看出區別在哪裡!

  下面舉個例子,建立MyBeanPostProcessor類,實現BeanPostProcessor介面,注意,這裡沒有在Student類上實現BeanPostProcessor介面。

@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {

    /**
     * 實現了BeanPostProcessor介面後,重寫postProcessBeforeInitialization,在各種Aware介面執行完畢後執行該方法
     *
     * @param bean     本次處理的bean
     * @param beanName 本次處理的beanName(bean id)
     * @return 返回的是在本方法中處理後的bean
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessBeforeInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }

    /**
     * 實現了BeanPostProcessor介面後,重寫postProcessBeforeInitialization,在initMethod方法執行完畢後執行該方法
     *
     * @param bean     本次處理的bean
     * @param beanName 本次處理的beanName(bean id)
     * @return 返回的是在本方法中處理後的bean
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessAfterInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }
}

  建立兩個類,分別是Student和User類,其中Use類沒有實現Aware介面,Student類實現了前面提到的3個Aware介面

@Data
public class User {
    private Integer id;
    private String name;
}

  

@Data
@Slf4j
public class Student implements BeanNameAware, BeanFactoryAware, ApplicationContextAware {

    private Integer id;
    private String name;

    /**
     * 實現了BeanNameAware介面後,需重寫setBeanName方法,接收的引數就是bean的id
     *
     * @param s bean的id
     */
    @Override
    public void setBeanName(String s) {
        log.info("call BeanNameAware.setBeanName()");
    }

    /**
     * 實現BeanFactoryAware介面後,需重寫setBeanFactroy
     *
     * @param beanFactory 建立該bean的bean工廠
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("call BeanFactoryAware.setBeanFactory()");
    }

    /**
     * 實現ApplicationContextAware介面後,徐重寫setApplicationContext方法
     *
     * @param applicationContext 該bean所在的上下文(applicationContext、容器)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("call ApplicationContextAware.setApplicationContext()");
    }
}

  

  xml配置檔案:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>

    <bean class="cn.ganlixin.entity.User" id="user">
        <property name="id" value="88"/>
        <property name="name" value="王五"/>
    </bean>

    <!-- 將實現了BeanPostProcessor介面的類也宣告為bean -->
    <bean class="cn.ganlixin.processor.MyBeanPostProcessor"/>
</beans>

  

  測試:

INFO  [main] cn.ganlixin.entity.Student - call BeanNameAware.setBeanName()
INFO  [main] cn.ganlixin.entity.Student - call BeanFactoryAware.setBeanFactory()
INFO  [main] cn.ganlixin.entity.Student - call ApplicationContextAware.setApplicationContext()
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessBeforeInitialization, beanName:student1, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessAfterInitialization, beanName:student1, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessBeforeInitialization, beanName:user, bean:User(id=88, name=王五)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessAfterInitialization, beanName:user, bean:User(id=88, name=王五)
INFO  [main] cn.ganlixin.Test - 測試程式獲取到的student bean:Student(id=99, name=張三)

  從上面的執行結果可以得出以下結論:

  1.因為只有Student實現了Aware介面,所以建立student bean的時候會呼叫對應的Aware介面方法,而User類沒有實現Aware介面,所以並沒有呼叫Aware介面方法;

  2.Student和User類都沒有繼承BeanPostProcessor介面,但是在建立student和user bean的時候,都掉用了MyBeanPostProcessor類中的前置和後置處理(繼承自BeanPostProcessor介面);

  3.BeanPostProcessor介面的前置和後置處理,是在Aware介面之後呼叫;

  4.很重要的一點,需要將BeanPostProcessor介面實現類宣告為bean,使用<bean>配置或者使用@Component註解,不然BeanPostProcessor不起作用。

 

四.InitializingBean介面

  建立Student類,實現InitializingBean介面,然後重寫afterPropertiesSet方法:

@Data
@Slf4j
public class Student implements InitializingBean {

    private Integer id;
    private String name;

    @Override
    public void afterPropertiesSet() throws Exception {
        // 同樣可以在這裡修改bean的屬性值
        log.info("InitialingBean.afterPropertiesSet, this:{}", this);
    }
}

  修改xml配置檔案,建立student bean,測試:

INFO  [main] cn.ganlixin.entity.Student - InitialingBean.afterPropertiesSet, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程式獲取到的student bean:Student(id=99, name=張三)

  

五.init-method

  建立Student類,增加一個額外的方法display()

@Data
@Slf4j
public class Student {

    private Integer id;
    private String name;

    public void display() {
        log.info("Student.display call, this:{}", this);
    }
}

  修改配置檔案,在<bean>標籤中增加init-method屬性,值為display,也就是Student的display方法名:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student" init-method="display">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>
</beans>

  執行測試:

INFO  [main] cn.ganlixin.entity.Student - Student.display call, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程式獲取到的student bean:Student(id=99, name=張三)

  上面,輸出了display中的內容,這是在設定bean的時候呼叫的。

 

六.DestructionAwareBeanPostProcessor介面

  DestructionAwareBeanPostProcessor介面,從名稱上可以看出來是DestructionAware + BeanPostProcessor的組合,其實也的確是這樣,但是需要注意的就是,spring並沒有提供DestructionAware介面!!

  下面是DestructionAwareBeanPostProcessor介面的定義:

public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {

    /**
     * Destruction執行的操作
     *
     * @param bean     處理的bean
     * @param beanName bean的名稱
     * @throws BeansException
     */
    void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;

    /**
     * 是否需要執行postProcessBeforeDestruction方法
     *
     * @param bean 執行Destruction的bean
     * @return 是否需要執行postProcessBeforeDestruction方法
     */
    default boolean requiresDestruction(Object bean) {
        return true;
    }
}

  DestructionAwareBeanPostProceesor繼承自BeanPostProcessor介面,所以也可以重寫前值和後置處理。

  下面介紹使用示例,建立MyDestructionAwareBeanPostProceesor,繼承DestructionAwareBeanPostProceesor介面:

@Slf4j
public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor {

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        log.info("DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, \n\tbeanName:{}, bean:{}", beanName, bean);
    }
    
    @Override
    public boolean requiresDestruction(Object bean) {
        return true; // 返回true,一律執行postProcessBeforeDestruction方法
        // 如果返回false,則不執行postProcessBeforeDestruction方法
    }
}

  修改配置檔案:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>


    <bean class="cn.ganlixin.entity.User" id="user">
        <property name="id" value="88"/>
        <property name="name" value="王五"/>
    </bean>

    <!-- 將實現了DestructionAwareBeanPostProcessor介面的實現類宣告為bean> -->
    <bean class="cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor"/>
</beans>

  測試程式:

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來載入配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);
        User user = context.getBean("user", User.class);

        log.info("測試程式獲取到的student bean:{}", student);

        // 獲取bean工廠,然後呼叫destroyBean銷燬bean
        AutowireCapableBeanFactory factory = context.getAutowireCapableBeanFactory();
        factory.destroyBean(student);
    }
}

  執行測試程式,輸出如下:

INFO  [main] cn.ganlixin.Test - 測試程式獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor - DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, 
	beanName:cn.ganlixin.entity.Student, bean:Student(id=99, name=張三)

  可以看到,在手動呼叫destroyBean方法來銷燬student bean的時候,呼叫了MyDestructionAwareBeanPostProcessor中定義的方法。

  需要注意的是,雖然這裡使用destroyBean來銷燬了student bean,如果又通過getBean來獲取student bean,則會重新建立student bean。

 

七.DisposableBean介面 

  前面介紹了DestructionAwareBeanPostProcessor介面,可以對所有的bean設定銷燬(destruction)後的處理操作。

  而這裡介紹的DisposableBean介面,就是對單獨的Bean進行destrction後的處理,也就是說不是應用到所有的bean上。

  簡單介紹一下用法,建立Student類和User類,User類正常(不實現任何介面),Student類實現DisposableBean介面,然後重寫destroy方法:

@Data
@Slf4j
public class Student implements DisposableBean {

    private Integer id;
    private String name;

    @Override
    public void destroy() throws Exception {
        log.info("DisposableBean.destroy, this:{}", this);
    }
}

@Data
public class User {
    private Integer id;
    private String name;
}

  建立配置檔案:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>

    <bean class="cn.ganlixin.entity.User" id="user">
        <property name="id" value="88"/>
        <property name="name" value="王五"/>
    </bean>
</beans>

  測試程式:

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來載入配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);
        User user = context.getBean("user", User.class);

        log.info("測試程式獲取到的student bean:{}", student);
        log.info("測試程式獲取到的user bean:{}",user);

        // 獲取bean工廠,然後呼叫destroyBean銷燬bean
        AutowireCapableBeanFactory factory = context.getAutowireCapableBeanFactory();
        factory.destroyBean(student);
        factory.destroyBean(user);
    }
}

  執行輸出:

INFO  [main] cn.ganlixin.Test - 測試程式獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程式獲取到的user bean:User(id=88, name=王五)
INFO  [main] cn.ganlixin.entity.Student - DisposableBean.destroy, this:Student(id=99, name=張三)

  可以看到,雖然測試程式碼中destroy了student和user兩個bean,但是隻有student bean在銷燬時觸發了DisposableBean的destory方法。

 

八.destroy-method方法

  和init-method相對應的就是destory-method方法了,建立Student類,增加clean方法(自定義):

@Data
@Slf4j
public class Student {

    private Integer id;
    private String name;

    public void clean() {
        log.info("Student.clean, this:{}", this);
    }
}

  修改配置檔案,<bean>標籤中使用destroy-method屬性,值為clean方法

<bean class="cn.ganlixin.entity.Student" id="student" destroy-method="clean">
    <property name="id" value="99"/>
    <property name="name" value="張三"/>
</bean>

  測試程式:

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來載入配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);

        log.info("測試程式獲取到的student bean:{}", student);

        // 刪除bean
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getAutowireCapableBeanFactory();
        registry.removeBeanDefinition("student");
    }
}

  輸出:

INFO  [main] cn.ganlixin.Test - 測試程式獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - Student.clean, this:Student(id=99, name=張三)

  

九.宣告週期大雜燴

  上面對每一種介面都做了介紹,這裡就將所有介面都做一下整合,嘗試在一個測試程式中測試所有介面,這個過程中就會對Bean的生命週期有清晰的認識:

9.1 實現多介面的Student類

  建立Student類,實現Aware、InitializingBean、DisposableBean介面,並且增加display、clean方法,作為init-method和destory-method。

package cn.ganlixin.entity;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

@Data
@Slf4j
public class Student implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {

    private Integer id;
    private String name;

    @Override
    public void setBeanName(String s) {
        log.info("BeanNameAware.setBeanName, this:{}", this);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("BeanFactoryAware.setBeanFactory, this:{}", this);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("ApplicationContextAware.setApplicationContext, this:{}", this);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("InitialingBean.afterPropertiesSet, this:{}", this);
    }

    @Override
    public void destroy() throws Exception {
        log.info("DisposableBean.destory, this:{}", this);
    }

    public void display() {
        log.info("init-method, Student.display, this:{}", this);
    }

    public void clean() {
        log.info("destroy-method, Student.clean, this:{}", this);
    }
}

 

9.2 BeanPostProcessor前後置處理

  建立MyBeanPostProcessor介面實現類,並重寫前置和後置處理方法:

package cn.ganlixin.processor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessBeforeInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessAfterInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }
}

 

9.3 DestructionAwareBeanPostPrecessor介面

  建立MyDestructionAwareBeanPostProcessor類,並重寫其中的方法(不重寫BeanPostProcessor的前後置處理方法):

package cn.ganlixin.processor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;

@Slf4j
public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor {

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        log.info("DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, \n\tbeanName:{}, bean:{}", beanName, bean);
    }

    @Override
    public boolean requiresDestruction(Object bean) {
        return true; // 返回true,一律執行postProcessBeforeDestruction方法
        // 如果返回false,則不執行postProcessBeforeDestruction方法
    }
}

  

9.4 配置xml檔案  

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 建立student bean,指定init-method和destroy-method -->
    <bean class="cn.ganlixin.entity.Student" id="student" init-method="display" destroy-method="clean">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>


    <!-- 將實現了DestructionAwareBeanPostProcessor介面的實現類宣告為bean-->
    <bean class="cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor"/>

    <!-- 將實現了BeanPostProcessor介面的類也宣告為bean-->
    <bean class="cn.ganlixin.processor.MyBeanPostProcessor"/>
</beans>

  

9.5 測試程式碼

package cn.ganlixin;

import cn.ganlixin.entity.Student;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來載入配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);

        log.info("測試程式獲取到的student bean:{}", student);

        // 刪除bean
        BeanDefinitionRegistry factory = (BeanDefinitionRegistry) context.getAutowireCapableBeanFactory();
        factory.removeBeanDefinition("student");
    }
}

  

9.6 輸出結果

INFO  [main] cn.ganlixin.entity.Student - BeanNameAware.setBeanName, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - BeanFactoryAware.setBeanFactory, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - ApplicationContextAware.setApplicationContext, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessBeforeInitialization, beanName:student, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - InitialingBean.afterPropertiesSet, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - init-method, Student.display, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessAfterInitialization, beanName:student, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程式獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor - DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, 
	beanName:student, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - DisposableBean.destory, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - destroy-method, Student.clean, this:Student(id=99, name=張三)

  

十.總結

  看了上面這個輸出結果,再結合下面這個圖,基本就能掌握Bean的大致生命週期了。

  

 

   原文地址:https://www.cnblogs.com/-beyond/p/13188675.html

   

 

  

 

相關文章