什么是bean的生命周期?
Spring是一個容器, bean是存在于容器中被管理的對象。bean的生命周期就是指bean從實例化到被設置屬性,再到容器銷毀時bean被銷毀的整個過程。
為什么要了解bean的生命周期?
Spring 就是面向 Bean 的編程。如果把Spring比做一個舞臺,那么bean就是舞臺上的演員。Spring 作為Java中最優(yōu)秀的框架之一,已被廣泛的使用,很多第三方框架在設計的時候都考慮到了和Spring的集成,集成的根本其實就是在Spring容器中定義bean。而作為一個優(yōu)秀的框架,Spring很多設計思想都獨具匠心,在bean的整個生命周期過程中預留了很多擴展接口和自定義處理,掌握了bean的生命周期不僅可以學習優(yōu)秀的設計模式,而且對理解 Spring 框架原理有非常好的作用,有助于深入理解Ioc和AOP。
bean生命周期詳解
bean的生命周期不就是實例化,設置屬性,使用完以后被銷毀這么簡單嗎?實例化就是調(diào)用類的構造方法,設置屬性就是調(diào)用setter方法注入,銷毀甚至可以不用定義destroy方法。那么還有那么多要了解的么?
日常開發(fā)中我們定義最多的就是下面這種業(yè)務bean,這類bean的生命周期的確很簡單
<bean id="xxService" class="com.xx.xx.service.impl.xxServiceImpl"/>
除了這類bean的定義,我們在搭建Spring框架時其實還定義了很多bean。其中涉及到
BeanNameAware, BeanFactoryAware, BeanFactoryPostProcessor,InitializingBean, DisposableBean等各種生命周期接口。例如
屬性加載器定義:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
...
</bean>
線程池定義:
<bean id ="asyncExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="20" />
<property name="keepAliveSeconds" value="200" />
<property name="maxPoolSize" value="300" />
<property name="queueCapacity" value="300" />
<property name="threadNamePrefix" value="test_asynchronousExecutor" />
<property name="threadGroupName" value="test_asynchronousExecutorGroup" />
</bean>
下面我們詳細介紹bean的生命周期過程
-
生命周期流程
生命周期流程.png 各種生命周期接口方法分類
Bean的完整生命周期經(jīng)歷了各種方法調(diào)用,這些方法可以劃分為以下幾類:
1)Bean自身的方法:這個包括了Bean本身調(diào)用的方法和通過配置文件中<bean>的init-method和destroy-method指定的方法
2)Bean級生命周期接口方法:這個包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean這些接口的方法
3)容器級生命周期接口方法:這個包括了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 這兩個接口實現(xiàn),一般稱它們的實現(xiàn)類為“后處理器”。
4)工廠后處理器接口方法:這個包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的工廠后處理器 接口的方法。工廠后處理器也是容器級的。在容器啟動裝配配置文件之后立即調(diào)用。 (PropertyPlaceholderConfigurer其實就是一個工廠后處理器接口的實現(xiàn))
3.代碼演示
1)定義一個bean,調(diào)用Bean自身的方法和Bean級生命周期接口方法。它實現(xiàn)了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean這4個接口,同時有2個方法,對應配置文件中<bean>的init-method和destroy-method
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("【構造器】調(diào)用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接口】調(diào)用BeanFactoryAware.setBeanFactory()");
this.beanFactory = arg0;
}
// 這是BeanNameAware接口方法
@Override
public void setBeanName(String arg0) {
System.out.println("【BeanNameAware接口】調(diào)用BeanNameAware.setBeanName()");
this.beanName = arg0;
}
// 這是InitializingBean接口方法
@Override
public void afterPropertiesSet() throws Exception {
System.out
.println("【InitializingBean接口】調(diào)用InitializingBean.afterPropertiesSet()");
}
// 這是DiposibleBean接口方法
@Override
public void destroy() throws Exception {
System.out.println("【DiposibleBean接口】調(diào)用DiposibleBean.destory()");
}
// 通過<bean>的init-method屬性指定的初始化方法
public void myInit() {
System.out.println("【init-method】調(diào)用<bean>的init-method屬性指定的初始化方法");
}
// 通過<bean>的destroy-method屬性指定的初始化方法
public void myDestory() {
System.out.println("【destroy-method】調(diào)用<bean>的destroy-method屬性指定的初始化方法");
}
}
2)容器級生命周期接口BeanPostProcessor。BeanPostProcessor接口包括2個方法postProcessAfterInitialization和postProcessBeforeInitialization,這兩個方法的第一個參數(shù)都是要處理的Bean對象,第二個參數(shù)都是Bean的name。返回值也都是要處理的Bean對象
public class MyBeanPostProcessor implements BeanPostProcessor {
public MyBeanPostProcessor() {
super();
System.out.println("這是BeanPostProcessor實現(xiàn)類構造器??!");
}
@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;
}
}
3)工廠后處理器接口方法
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public MyBeanFactoryPostProcessor() {
super();
System.out.println("這是BeanFactoryPostProcessor實現(xiàn)類構造器??!");
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0)
throws BeansException {
System.out
.println("BeanFactoryPostProcessor調(diào)用postProcessBeanFactory方法");
BeanDefinition bd = arg0.getBeanDefinition("person");
bd.getPropertyValues().addPropertyValue("phone", "110");
}
}
4)配置文件
<?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="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>
5)測試代碼
public class BeanLifeCycle {
public static void main(String[] args) {
System.out.println("現(xiàn)在開始初始化容器");
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("現(xiàn)在開始關閉容器!");
((ClassPathXmlApplicationContext)factory).registerShutdownHook();
}
}
運行結果:
現(xiàn)在開始初始化容器
這是BeanFactoryPostProcessor實現(xiàn)類構造器!!
BeanFactoryPostProcessor調(diào)用postProcessBeanFactory方法
這是BeanPostProcessor實現(xiàn)類構造器??!
【構造器】調(diào)用Person的構造器實例化
【注入屬性】注入屬性address
【注入屬性】注入屬性name
【注入屬性】注入屬性phone
【BeanNameAware接口】調(diào)用BeanNameAware.setBeanName()
【BeanFactoryAware接口】調(diào)用BeanFactoryAware.setBeanFactory()
BeanPostProcessor接口方法postProcessBeforeInitialization對屬性進行更改!
【InitializingBean接口】調(diào)用InitializingBean.afterPropertiesSet()
【init-method】調(diào)用<bean>的init-method屬性指定的初始化方法
BeanPostProcessor接口方法postProcessAfterInitialization對屬性進行更改!
容器初始化成功
Person [address=武漢, name=張三, phone=110]
現(xiàn)在開始關閉容器!
【DiposibleBean接口】調(diào)用DiposibleBean.destory()
【destroy-method】調(diào)用<bean>的destroy-method屬性指定的初始化方法
現(xiàn)在回過頭來看看propertyConfigurer這個bean,它其實不是普通的bean,就是一個工廠后處理器,在容器啟動的時候,它的postProcessBeanFactory方法被調(diào)用,方法邏輯就是從配置的locations路徑中尋找properties文件,將文件內(nèi)容轉(zhuǎn)換為map對象,然后將容器中其他所有bean(其他是指非容器級生命周期接口的bean和非工廠后處理器的bean) <property>中的占位符替換成配置文件中的值。等到所有占位符替換完成,bean就可以正常的初始化了。
說到bean的生命周期,不得不提一下FactoryBean接口,它和BeanFactory名字非常類似,但是含義卻千差萬別。 它是一個特殊的接口,實現(xiàn)了此接口的bean,容器將調(diào)用接口的getObject()方法得到該bean對象,而不是調(diào)用類的構造方法。除此之外,其他生命周期和前面描述的一樣。
FactoryBean實際應用:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="xxx_master" />
<property name="mapperLocations">
<array>
<value>classpath*:mapper/**/*.xml</value>
</array>
</property>
</bean>
