1. 程式人生 > >Spring學習5(3):Bean的生命週期

Spring學習5(3):Bean的生命週期

Spring學習5(3)

 Bean生命週期由多個特定的生命階段組成,每個生命階段都開出介面,允許外界由此對Bean施加控制。  在spring中有兩個層面來定義Bean的生命週期,一個是Bean的作用範圍,一個是例項化Bean時所經歷的一系列階段。

BeanFactory中Bean的生命週期

生命週期圖解

在這裡插入圖片描述

 具體呼叫過程如下:

  1. 當呼叫getBean()來請求某一個Bean時,如果容器註冊了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor介面則會在例項化Bean之前,將呼叫介面的postProcessBeforeInstantiation()
    方法。
  2. 根據配置情況呼叫Bean建構函式或工廠方法例項化Bean。
  3. 如果容器註冊了在步驟1中的那個介面那麼在例項化Bean後,可以呼叫介面的postProcessAfterInstantiation()方法來對已經例項化的物件做一點“梳妝打扮”。
  4. 如果Bean配置了屬性資訊,那麼容器在這一步將配置值設定到Bean對應屬性中,不過在設定每個屬性之前可以呼叫步驟1介面的postProcessPropertyValues()方法。
  5. 呼叫Bean的屬性設定方法設定屬性值。
  6. 如果Bean實現了org.springframework.bean.factory.BeanNameAware介面則將呼叫setBeanName()介面方法,將配置檔案中該Bean對應的名稱設定到Bean中。
  7. 如果Bean實現了org.springframework.beans.factory.BeanFactoryAware則將嗲用setBeanFactory()方法將BeanFactory容器例項設定到Bean中。
  8. 如果BeanFactory裝配了org.springframework.beans.factory.config.BeanPostProcessor,則呼叫BeanPostProcessor的Objetc:postProcessBeforeInitialization(Object bean, String beanName)這個函式提供當前正在處理的Bean以及beanName(當前Bean的配置名)返回加工處理後的Bean。這個方法可以對某些Bean進行特殊的處理,甚至改變Bean的行為,是spring框架中的重要方法,為容器提供對Bean進行後續加工處理的切入點,spring容器所提供的各種功能如Aop,動態代理等都經過BeanPostProcessor來實施。
  9. 如果Bean實現了InitializingBean的介面afterPropertiesSet()方法。
  10. 如果在<bean>中通過了init-method定義了初始化方法,則將執行這個方法。
  11. BeanPostProcessor除了步驟8中的方法還定義了postProcessAfterInitialization(Object bean, String beanName)方法,此時會呼叫這個方法讓容器再次獲得對Bean進行加工的機會。
  12. 如果在<bean>中指定Bean的作用範圍為scope="prototype"則將Bean返回給呼叫者,spring不再管理Bean的生命週期;如果指定為scope="singletion"則將Bean放入springIoC容器的快取池中,並將Bean引用放回給呼叫者,spring繼續對這些Bean進行後續的生命管理。
  13. 對於scope="singleton"的Bean(預設情況),當容器關閉時,如果Bean實現了DisposableBean介面,則將呼叫介面的destory()方法,可以在此編寫釋放資源,記錄日誌等操作。
  14. 對於scope="singletion"的Bean,如果通過<bean>的destroy-method屬性指定了Bean的銷燬方法,那麼Spring將執行Bean的這個方法,完成Bean資源的釋放。

Bean生命週期例項

 採用之前用過的Car類,讓它實現所有Bean級的生命介面。  我們修改com.smart下Car.java檔案為如下程式碼:

package com.smart;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;

public class Car implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean{
	private String brand;
	private String color;
	private int maxSpeed;
	
	private BeanFactory beanFactory;
	private String beanName;
	public Car() {
		System.out.println("呼叫Car()建構函式。");
		
	}
	
	public Car(String brand, String color, int maxSpeed) {
		this.brand = brand;
		this.color = color;
		this.maxSpeed = maxSpeed;
	}
	
	public void introduce() {
		System.out.println("brand:"+brand+";color:"
				+ color+";maxSpeed:"
				+ maxSpeed+".");
	}
	
	public void setBrand(String brand) {
		this.brand = brand;
		System.out.println("呼叫setBrand()設定屬性。");
	}
	
	public void setColor(String color) {
		this.color = color;
		System.out.println("呼叫setColor()設定屬性。");
	}
	
	public void setMaxSpeed(int maxSpeed) {
		this.maxSpeed = maxSpeed;
		System.out.println("呼叫setMaxSpeed()設定屬性。");
	}
	
	//BeanFactoryAwawre介面方法
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException{
		System.out.println("呼叫BeanFactoryAware.setBeanFactory().");
		this.beanFactory = beanFactory;
	}
	
	//BeanNameAware介面方法
	public void setBeanName(String beanName) {
		System.out.println("呼叫BeanNameAware.setBeanName().");
		this.beanName = beanName;
	}
	
	//InitializingBean介面方法
	public void afterPropertiesSet() throws Exception{
		System.out.println("呼叫InitializingBean.afterPropertiesSet().");
		
	}
	
	//DisposableBean介面方法
	public void destroy() throws Exception{
		System.out.println("呼叫DisposableBean.destroy().");
	}
	
	//通過<bean>的init-method屬性指定的初始化方法
	public void myInit() {
		System.out.println("呼叫init-method所指定的myInit(),將maxSpeed設定為240.");
		this.maxSpeed = 240;
	}
	
	//通過<bean>的destroy-method屬性指定的銷燬方法
	public void myDestroy() {
		System.out.println("呼叫destroy-method所指定的myDestroy().");
	}
}

 這裡實現了BeanFactoryAware, BeanNameAware,InitializingBean,DisposableBean等Bean級的生命週期介面,在下面定義了myInit()和myDestroy()方法,這兩個方法就可以在<bean>中通過init-method和destroy-method方法。

 MyInstantiationAwareBeanPostProcessor通過擴充套件InstantiationAwareBeanPostProcessor介面卡InstatiationAwareBeanPostProcessorAdapter提供實現。我們在com.smart.beanfactory中增加myInstantiationAwareBeanPostProcessor.java,程式碼如下:

package com.smart.beanfactory;
import java.beans.PropertyDescriptor;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.
InstantiationAwareBeanPostProcessorAdapter;
import com.smart.Car;
public class MyInstantiationAwareBeanPostProcessor extends 
InstantiationAwareBeanPostProcessorAdapter{
	
	//介面:在例項化Bean前呼叫
	public Object postProcessBeforeInstantiation(Class beanClass, String beanName)
		throws BeansException{
		if("car1".equals(beanName)) {
			System.out.println("InstantiationAware BeanPostProcessor.postProcess"
					+ "	BeforeInstantiation");
		}
		return null;
	}
	
	//介面:在例項化Bean後呼叫
	public boolean postProcessAfterInstantiation(Object bean, String beanName)
		throws BeansException{
		if("car1".equals(beanName)) {
			System.out.println("InstantiationAware BeanPostProcessor.postProcess"
					+ "		AfterInstantiation");
		}
		return true;
	}
	//介面:在設定某個屬性時呼叫
	public PropertyValues postProcessPropertyValues(
		PropertyValues pvs,PropertyDescriptor[] pds, Object bean, String beanName)
		throws BeansException{
		if("car1".equals(beanName)) {
			System.out.println("Instantiation AwareBeanPostProcessor.postProcess"
					+ "PropertyValues");
		}
		return pvs;
	}
}

 除此之外spring還有一個BeanPostProcessor實現類,對配置檔案所提供的屬性設定值進行判斷,並執行相應的“補缺補漏操作”,在程式碼清單如下:

package com.smart.beanfactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import com.smart.Car;
public class MyBeanPostProcessor implements BeanPostProcessor{
	public Object postProcessBeforeInitialization(Object bean, String beanName)
		throws BeansException{
		if(beanName.equals("car1")) {
			Car car = (Car)bean;
			if(car.getColor() == null) {
				System.out.println("呼叫BeanPostProcessor.postProcess"
						+ "	BeforeInitialization(), color is empty, default color is black");
				car.setColor("黑色");
				
			}
		}
		return bean;
	}
	
	public Object postProcessAfterInitialization(Object bean, String beanName)
		throws BeansException{
		if(beanName.equals("car1")) {
			Car car = (Car)(bean);
			if(car.getMaxSpeed() >= 200) {
				System.out.println("呼叫BeanPostProcessor.postProcess"
						+ "AfterInitialization(), modify maxSpeed to 200.");
				car.setMaxSpeed(200);
			}
		}
		return bean;
	}
}	

註冊介面

 上述這寫介面除了在Car.java中直接定義的介面其他的介面都需要註冊載入。首先我們在beans.xml中註冊myInit和myDestroy方法。程式碼更改如下:

	<bean id="car1" class="com.smart.Car"
	init-method="myInit"
	destroy-method="myDestroy"
	p:brand="紅旗CA72"
	p:color="黑色"
	p:maxSpeed="200"
	/>
	</beans>

 下面讓容器裝載配置檔案,然後分別註冊上面所提供的兩個後處理器。在com.smart.beanfactory下建立BeanLifeCycle.java檔案,程式碼如下:

package com.smart.beanfactory;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import com.smart.Car;
public class BeanLifeCycle{
	private static void LifeCycleInBeanFactory() {
		//裝載配置檔案並啟動容器
		Resource res = new ClassPathResource("com/smart/beanfactory/beans.xml");
		BeanFactory bf = new DefaultListableBeanFactory();
		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((DefaultListableBeanFactory)bf);
		reader.loadBeanDefinitions(res);
		
		//向容器註冊MyBeanPostProcessor後處理器
		((ConfigurableBeanFactory)bf).addBeanPostProcessor(new MyBeanPostProcessor());
		
		//向容器中註冊MyInstantiationAwareBeanPostProcessor後處理器
		((ConfigurableBeanFactory)bf).addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor());
		
		//第一次從容器中獲取car,將出發容器例項化Bean,將引發生命週期方法的呼叫
		Car car1 = (Car)bf.getBean("car1");
		car1.introduce();
		car1.setColor("紅色");
		
		//第二次從容器中獲取Car,直接從快取池中獲取
		Car car2 = (Car)bf.getBean("car1");
		
		//檢視car1和car2是否指向同一引用
		System.out.println("car1 == car2:" + (car1==car2));
		
		//關閉容器
		((DefaultListableBeanFactory)bf).destroySingletons();
	}
	
	public static void main(String[] args) {
		LifeCycleInBeanFactory();
	}
}

 這裡需要注意的是多個後處理器的呼叫順序和註冊順序無關,必須通過實現org.springframework.core.Ordered介面來確定呼叫順序。  執行該程式碼即可基本瞭解呼叫的過程。

ApplicationContext中Bean的生命週期

 書中給出的生命週期圖如下: 在這裡插入圖片描述  可以看到在Application只是多了幾個步驟:

  1. 如果Bean實現了org.springframework.context.ApplicationContextAware介面則會增加一個呼叫該介面方法setApplicationContext()的步驟
  2. 如果在配置檔案中申明瞭後處理介面BeanFactoryPostProcessor的實現類,則應用上下文在裝載配置檔案之後,初始化例項之前將呼叫BeanFactoryPostProcessor對配置資訊進行加工處理。

 除此之外,ApplicationContext會利用java的反射機制自動識別出配置檔案中定義的BeanPostProcessor等後處理器並註冊到上下文中,而不像BeanFactory需要手工呼叫addBeanPostProcessor()進行註冊。

例項

 假設我們希望對配置檔案中car1的brand配置屬性進行調整。在com.smart.context建立一個MyBeanFactoryPostProcessor.java,程式碼如下:

package com.smart.context;
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;
import com.smart.Car;

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
	//對car的brand配置資訊進行加工操作
	public void postProcessBeanFactory(ConfigurableListableBeanFactory bf)
		throws BeansException{
		BeanDefinition bd = bf.getBeanDefinition("car1");
		bd.getPropertyValues().addPropertyValue("brand", "奇瑞QQ");
		System.out.println("呼叫BeanFactoryPostProcessor.postProcessBean Factory()");
		
	}
}

 ApplicationContext在啟動時,將首先為配置檔案中的每一個<bean>生成一個BeanDefinition物件(<bean>在spring容器中的內部表示)而後我們就有機會對其進行修改。  接下來對beans.xml修改:

<bean id="car1" class="com.smart.Car"
	init-method="myInit"
	destroy-method="myDestroy"
	p:brand="紅旗CA72"
	p:color="黑色"
	p:maxSpeed="200"
	/>
	<bean id = "myBeanFactoryPostProcessor"
		class = "com.smart.context.MyBeanFactoryPostProcessor"/>