这是回顾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的生命周期的一部分。