这是回顾Spring系列书籍的第二篇文章,主要分析Spring容器中Bean的生命周期,这里的Spring容器主要指BeanFactory,不过ApplicationContext大体上也差不多,区别不太大。
在分析Bean的生命周期前,我们先认识系统认识下面几个接口。
- 一、 Bean对象的初始化和销毁方法
在Spring中,提供了两种操作Bean对象的**初始化(注意此时,对象已经被实例化出来了)**和销毁方式:
1、通过Bean标签中的init-method和destroy-method属性来设置初始化及销毁对象时的回调方法
2、通过@PostConstruct和@PreDestroy注解来标识初始化方法以及销毁方法。(使用注解更常用)
public class User {
private int id;
public User(){
System.out.println("User 被实例化");
}
public int getId() {
return id;
}
/**
* 自定义的初始化方法
*/
public void start(){
System.out.println("--自定义的初始化的方法--");
}
/**
* 自定义的初始化的方法
*/
@PostConstruct
public void postConstruct(){
System.out.println("...postConstruct...");
}
/**
* 销毁前回调方法
*/
@PreDestroy
public void preDestory(){
System.out.println("--preDestory---");
}
/**
* 销毁前的回调方法
*/
public void end(){
System.out.println("--end--");
}
@Override
public String toString() {
return "User [id=" + id + "]";
}
}
Spring 配置文件中配置如下
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 激活那些已经在spring容器里注册过的bean(无论是通过xml的方式还是通过package sanning的方式)上面的注解 -->
**如果不添加此处,则你的PostConstruct和PreDestroy注解都无效**
<context:annotation-config />
<bean class="com.dpb.pojo.User" id="user" init-method="start" destroy-method="end">
<property name="name" value="波波烤鸭"></property>
</bean>
</beans>
测试:
@Test
public void test1() {
ClassPathXmlApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
User user = ac.getBean(User.class);
System.out.println(user);
// 注册销毁
ac.registerShutdownHook();
}
最后的输出为,可以看到,在getBean被调用的时候,User实体先被实例化,然后调用Bean自定义的初始化方法(注解优先);容器销毁的时候先调用定义的销毁方法。
User 被实例化
...postConstruct...
--自定义的初始化的方法--
User [id=0, name=波波烤鸭, beanName=null]
--preDestory---
--end--
- 二、Spring之InitializingBean接口和DisposableBean接口
这两个接口的使用方式都要求 我们定义的Bean实现这两个接口,并实现具体的方法,因此会有耦合。
InitializingBean接口的作用在于:允许一个Bean在它的所有必须属性被BeanFactory设置后,来执行初始化的工作,这个接口里面只有一个方法:
void afterPropertiesSet() throws Exception;
DisposableBean接口的作用是:允许在容器销毁该Bean的时候获得一次回调,内部也只有一个方法
void destroy() throws Exception;
代码如下
public class User implements InitializingBean,DisposableBean{
private int id;
private String name;
public User(){
System.out.println("User 被实例化");
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("设置:"+name);
this.name = name;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", beanName=" + beanName + "]";
}
@Override
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("destory ....");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet....");
}
}
Spring配置文件如下:
<?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="com.dpb.pojo.User" id="user" >
<property name="name" value="波波烤鸭"></property>
</bean>
</beans>
测试代码:
@Test
public void test1() {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = ac.getBean(User.class);
System.out.println(user);
ac.registerShutdownHook();
}
最后输出为如下,可以看到,在Bean实例化后,相关的属性被设置后
User 被实例化
设置:波波烤鸭
afterPropertiesSet....
User [id=0, name=波波烤鸭, beanName=null]
destory ....
总结一:上述两者结合,其执行顺序为:
(1)、调用默认构造函数实例化对象—>
(2)、设置相关的必须属性—>
(3)、调用PostConstruct注解的方法—>
(4)、调用InitializingBean的afterPropertiesSet方法—>
(5)、调用init-method属性对应的自定义初始化方法—>
(6)、初始化完成—>
(7)、调用PreDestroy注解的方法—>
(8)、调用DisposableBean的destroy方法—>
(9)、调用destroy-method属性对应的自定义销毁前回调方法
-
三、Spring之Aware系列接口
- ApplicationContextAware
- BeanClassLoaderAware
- BeanFactoryAware
- BeanNameAware
可以让某些定义的Bean来实现Aware接口,这样这些Bean就可以访问Spring容器了,但和InitializingBean接口类似,会造成Bean 和Spring框架的耦合。Aware系列的接口内部都有一个setXXX的方法,方法的形参是Aware接口前面的内容。
这里我不再粘代码过来了,可以考虑让你的实体实现上面几个Aware接口,就能看见这些内容是什么时候被set到Bean中去的。 setBeanName、setBeanFactory等发生在上面步骤的(2)之后,(3)之前,即实例化对象后并设置完一些必须的属性之后,讲这些 内容放到Bean中去,然后调用PostConstruct注解修饰的方法。
- 四、Spring之InstantiationAwareBeanPostProcessor接口
这个接口是感知Bean实例化的,注意是实例化而不是初始化,它的父接口BeanPostProcessor的内部方法是感知初始化的,这是区别。 除了继承了父接口的两个方法以外,该接口额外定义了以下三个方法:
-
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
// 这个方法往往被认为是Bean的生命周期开始时,最先执行的方法(在Bean实例化之前调用),此时Bean尚未实例化,可 以返回任意类型的Object。由于此时目标对象尚未实例化,所以这个返回值可以用来代替原本应该生成的实例对象(例如创建一个代理 对象)。如果该方法的返回值不为null,即用来代替原本应该生成的实例对象,则后续只有postProcessAfterInitialization 方法会调用(注意此方法来自父接口,是初始化完毕后调用的方法),其他方法不再调用。否则按照正常流程走
-
boolean postProcessAfterInstantiation(Object bean, String beanName)
// 在目标对象实例化之后调用,此时Bean已经被实例化,但是该实例的属性尚未被设置,都是null。它的返回值是postProcess PropertyValues方法会不会调用的决定因素之一。如果该方法返回false且mbd.getDependencyCheck()返回true(表示不 需要check),则postProcessPropertyValues方法不会被调用;否则下面这个方法会执行。
-
PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
// 对属性值进行修改,当然该方法是否会执行取决于上一个方法返回的结果,可以通过如下代码拿到准备设置的属性值,并对其进行 修改。
PropertyValue value = pvs.getPropertyValue("name"); System.out.println("修改前name的值是:"+value.getValue()); value.setConvertedValue("bobo");
父接口中定义的两个方法如下(是在初始化前后执行的):
-
Object postProcessBeforeInitialization(Object bean, String beanName)
// 在Bean自定义的初始化方法之前执行,在Bean实例化及属性注入后执行的, 且在自定义的初始化方法之前执行(通过init-method指定)
-
Object postProcessAfterInitialization(Object bean, String beanName)
// 在Bean自定义的初始化方法执行完成之后执行
你可以自定义一个MyInstantiationAwareBeanPostProcessor,实现这个接口的5个方法,代码不再粘贴,观察这几个方法的执行 顺序,结合上面的执行顺序,结果如下:
(0)、调用postProcessBeforeInstantiation方法,在Bean被实例化之前—>
(1)、调用默认构造函数实例化对象—>
(2)、调用postProcessAfterInstantiation方法,此时Bean已经被实例化,但是其属性尚未被设置—>
(3)、调用postProcessPropertyValues方法,设置其相关的属性,当然是否执行取决于上一个方法的返回结果—>
(4)、设置相关的必须属性—@Deprecated>
(5)、设置Aware接口相关的内容—>
(6)、调用postProcessBeforeInitialization方法,准备开始初始化—>
(7)、调用PostConstruct注解的方法—>
(8)、调用InitializingBean的afterPropertiesSet方法—>
(9)、调用init-method属性对应的自定义初始化方法—>
(10)、调用postProcessAfterInitialization,初始化完毕—>
(11)、初始化完成—>
(12)、调用PreDestroy注解的方法—>
(13)、调用DisposableBean的destroy方法—>
(14)、调用destroy-method属性对应的自定义销毁前回调方法
- 五、Spring之BeanFactoryPostProcessor接口
这个接口的名称已经指示我们它是和BeanFactory(即Spring容器)相关的,而不是Bean相关的。Spring允许通过这个接口在 容器实例化任何Bean之前读取Bean的定义(即BeanDefinition),从而可以修改它。可以定义多个BeanFactoryPostProcessor ,通过设置order属性来确定他们的执行顺序,该接口内部只有一个方法:
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
可以自定义类实现这个接口,可能的代码如下:
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
/**
* 本方法在Bean对象实例化之前执行,
* 通过beanFactory可以获取bean的定义信息,
* 并可以修改bean的定义信息。这点是和BeanPostProcessor最大区别
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println(">> BeanFactoryPostProcessor 开始执行了");
String[] names = beanFactory.getBeanDefinitionNames();
for (String name : names) {
if("user".equals(name)){
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(name);
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
// MutablePropertyValues如果设置了相关属性,可以修改,如果没有设置则可以添加相关属性信息
if(propertyValues.contains("name")){
propertyValues.addPropertyValue("name", "bobo");
System.out.println("修改了属性信息");
}
}
}
System.out.println(">> BeanFactoryPostProcessor 执行结束");
}
}
事实上,这个方法的执行顺序甚至在postProcessBeforeInstantiation方法之前,即之前的第(0)步以前,但是就定义来讲,这个 方法不属于Bean的生命周期的一部分。因此该方法的执行顺序是在最前面的,当然你在这个方法里面增加或者修改了Bean的一些属性, 仍然可以在后续的postProcessPropertyValues方法中继续修改。
最后来一份总结吧:
1、如果实现了BeanFactoryPostProcessor接口,那么在容器启动的时候,在所有的Bean实例化之前,但是BeanDefinition对象 已经构造出来了,会执行postProcessBeanFactory方法,这个方法内部可以修改Bean中元数据的信息;
2、如果实现了InstantiationAwareBeanPostProcessor接口,那么在实例化Bean之前会调用postProcessBeforeInstantiation 方法,该方法如果不返回null则会跳过后续的一部分流程,直接调用postProcessAfterInitialization方法,此时就初始化完成。 如果返回null,则正常调用构造函数实例化对象,然后调用postProcessAfterInstantiation,这个时候对象已经被实例化,但是 属性并未设置,它的返回值决定了是否会调用postProcessPropertyValues方法,用来修改相关的属性,在初始化方法之前;
3、设置完相关的属性之后(不管是在postProcessPropertyValues方法内部修改的,还是在配置中指定的属性),如果Bean实现 了相关的Aware接口,那么相应的set方法会被执行;
4、如果实现了BeanPostProcessor接口,那么接下来会调用postProcessBeforeInitialization方法,这是在初始化之前调用;
5、如果Bean内部有使用PostConstruct注解修饰的方法,则该方法现在会被调用;
6、如果Bean实现了InitializingBean接口,则现在开始执行afterPropertiesSet方法;
7、如果指定了init-method属性,则开始执行指定的方法(自定义的初始化方法);
8、执行postProcessAfterInitialization方法,和第4个步骤对应,到此初始化过程结束;
9、初始化完成后,对于单例的Bean,容器就将这个Bean放置缓存池中,下次使用这个Bean的时候将直接从容器缓存中取出,Spring 继续对Bean后续的生命周期进行管理。如果Bean的作用域范围为prototype,则由调用者负责Bean后续的生命管理,Spring不再管 理这个Bean的生命周期。
10、当对象需要销毁时(或者说容器关闭时),最初调用的是Bean中被PreDestroy注解修饰的方法;
11、如果Bean实现了DisposableBean接口,则执行destroy方法;
12、如果在配置文件中指定了destroy-method属性,则执行指定的方法。
以上为完整的执行顺序,但是记住第一步并不属于Bean的生命周期的一部分。