Spring核心系列之Bean的生命週期

我又不是架構師發表於2018-01-05

Spring核心系列之Bean的生命週期

Hello,大家好,上一篇Spring系列的文章給大家講了Spring容器的一些知識,然後講了核心的WebApplicationContext的初始化,這一節準備給大家講解Spring 容器中的Bean的生命週期。這一節我個人覺得還是比較實用的,在實際工作當中經常會用到這些知識來解決一些非常棘手的問題。好了,老套路,文章結構:

  1. ApplicationContext中Bean的生命週期

1. ApplicationContext中Bean的生命週期

先來張圖:

Spring核心系列之Bean的生命週期
大家看到這張圖肯定是一臉矇蔽。不要著急,我來慢慢解釋:從getBean(...)為觸發點,Spring容器的Bean生命週期就經歷了圖中的生命週期,先分個類:

  1. 圖中綠色箭頭的三個步驟(InstantiationAwareBeanPostProcessor)和粉紅色箭頭的兩個步驟(BeanPostProcessor)為容器級的生命週期介面,當Spring每載入任何一個Bean到容器中時,這些介面都會起到如圖中的幾次呼叫。這兩個處理器叫做"容器級後處理器",他們的影響是全域性的,能夠影響所有的Bean.
  2. 圖中大紅色圓圈圈住的介面叫做"工廠級後處理器",類似的介面還有CustomEditorConfigurer,PropertyPlaceholderConfigurer等,這類介面只在上下文初始化的時候呼叫一次,其目的是完成一些配置檔案的加工處理工作。
  3. 剩下的就簡單了,屬於Bean級別的介面,專屬於某個Bean所有,每個Bean例項化的時候呼叫自己特有的。

值得一提的是,無論是"容器級後處理器"還是"工廠級後處理器",他們都是可以配置多個的(如,配置兩個BeanPostProcessor),如果想控制他們的呼叫順序,實現一個org.springframework.core.Ordered介面即可。當然了,一般不用,一般一類後處理器只有一個即可。

重點強調!: 這些介面的呼叫順序並不是一塵不變的,會隨便Spring的版本變動而變動,大家要做的是萬變不離其宗,知道能夠通過這些介面在Bean初始化的時做一些屬性上的操作。呼叫順序要根據具體的版本來自己測試。下面我會給大家來列一個例子:

public class Student implements BeanFactoryAware, BeanNameAware,
        InitializingBean, DisposableBean,ApplicationContextAware {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("BeanFactoryAware......");
    }

    @Override
    public void setBeanName(String s) {
        System.out.println("BeanNameAware......");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean......");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean......");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("ApplicationContextAware......");
    }
}

複製程式碼

BeanFactoryPostProcessor:

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    public MyBeanFactoryPostProcessor() {
        super();
        System.out.println("這是BeanFactoryPostProcessor實現類構造器!!");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0)
            throws BeansException {
        System.out.println("BeanFactoryPostProcessor呼叫postProcessBeanFactory方法");
        BeanDefinition bd = arg0.getBeanDefinition("student");
        MutablePropertyValues propertyValues = bd.getPropertyValues();
        //配置檔案中的資訊在載入到Spring中後以BeanDefinition的形式存在.在這裡又可以更改BeanDefinition,所以可以理解為更改配置檔案裡面的內容
//        propertyValues.add("zdy","123");
    }

}
複製程式碼

BeanPostProcessor:

public class MyBeanPostProcessor implements BeanPostProcessor {

    public MyBeanPostProcessor() {
        super();
        System.out.println("這是BeanPostProcessor實現類構造器!!");
    }

    @Override
    public Object postProcessAfterInitialization(Object arg0, String arg1)
            throws BeansException {
        System.out.println("BeanPostProcessor介面方法After對屬性進行更改!");
        return arg0;
    }

    @Override
    public Object postProcessBeforeInitialization(Object arg0, String arg1)
            throws BeansException {
        System.out.println("BeanPostProcessor介面方法Before對屬性進行更改!");
        return arg0;
    }
}
複製程式碼

InstantiationAwareBeanPostProcessorAdapter:

public class MyInstantiationAwareBeanPostProcessor extends
        InstantiationAwareBeanPostProcessorAdapter {

    public MyInstantiationAwareBeanPostProcessor() {
        super();
        System.out.println("這是InstantiationAwareBeanPostProcessorAdapter實現類構造器!!");
    }

    // 介面方法、例項化Bean之前呼叫
    @Override
    public Object postProcessBeforeInstantiation(Class beanClass,
                                                 String beanName) throws BeansException {
        System.out.println("InstantiationAwareBeanPostProcessor呼叫Before方法");
        return null;
    }

    // 介面方法、例項化Bean之後呼叫
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        System.out.println("InstantiationAwareBeanPostProcessor呼叫Ater方法");
        return bean;
    }

    // 介面方法、設定某個屬性時呼叫
    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs,
                                                    PropertyDescriptor[] pds, Object bean, String beanName)
            throws BeansException {
        System.out.println("InstantiationAwareBeanPostProcessor呼叫postProcessPropertyValues方法");
        return pvs;
    }
}
複製程式碼

然後我們的Main方法:

public class App 
{
    public static void main( String[] args )
    {

        ApplicationContext ac =new ClassPathXmlApplicationContext("applicationContext.xml");
        Student stu = (Student) ac.getBean("student");
        stu.setName("wangwu");
    }
}

複製程式碼

Spring檔案最簡單:(注意要把我們自己定義的處理器全部加到容器裡去

<?xml version="1.0" encoding="UTF-8"?>
<beans
    <bean id="student" class="com.zdy.Student">
        <constructor-arg value="zhangsan"/>
    </bean>
    <bean id="myBeanFactoryPostProcessor" class="com.zdy.MyBeanFactoryPostProcessor"></bean>
    <bean id="myInstantiationAwareBeanPostProcessor" class="com.zdy.MyInstantiationAwareBeanPostProcessor"></bean>
    <bean id="myBeanPostProcessor" class="com.zdy.MyBeanPostProcessor"></bean>
</beans>
複製程式碼

然後run一下子,看結果:

輸出為:
這是BeanFactoryPostProcessor實現類構造器!!
BeanFactoryPostProcessor呼叫postProcessBeanFactory方法
這是InstantiationAwareBeanPostProcessorAdapter實現類構造器!!
這是BeanPostProcessor實現類構造器!!
InstantiationAwareBeanPostProcessor呼叫Before方法
InstantiationAwareBeanPostProcessor呼叫postProcessPropertyValues方法
BeanNameAware......
BeanFactoryAware......
ApplicationContextAware......
BeanPostProcessor介面方法Before對屬性進行更改!
InitializingBean......
InstantiationAwareBeanPostProcessor呼叫Ater方法
BeanPostProcessor介面方法After對屬性進行更改!
複製程式碼

好了,其實大致流程就說完了,我大致針對Bean的生命週期說一下:Spring為了儘可能的把自己內部的東西機制暴露出來給使用者使用,所以在Bean建立的過程中加了很多機制,通過所謂的"處理器"Processor暴露出來,然後處理器都有自己的順序,我們需要做的就是定義好處理器的邏輯,然後註冊到Sprinhg容器中,Spring就會呼叫了。其次,還有一種方式,就是讓我們的Bean實現一些介面(類似於ApplicationContextAware),通過這種方式,在Bean初始化的某個步驟呼叫介面定義好的方法來傳入一些資訊進來,像ApplicationContextAware就把ApplicationContext給傳給我們了。

然後我給大家說幾個實用點的知識點,大家記著,用到時回來翻一翻就可以了:

  1. 上面的生命週期流程圖,時候的時候注意呼叫先後順序,避免屬性被覆蓋的現象。
  2. BeanFactoryPostProcessor 主要是在Spring剛載入完配置檔案,還沒來得及初始化Bean的時候做一些操作。比如篡改某個Bean在配置檔案中配置的內容。
  3. InstantiationAwareBeanPostProcessorAdapter 基本沒什麼鳥用,Bean初始化後,還沒有設定屬性值時呼叫,和BeanFactoryPostProcessor一樣,可以篡改配置檔案載入到記憶體中的資訊。
  4. ApplicationContextAware:用處很大,注入了ApplicationContext到Bean中。
  5. InitializingBean:有用處,可以在Bean屬性全部改完之後,再做一些定製化操作。
  6. BeanPostProcessor:沒什麼用,Spring框架內部使用的比較猛,像什麼AOP,動態代理,都是在這搞事。後期有時間和大家分析。
  7. 其他的像什麼init-method,destroy方法,基本都是個擺設。。我是沒怎麼用過,只知道有這麼回事。

結語

好了,Bean的生命週期算上和大家分享完了,其實沒什麼東西,大家知道有這麼回事,能用到"後處理器"搞事的時候回來大致看下順序即可。其次就是一些Bean實現的介面,最常用的就是(ApplicationContextAware和InitializingBean)了。還有就是BeanPostProcessor,因為這個介面的方法裡會把Bean實體以Object傳進去。所以可以進行一些屬性上的操作。其實說實在的,程式設計師用的比較少。框架內部用的多。OK,這篇講的其實比較糙,主要是因為沒什麼東西可講,Over,Have a good day !

感覺這期講的不好,給大家一個傳送門吧。感覺講的比我還糙的一篇,大家結合著參考下 (^_^):

相關文章