實戰錄 | Spring中bean的生命週期

《實戰錄》導語

雲端衛士《實戰錄》欄目定期會向粉絲朋友們分享一些在開發運維中的經驗和技巧,希望對於關注我們的朋友有所裨益。本期分享人爲雲端衛士安全運營工程師顧慶威,將帶來Spring中bean的生命週期的分享。


Spring作爲當前Java最流行、最強大的輕量級框架,受到了程序員的熱烈歡迎。準確的瞭解Spring Bean的生命週期是非常必要的。我們通常使用ApplicationContext作爲Spring容器。這裏,我們講的也是 ApplicationContext中Bean的生命週期。而實際上BeanFactory也是差不多的,只不過處理器需要手動註冊。 


生命週期流程圖


Spring Bean的完整生命週期從創建Spring容器開始,直到最終Spring容器銷燬Bean,這其中包含了一系列關鍵點。

若容器註冊了以上各種接口,程序那麼將會按照以上的流程進行。下面將仔細講解各接口作用。


各種接口方法分類


Bean的完整生命週期經歷了各種方法調用,這些方法可以劃分爲以下幾類:


1、Bean的建立:容器尋找Bean的定義信息並將其實例化。

2、Setter注入執行Bean的屬性依賴注入。
3、BeanNameAware的setBeanName():如果Bean類有實現org.springframework.beans.BeanNameAware接口,工廠調用Bean的setBeanName()方法傳遞Bean的ID。
4、BeanFactoryAware的setBeanFactory():如果Bean類有實現org.springframework.beans.factory.BeanFactoryAware接口,工廠調用setBeanFactory()方法傳入工廠自身。
5、BeanPostProcessors的ProcessBeforeInitialization():如果有org.springframework.beans.factory.config.BeanPostProcessors和Bean關聯,那麼其postProcessBeforeInitialization()方法將被將被調用。
6、initializingBean的afterPropertiesSet():如果Bean類已實現org.springframework.beans.factory.InitializingBean接口,則執行他的afterProPertiesSet()方法。
7、Bean定義文件中定義init-method:可以在Bean定義文件中使用"init-method"屬性設定方法名稱例如:如果有以上設置的話,則執行到這個階段,就會執行initBean()方法。
8、BeanPostProcessors的ProcessaAfterInitialization():如果有任何的BeanPostProcessors實例與Bean實例關聯,則執行BeanPostProcessors實例的ProcessaAfterInitialization()方法。
此時,Bean已經可以被應用系統使用,並且將保留在BeanFactory中知道它不在被使用。有兩種方法可以將其從BeanFactory中刪除掉。
9、DisposableBean的destroy():在容器關閉時,如果Bean類有實現org.springframework.beans.factory.DisposableBean接口,則執行他的destroy()方法。
10、Bean定義文件中定義destroy-method方法。


演示


我們用一個簡單的Spring Bean來演示一下Spring Bean的生命週期。


1、首先是一個簡單的Spring Bean,調用Bean自身的方法和Bean級生命週期接口方法,爲了方便演示,它實現了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean這4個接口,同時有2個方法,對應配置文件中<bean>的init-method和destroy-method。如下:


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class Person implements BeanFactoryAware, BeanNameAware,InitializingBean, DisposableBean {
    private String name;
    private String address;
    private int phone;
    private BeanFactory beanFactory;
    private String beanName;
    public Person() {
        System.out.println("【構造器】調用Person的構造器實例化");
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        System.out.println("【注入屬性】注入屬性name");
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        System.out.println("【注入屬性】注入屬性address");
        this.address = address;
    }
    public int getPhone() {
        return phone;
    }
    public void setPhone(int phone) {
        System.out.println("【注入屬性】注入屬性phone");
        this.phone = phone;
    }
    @Override
    public String toString() {
        return "Person [address=" + address + ", name=" + name + ", phone="
                + phone + "]";
    }
    // 這是BeanFactoryAware接口方法
       
@Override
    public void setBeanFactory(BeanFactory arg0) throws BeansException {
        System.out.println("【BeanFactoryAware接口】調用BeanFactoryAware.setBeanFactory()");
        this.beanFactory = arg0;
    }
    // 這是BeanNameAware接口方法
      
@Override
    public void setBeanName(String arg0) {
        System.out.println("【BeanNameAware接口】調用BeanNameAware.setBeanName()");
        this.beanName = arg0;
    }
    // 這是InitializingBean接口方法
      
@Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("【InitializingBean接口】調用InitializingBean.afterPropertiesSet()");
    }
    // 這是DiposibleBean接口方法
       
@Override
    public void destroy() throws Exception {
        System.out.println("【DiposibleBean接口】調用DiposibleBean.destory()");
    }
    // 通過<bean>的init-method屬性指定的初始化方法
        
    public void myInit() {
        System.out.println("【init-method】調用<bean>的init-method屬性指定的初始化方法");
    }
    // 通過<bean>的destroy-method屬性指定的初始化方法
        
    public void myDestory() {
        System.out.println("【destroy-method】調用<bean>的destroy-method屬性指定的初始化方法");
    }
}


2、接下來是演示BeanPostProcessor接口的方法,如下:


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
    public MyBeanPostProcessor() {
        super();
        System.out.println("這是BeanPostProcessor實現類構造器!!");
        // TODO Auto-generated constructor stub
    }
    @Override
    public Object postProcessAfterInitialization(Object arg0, String arg1)
            throws BeansException {
        System.out.println("BeanPostProcessor接口方法postProcessAfterInitialization對屬性進行更改!");
        return arg0;
    }
    @Override
    public Object postProcessBeforeInitialization(Object arg0, String arg1)
            throws BeansException {
        System.out.println("BeanPostProcessor接口方法postProcessBeforeInitialization對屬性進行更改!");
        return arg0;
    }
}


如上,BeanPostProcessor接口包括2個方法postProcessAfterInitialization和postProcessBeforeInitialization,這兩個方法的第一個參數都是要處理的Bean對象,第二個參數都是Bean的name。返回值也都是要處理的Bean對象。這裏要注意。 


3、InstantiationAwareBeanPostProcessor 接口本質是BeanPostProcessor的子接口,一般我們繼承Spring爲其提供的適配器類InstantiationAwareBeanPostProcessor Adapter來使用它,如下:


import java.beans.PropertyDescriptor;

import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.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調用postProcessBeforeInstantiation方法");
                return null;
     }
      // 接口方法、實例化Bean之後調用
      
@Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
                    throws BeansException {
                System.out.println("InstantiationAwareBeanPostProcessor調用postProcessAfterInitialization方法");
                return bean;
       }

      // 接口方法、設置某個屬性時調用
      
@Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs,
                    PropertyDescriptor[] pds, Object bean, String beanName)
                    throws BeansException {
                System.out.println("InstantiationAwareBeanPostProcessor調用postProcessPropertyValues方法");
                return pvs;
    }

}


這個有3個方法,其中第二個方法postProcessAfterInitialization就是重寫了BeanPostProcessor的方法。第三個方法postProcessPropertyValues用來操作屬性,返回值也應該是PropertyValues對象。


4、演示工廠後處理器接口方法,如下:
 

package springBeanTest;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
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("person");
        bd.getPropertyValues().addPropertyValue("phone", "110");
    }
}


5、配置文件如下beans.xml,很簡單,使用ApplicationContext,處理器不用手動註冊:
 

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"
        
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
            xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
            xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">                 <bean id="beanPostProcessor" class="springBeanTest.MyBeanPostProcessor">
    </bean>         
      <bean id="instantiationAwareBeanPostProcessor" class="springBeanTest.MyInstantiationAwareBeanPostProcessor">
    </bean>
      <bean id="beanFactoryPostProcessor" class="springBeanTest.MyBeanFactoryPostProcessor">
    </bean>
      <bean id="person" class="springBeanTest.Person" init-method="myInit"
       destroy-method="myDestory" scope="singleton" p:name="張三" p:address="廣州" p:phone="15900000000"
/>
</beans>


6、下面測試一下:


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanLifeCycle {
    public static void main(String[] args) {
        System.out.println("現在開始初始化容器");     
           ApplicationContext factory =

            new ClassPathXmlApplicationContext("springBeanTest/beans.xml");
        System.out.println("容器初始化成功");    
        //得到Preson,並使用
        Person person = factory.getBean("person",Person.class);
        System.out.println(person);        
        System.out.println("現在開始關閉容器!");
        ((ClassPathXmlApplicationContext)factory).registerShutdownHook();
    }
}

關閉容器使用的是實際是AbstractApplicationContext的鉤子方法。
我們來看一下結果:

現在開始初始化容器

這是BeanFactoryPostProcessor實現類構造器!!

BeanFactoryPostProcessor調用postProcessBeanFactory方法

這是BeanPostProcessor實現類構造器!!

這是InstantiationAwareBeanPostProcessorAdapter實現類構造器!!

InstantiationAwareBeanPostProcessor調用postProcessBeforeInstantiation方法

【構造器】調用Person的構造器實例化

InstantiationAwareBeanPostProcessor調用postProcessPropertyValues方法

【注入屬性】注入屬性address

【注入屬性】注入屬性name

【注入屬性】注入屬性phone

【BeanNameAware接口】調用BeanNameAware.setBeanName()

【BeanFactoryAware接口】調用BeanFactoryAware.setBeanFactory()

BeanPostProcessor接口方法postProcessBeforeInitialization對屬性進行更改!

【InitializingBean接口】調用InitializingBean.afterPropertiesSet()

【init-method】調用<bean>的init-method屬性指定的初始化方法

BeanPostProcessor接口方法postProcessAfterInitialization對屬性進行更改!

InstantiationAwareBeanPostProcessor調用postProcessAfterInitialization方法

容器初始化成功

Person [address=廣州, name=張三, phone=110]

現在開始關閉容器!

【DiposibleBean接口】調用DiposibleBean.destory()

【destroy-method】調用<bean>的destroy-method屬性指定的初始化方法

實際上,ApplicationContext除了向BeanFactory那樣維護容器外,還提供了更加豐富的框架功能,如Bean的消息,事件處理機制等,這個可自行了解。 


發佈了44 篇原創文章 · 獲贊 6 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章