深入理解 Spring Bean 生命周期

一、Bean 生命周期

Spring Bean 的生命周期管理比较复杂,正确理解 Spring Bean 的生命周期非常重要,因为有时我们需要利用 Spring 提供的扩展点来自定义 Bean 的创建过程。《Spring 实战(第四版)》[1] 中详细描述了 Bean 装载到 Spring 应用上下文中的典型的生命周期过程。
在这里插入图片描述
如上图所示,在 Bean 准备就绪之前,Bean 工厂执行了若干启动步骤。详细描述如下:

  1. Spring 对 Bean 进行实例化;
  2. Spring 将值和 Bean 的引用注入到 Bean 对应的属性中;
  3. 如果 Bean 实现了 BeanNameAware 接口,Spring 将 Bean 的 ID 传递给setBeanName()方法;
  4. 如果 Bean 实现了 BeanFactoryAware 接口,Spring 将调用 setBeanFactory()方法,将 BeanFactory 容器实例传入;
  5. 如果 Bean 实现了 ApplicationContextAware 接口,Spring 将调用 Bean 的 setApplicationContext()方法,将 Bean 所在应用上下文的引用传入进来;
  6. 如果 Bean 实现了 BeanPostProcessor 接口,Spring 将调用它们的 postProcessBeforeInitialization()方法;
  7. 如果 Bean 实现了 InitializingBean 接口,Spring 将调用他们的 afterPropertiesSet()方法。类似地,如果 Bean 使用init-method 声明了初始化方法,该方法也会被调用;
  8. 如果 Bean 实现了 BeanPostProcessor 接口,Spring 将调用他们的 postProcessAfterInitialization()方法;
  9. 此时,Bean 已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到应用上下文被销毁;
  10. 如果 Bean 实现了 DisposableBean 接口,Spring 将调用它的 destory()接口方法。同样,如果 Bean 使用了destory-method 声明了销毁方法,该方法也会被调用。

文章 [2] 中把 Spring Bean 的生命周期分为四个阶段,其中实例化和属性赋值对应构造方法和 setter 方法的注入,初始化和销毁是用户能自定义扩展的两个阶段。

a.实例化 Instantiation 对应上面的步骤 1
b.属性赋值 Populate 对应上面的步骤 2
c.初始化 Initialization 对应上面的步骤 3-8
c.销毁 Destruction 对应上面的步骤 10

实例化 -> 属性赋值 -> 初始化 -> 销毁。

主要逻辑都在 doCreate() 方法中,逻辑很清晰,就是顺序调用以下三个方法,这三个方法与三个生命周期阶段一一对应。

createBeanInstance() -> 实例化
populateBean() -> 属性赋值
initializeBean() -> 初始化

源码如下,能证明实例化,属性赋值和初始化这三个生命周期的存在。关于本文的Spring 源码都将忽略无关部分,便于理解:

// 忽略了无关代码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (instanceWrapper == null) {
       // 实例化阶段!
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
       // 属性赋值阶段!
      populateBean(beanName, mbd, instanceWrapper);
       // 初始化阶段!
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   }

至于销毁,是在容器关闭时调用的,详见ConfigurableApplicationContext#close()

二、常用扩展点

文章 [2] 中分2大类常用扩展点:影响多个 Bean 的接口只调用一次的接口

影响多个 Bean 的接口

实现了这些接口的 Bean 会切入到多个 Bean 的生命周期中。正因为如此,这些接口的功能非常强大,Spring 内部扩展也经常使用这些接口,例如自动注入以及 AOP 的实现都和他们有关。

  • BeanPostProcessor
  • InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor 作用于实例化阶段的前后,BeanPostProcessor作用于初始化阶段的前后。正好和第一、第三个生命周期阶段对应。

通过图能更好理解:
在这里插入图片描述
InstantiationAwareBeanPostProcessor 实际上继承了 BeanPostProcessor 接口,严格意义上来看他们不是两兄弟,而是两父子。但是从生命周期角度我们重点关注其特有的对实例化阶段的影响,图中省略了从 BeanPostProcessor 继承的方法。

InstantiationAwareBeanPostProcessor extends BeanPostProcessor

InstantiationAwareBeanPostProcessor源码分析:

  • postProcessBeforeInstantiation调用点,忽略无关代码:
@Override
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {

        try {
            // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
            // postProcessBeforeInstantiation方法调用点,这里就不跟进了,
            // 有兴趣的同学可以自己看下,就是for循环调用所有的InstantiationAwareBeanPostProcessor
            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
            if (bean != null) {
                return bean;
            }
        }
        
        try {   
            // 上文提到的doCreateBean方法,可以看到
            // postProcessBeforeInstantiation方法在创建Bean之前调用
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isTraceEnabled()) {
                logger.trace("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
        }
        
    }

可以看到,postProcessBeforeInstantiation 在 doCreateBean 之前调用,也就是在 bean 实例化之前调用的,英文源码注释解释道该方法的返回值会替换原本的 Bean 作为代理,这也是 AOP 等功能实现的关键点。

  • postProcessAfterInstantiation调用点,忽略无关代码:
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {

   // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
   // state of the bean before properties are set. This can be used, for example,
   // to support styles of field injection.
   boolean continueWithPropertyPopulation = true;
    // InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation()
    // 方法作为属性赋值的前置检查条件,在属性赋值之前执行,能够影响是否进行属性赋值!
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
               continueWithPropertyPopulation = false;
               break;
            }
         }
      }
   }

   // 忽略后续的属性赋值操作代码
}

可以看到该方法在属性赋值方法内,但是在真正执行赋值操作之前。其返回值为boolean,返回 false 时可以阻断属性赋值阶段(continueWithPropertyPopulation = false;)。

关于 BeanPostProcessor 执行阶段的源码穿插在下文 Aware 接口的调用时机分析中,因为部分 Aware 功能的就是通过他实现的! 只需要先记住 BeanPostProcessor 在初始化前后调用就可以了。

只调用一次的接口

这一大类接口的特点是功能丰富,常用于用户自定义扩展。
第二大类中又可以分为两类:

  1. Aware类型的接口

Aware 类型的接口的作用就是让我们能够拿到 Spring 容器中的一些资源。基本都能够见名知意,Aware 之前的名字就是可以拿到什么资源,例如 BeanNameAware 可以拿到BeanName,以此类推。调用时机需要注意:所有的 Aware 方法都是在初始化阶段之前调用的!

Aware 接口众多,这里同样通过分类的方式帮助大家记忆。
Aware 接口具体可以分为两组。如下排列顺序同样也是Aware接口的执行顺序,能够见名知意的接口不再解释。

Aware Group1

a. BeanNameAware
b. BeanClassLoaderAware
c. BeanFactoryAware

Aware Group2

a. EnvironmentAware
b. EmbeddedValueResolverAware 这个知道的人可能不多,实现该接口能够获取Spring EL解析器,用户的自定义注解需要支持 spel 表达式的时候可以使用,非常方便。
c. ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware) 这几个接口可能让人有点懵,实际上这几个接口可以一起记,其返回值实质上都是当前的 ApplicationContext 对象,因为 ApplicationContext 是一个复合接口:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}

Aware 调用时机源码分析

详情如下,忽略了部分无关代码。代码位置就是我们上文提到的 initializeBean 方法详情,这也说明了Aware都是在初始化阶段之前调用的!

// 见名知意,初始化阶段调用的方法
    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {

        // 这里调用的是Group1中的三个Bean开头的Aware
        invokeAwareMethods(beanName, bean);

        Object wrappedBean = bean;
        
        // 这里调用的是Group2中的几个Aware,
        // 而实质上这里就是前面所说的BeanPostProcessor的调用点!
        // 也就是说与Group1中的Aware不同,这里是通过BeanPostProcessor(ApplicationContextAwareProcessor)实现的。
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        // 下文即将介绍的InitializingBean调用点
        invokeInitMethods(beanName, wrappedBean, mbd);
        // BeanPostProcessor的另一个调用点
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

        return wrappedBean;
    }

可以看到并不是所有的Aware接口都使用同样的方式调用。Bean××Aware 都是在代码中直接调用的:

private void invokeAwareMethods(final String beanName, final Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof BeanNameAware) {
				((BeanNameAware) bean).setBeanName(beanName);
			}
			if (bean instanceof BeanClassLoaderAware) {
				ClassLoader bcl = getBeanClassLoader();
				if (bcl != null) {
					((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
				}
			}
			if (bean instanceof BeanFactoryAware) {
				((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
			}
		}
	}

而 ApplicationContext 相关的 Aware 都是通过BeanPostProcessor#postProcessBeforeInitialization()实现的。感兴趣的可以自己看一下ApplicationContextAwareProcessor 这个类的源码,就是判断当前创建的 Bean 是否实现了相关的 Aware 方法,如果实现了会调用回调方法将资源传递给 Bean。

至于 Spring 为什么这么实现,应该没什么特殊的考量。也许和 Spring 的版本升级有关。基于对修改关闭,对扩展开放的原则,Spring 对一些新的 Aware 采用了扩展的方式添加。

BeanPostProcessor 的调用时机也能在这里体现,包围住 invokeInitMethods 方法,也就说明了在初始化阶段的前后执行。

关于Aware接口的执行顺序,其实只需要记住第一组在第二组执行之前就行了。每组中各个Aware方法的调用顺序其实没有必要记,有需要的时候点进源码一看便知。

  1. 生命周期接口

至于剩下的两个生命周期接口就很简单了,实例化和属性赋值都是 Spring 帮助我们做的,能够自己实现的有初始化和销毁两个生命周期阶段。

a. InitializingBean 对应生命周期的初始化阶段,在上面源码的invokeInitMethods(beanName, wrappedBean, mbd) 方法中调用。

有一点需要注意,因为 Aware 方法都是执行在初始化方法之前,所以可以在初始化方法中放心大胆的使用 Aware 接口获取的资源,这也是我们自定义扩展 Spring 的常用方式。

除了实现 InitializingBean 接口之外还能通过注解或者 xml 配置的方式指定初始化方法,至于这几种定义方式的调用顺序其实没有必要记。因为这几个方法对应的都是同一个生命周期,只是实现方式不同,我们一般只采用其中一种方式。

b. DisposableBean 类似于InitializingBean,对应生命周期的销毁阶段,以ConfigurableApplicationContext#close()方法作为入口,实现是通过循环取所有实现了DisposableBean 接口的 Bean 然后调用其 destroy() 方法 。感兴趣的可以自行跟一下源码。

三、代码示例

参考[3],以及[2]的分析,通过示例验证 Bean 的生命周期。

Book.java

public class Book implements BeanNameAware, BeanFactoryAware, BeanClassLoaderAware,
        ApplicationContextAware, InitializingBean, DisposableBean {

    private String bookName;

    public Book() {
        System.out.println("1-----book instantiation");
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
        System.out.println("3-----set properties");
    }

    public String getBookName() {
        return bookName;
    }

    public void setBeanName(String s) {
        System.out.println("4-----setBeanName invoke");
    }

    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("5-----setBeanClassLoader invoke");
    }

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("6-----setBeanFactory invoke");
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("7-----setApplicationContext invoke");
    }

    @PostConstruct
    public void bookPostConstruct() {
        System.out.println("9-----bookPostConstruct invoke");
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("10-----afterPropertiesSet invoke");
    }

    public void myInitMethod() {
        System.out.println("11-----myInitMethod invoke");
    }

    @PreDestroy
    public void bookPreDestory() {
        System.out.println("13-----bookPreDestory invoke");
    }

    public void destroy() throws Exception {
        System.out.println("14-----destroy invoke");
    }

    public void myDestoryMethod() {
        System.out.println("15-----myDestoryMethod invoke");
    }

}

MyBeanPostProcessor.java

public class MyBeanPostProcessor implements BeanPostProcessor {

    // 容器加载的时候会加载一些其他的bean,会调用初始化前和初始化后方法
    // 这次只关注book(bean)的生命周期
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof Book) {
            System.out.println("8-----MyBeanPostProcessor.postProcessBeforeInitialization");
        }
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof Book) {
            System.out.println("12-----MyBeanPostProcessor.postProcessAfterInitialization");
        }
        return bean;
    }
}

MyInstantiationAwareBeanPostProcessor.java

public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if("book".equals(beanName)) {
            System.out.println("0-----MyInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation");
        }
        return null;
    }

    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if(bean instanceof Book) {
            System.out.println("2-----MyInstantiationAwareBeanPostProcessor.postProcessAfterInstantiation");
        }
        return true;
    }
}

SpringBeanLifecycleApplication.java

public class SpringBeanLifecycleApplication {
    public static void main(String[] args) throws InterruptedException {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-lifecycle.xml");
        Book book = (Book)context.getBean("book");
        System.out.println("Book name = " + book.getBookName());
        ((ClassPathXmlApplicationContext) context).close();

    }
}

bean-lifecycle.xml


<?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 https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 扫描bean -->
    <context:component-scan base-package="com.ding.demo"/>

    <!-- 实现了用户自定义初始化和销毁方法 -->
    <bean id="book" class="com.ding.demo.Book" init-method="myInitMethod" destroy-method="myDestoryMethod">
        <!-- 注入bean 属性名称 -->
        <property name="bookName" value="I Love Java" />
    </bean>

    <!--引入自定义的BeanPostProcessor-->
    <bean class="com.ding.demo.MyBeanPostProcessor"/>
    
    <!--引入自定义的InstantiationAwareBeanPostProcessor-->
    <bean class="com.ding.demo.MyInstantiationAwareBeanPostProcessor"/>
</beans>

运行结果:

0-----MyInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
1-----book instantiation
2-----MyInstantiationAwareBeanPostProcessor.postProcessAfterInstantiation
3-----set properties
4-----setBeanName invoke
5-----setBeanClassLoader invoke
6-----setBeanFactory invoke
7-----setApplicationContext invoke
8-----MyBeanPostProcessor.postProcessBeforeInitialization
9-----bookPostConstruct invoke
10-----afterPropertiesSet invoke
11-----myInitMethod invoke
12-----MyBeanPostProcessor.postProcessAfterInitialization
Book name = I Love Java
13-----bookPreDestory invoke
14-----destroy invoke
15-----myDestoryMethod invoke

参考文档

[1]《Spring实战(第四版)》
[2] https://www.jianshu.com/p/1dec08d290c1
[3] https://www.cnblogs.com/javazhiyin/p/10905294.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值