【Spring杂烩】探讨Spring中Bean的生命周期

深入了解Spring中Bean的生命周期的各个阶段


  • 前提概要
    • Bean的生命周期的定义层面
    • BeanFactory、ApplicationContext不同的Bean生命周期
    • Bean作用域于对生命周期的影响
    • Bean生命周期扩展接口、方法的分类
  • Bean的生命周期
    • 如何记住Bean生命周期的生命阶段?
    • Bean生命周期接口的分析
  • Bean生命周期的代码实现分析
    • 生命周期阶段的模拟
    • 代码模拟
  • 相关问题
    • init-method、@ProConstruct、@Bean(initMethod = “xxxMethod”)、initializingBean接口的区别
    • BeanFactoryPostProcessor的方法和InstantiationAwareBeanPostProcessor的前置方法是否有重叠实现之处?
    • BeanFactoryPostProcessor和BeanPostProcessor的区别?
    • 当Bean依赖其他Bean的时候,生命周期阶段又是怎么样执行的?
  • 参考资料

前提概要


Bean的生命周期的定义层面

在Spring中,可以从两个层面去定义Bean的生命周期:

  • 第一层面是Bean的作用范围
  • 第二层面是实例化Bean时所经历的一系列阶段

Bean的生命周期是什么意思?

意思就是说从Bean的诞生到销毁整个过程中,都会固定做哪些动作。而这些动作就构成了Bean的完整的生命周期。这些动作是有顺序的,就像是一条调用链条,对Bean进行层层加工,只有做完一个动作,才会轮到下一个动作。


BeanFactory、ApplicationContext不同的Bean生命周期

BeanFactory与ApplicationContext的Bean的生命周期基本相同,但还是有一些不一样的地方,总的来说ApplicationContext的Bean生命周期囊括了BeanFactory的Bean生命周期,所以之后的讲解,没有特殊提示,我们默认都是对ApplicationContext的Bean生命周期进行讲解

区别:
  • ApplicationContext的Bean生命周期多了一个ApplicationContextAware的阶段,就是实现了ApplicationContextAware接口,我们就可以通过setApplicationContext为该Bean获取到ApplicationContext

  • 此外,如果ApplicationContext的Bean生命周期还多了工厂后置处理器阶段(BeanFactoryPostProcessor),通过实现该接口,我们可以在Bean实例化之前,对Bean的元数据BeanDefinition进行修改

  • 另外,ApplicationContext会通过反射机制自动识别定义的BeanFacotryPostProcessorInstantiationAwareBeanPostProcessorBeanPostProcessor等Bean后置处理器,并自动将它们注册到应用上下文中。而BeanFactory则要在代码中手工调用addBeanPostProcessor()方法进行注册


Bean作用域于对生命周期的影响

Bean的作用域是会对Bean的生命周期产生部分影响的,Bean的作用域有:

作用域说明
singleton单例模式
prototype该作用域使用原型设计模式,每个请求都是一个新的Bean,而非单例
request该作用域将 bean 的定义限制为 HTTP 请求。只在 web-aware Spring ApplicationContext 的上下文中有效。
session该作用域将 bean 的定义限制为 HTTP 会话。 只在web-aware Spring ApplicationContext的上下文中有效。
global-session该作用域将 bean 的定义限制为全局 HTTP 会话。只在 web-aware Spring ApplicationContext 的上下文中有效。

我们这里只重点说明singleton和prototype的影响,因为其他的三种也不常用:
在这里插入图片描述

看图说话,我们可以看到再Bean的生命周期中,Bean已经被BeanPostProcessor处理了之后会进行准备就绪状态,而之后要看该Bean的作用域是singleton还是prototype来决定之后的流程

  • 如果是singleton,则按照正常的生命周期走完,直到被销毁
  • 如果是prototype,将不再继续Bean的生命周期,而是将其交给用户管理,不再属于Spring的管理范畴,也不会被Spring销毁,而是交给虚拟机gc

Bean生命周期扩展接口、方法的分类

这里引用《精通Spring 4.x 企业应用开发实践》书中的分类
在整个Bean生命周期阶段,我们可以大致的将扩展接口和方法分为4类:

  • 工厂后处理器接口方法
  • Bean级生命周期接口
  • 容器级生命周期接口
  • Bean自身的方法
Bean自身的方法
  • 如调用Bean构造方法实例化,调用Setter方法设置属性,以及通过<bean>的init-method , destroy-method所指定的方法
Bean级接口
  • BeanNameAwareBeanFactoryAwarInitializingBeanDisposableBean等接口,这些接口都是由当前Bean自身实现的。即某个Bean想要获取BeanFactory,则该Bean则实现BeanFactoryAware 接口,重写setBeanFactory方法。因为是当前Bean实现的,它也仅对当前的Bean生效
容器级接口(重点理解)
  • 不同于Bean级接口,容器级接口都不一样了,容器级接口,通常称为"后处理器"。后处理器接口一般不是由当前Bean去实现的,而是独立于Bean(虽然后处理器注册到容器中的形式,还是Bean),即不是由我们有需要的Bean去实现这些接口的,而是通常会找一个专门的Bean去实现这些接口,就像拦截器一样,说白了它们就是Spring提供的容器级Bean拦截器,但是他们不会拦截自己(即实现了后处理器的Bean是不会被后处理器处理的)
  • 容器级接口有InstantiationAwareBeanPostProcessorBeanPostProcessor当然BeanFactoryPostProcessor也是,但已经分类到工厂后处理器接口了
  • 所有的容器级接口在整个Spring生命周期中可以有多个实现,Spring本身也提供了多个保证自己业务执行的实现。如果我们自定义了多个相同后处理器的接口实现,我们可以通过实现Ordered接口来保证这些后处理器的的处理(拦截)顺序
工厂后处理器接口
  • 工厂后处理器接口指的就是BeanFactoryPostProcessor接口,在框架级别,Spring容器已经提供的AspectJWeavingEnabler、CustomAutowireConfigurer、ConfigurationClassPostProcessor等实现类。
  • 说白了就是Spring提供的Bean实例化前的拦截器,可以在Spring Bean实例化前进行拦截,按需对某个Bean的元数据进行修改,赋值等操作
  • 同时工厂后处理器也是容器级的生命周期接口,当有多个实现时,可以实现Ordered接口保证拦截顺序

Bean的生命周期


如何记住Bean生命周期的生命阶段?

为了方便记忆Bean生命周期已经常见的几个阶段

我将生命周期大致分为4个节点:

  • 实例化
  • 填充属性(属性注入)
  • 自定义初始化
  • 销毁

然后将Bean的生命周期扩展接口分布在4个节点前后,就形成为12个动作阶段

  • Bean实例化元数据级前置操作 | BeanFactoryPostProcessor
  • Bean实例化前置操作 | InstantiationAwareBeanPostProcessor
  • Bean实例化 | Constructor
  • Bean实例化后置操作 | InstantiationAwareBeanPostProcessor
  • Bean属性注入前拦截操作 | InstantiationAwareBeanPostProcessor
  • Bean属性注入,依赖注入 | DI
  • Bean Aware | xxxAware
  • Bean自定义初始化前置操作 | BeanPostProcessor
  • Bean自定义初始化操作 | InitializingBean/init-method/@PostConstruct
  • Bean自定义初始化后置操作 | BeanPostProcessor
  • Bean准备完毕 | 使用
  • Bean销毁前阶段 | DisposableBean/destory-method/@PreDestroy

所以我们知道,这里的12个生命周期动作阶段都是在4个生命周期节点前后插入的操作
当然实际的动作肯定不仅仅只有这一些,我们这里是只是指出主要的扩展接口。


Bean生命周期接口的分析

在这里插入图片描述
图来源于网上,这跟《精通Spring 4.x 企业应用开发实践》书中的图是一样的

我们这里就根据图中的流程重点讲解6个部分

  • BeanFactoryPostProcessor
  • InstantiationAwareBeanPostProcessor
  • XXXAware
  • BeanPostProcessor
  • 自定义初始化逻辑
  • 自定义销毁前逻辑

BeanFactoryPostProcessor

BeanFactoryPostProcessor可以说是Spring内置的Bean生命周期容器级拦截器,它的原理跟BeanPostProcessor基本相同,但并不是继承至BeanPostProcessor接口。它是也是一个顶级接口

实现它,我们只要重写一个方法postProcessBeanFactory(), 这个方法的调用时机是在Bean实例化之前调用,是可以对Bean的元数据BeanDefinition进行修改操作的。即在Bean未实例化前就可以对Bean的元数据进行修改。

因为BeanFactoryPostProcessor是容器级的接口,所以通常我们需要一个类去实现它,重写postProcessBeanFactory()方法,根据方法传递的beanFactory去获取某个Bean的元数据,然后进行修改,但它是不会拦截自己本身的(因为通常BeanFactoryPostProcessor实现类本质也是一个Bean)

在Spring中的应用场景有:

总结:

  • 容器级接口
  • 一个实现类只会执行一次postProcessBeanFactory()方法,按需修改
  • 调用时机在实例化前,可以修改Bean元数据
  • 多个实现类可以通过Order保证顺序

InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor也是一个容器级接口,虽然继承于BeanPostProcessor,但却是不竟相同的,InstantiationAwareBeanPostProcessor主要是在Bean实例化前后以及属性注入前执行的拦截操作

// postProcessBeforeInstantiation方法的作用在目标对象被实例化之前调用的方法,可以返回目标实例的一个代理用来代替目标实例,该方法在Bean类加载前执行
// beanClass参数表示目标对象的类型,beanName是目标实例在Spring容器中的name
// 返回值类型是Object,如果返回的是非null对象,接下来除了postProcessAfterInitialization方法会被执行以外,其它bean构造的那些方法都不再执行。否则那些过程以及postProcessAfterInitialization方法都会执行
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;

// postProcessAfterInstantiation方法的作用在目标对象被实例化之后并且在属性值被populate之前调用
// bean参数是目标实例(这个时候目标对象已经被实例化但是该实例的属性还没有被设置),beanName是目标实例在Spring容器中的name
// 返回值是boolean类型,如果返回true,目标实例内部的返回值会被populate,否则populate这个过程会被忽视
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;

// postProcessPropertyValues方法的作用在属性中被设置到目标实例之前调用,可以修改属性的设置
// pvs参数表示参数属性值(从BeanDefinition中获取),pds代表参数的描述信息(比如参数名,类型等描述信息),bean参数是目标实例,beanName是目标实例在Spring容器中的name
// 返回值是PropertyValues,可以使用一个全新的PropertyValues代替原先的PropertyValues用来覆盖属性设置或者直接在参数pvs上修改。如果返回值是null,那么会忽略属性设置这个过程(所有属性不论使用什么注解,最后都是null)
PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
    throws BeansException;

在Spring中的应用场景有:

要注意的点有:

  • postProcessBeforeInstantiation方法是在Bean的类加载前执行的,所以可以看到方法传入的不是当前Bean
  • postProcessPropertyValues方法时在Spring属性注入到Bean前执行的,但是在方法中对PropertyValues参数的修改会直接影响属性注入的结果,因为属性注入就是基于PropertyValues的值去注入的
  • 如果postProcessAfterInstantiation返回false,则不会执行postProcessPropertyValues方法

小结:

  • 容器级接口
  • 同一个实现类的同一个方法,Spring有多少Bean,就会拦截多少次
  • 调用时机在实例化前后以及属性注入前
  • 多个实现类,可以通过Order保证顺序

BeanPostProcessor

BeanPostProcessor通常称为后处理器,是一个容器级接口,它的方法则主要是在Bean实例化和属性注入之后,自定义初始化前后执行的拦截操作,有两个方法:

	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

before是自定义初始化前,after是自定义初始化后,同时我们可以看到传入的参数都是当前拦截的Bean实例对象,可以在方法中直接对它们进行修改操作。

BeanPostProcessor在Spring框架中占有重要的地位,为容器提供对Bean后续加工处理的切入点,Spring容器所提供的各种神奇的功能“AOP,动态代理”都是通过BeanPostProcessor实施的

小结:

  • 容器级接口
  • 同一个实现类的同一个方法,Spring有多少Bean,就会拦截多少次
  • 调用时间再实例化,属性注入,Aware之后;自定义初始化前后
  • 多个实现类可以使用Order保证顺序

XXXAware

基本上Aware结尾的都是为Bean的生命周期中加上一个某个东西的引用,就像附装插件一样。Spring中 也提供了多种的Aware实现

Aware是Bean级的生命周期接口,所以只有当有需要的Bean才会去实现他们,所以他们不会影响到其他没有实现该接口的Bean。

当我们实现了ApplicationContextAware接口,按我们就要实现setApplicationContext()方法

void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

通过该方法,我们从参数中的到传入的上下文,并赋值给成员属性,那么我这个Bean就带有上下文的引用了。其他的Aware也类似。

小结:

  • Bean级接口,按需实现
  • 通常是为了让Bean持有某个东西的引用
  • 调用时机是属性注入之后执行

自定义初始化逻辑
InitializingBean

InitializingBean是Bean级接口,作用是在Bean实例化且被填充了属性之后,Bean准备好之前的自定义初始化操作,只有一个方法要实现

void afterPropertiesSet() throws Exception;
  • The org.springframework.beans.factory.InitializingBean interface lets a bean perform initialization work after the container has set all necessary properties on the bean. The InitializingBean interfacespecifies a single method:

  • We recommend that you do not use the InitializingBean interface, because it unnecessarily couples the code to Spring. Alternatively, we suggest using the @PostConstruct annotation or specifying a POJO initialization method. In the case of XML-based configuration metadata, you can use the init-method attribute to specify the name of the method that has a void no-argument signature. With Java configuration, you can use the initMethod attribute of @Bean.

对InitializingBean的解释,我们引用官方文档的解释:

  • InitializingBean 是一个实例化回调(Initialization Callbacks),通过实现InitializingBean接口,我们可以在Spring容器为Bean填充了必要的属性之后,自定义一些初始化后逻辑。
  • 但是官方不推荐我们在项目中使用InitializingBean 接口,而是使用@PostConstruct注解或者通过xml方式的init-method指定bean本身的某个初始化方法。因为使用了InitializingBean
    接口,会让我们的代码跟Spring耦合在一起。
init-method属性

init-method属性指定的方式是Bean自身的方法,作用就是自定义初始化操作,作用类似于InitializingBean,但是比InitializingBean更晚执行

小贴士:
详细可以看文章下面的相关问题章节的init-method、@ProConstruct、@Bean(initMethod = “xxxMethod”)、initializingBean接口的区别


自定义销毁前逻辑
DisposableBean

DisposableBean是Bean级接口,作用是当容器关闭,触发Bean的销毁,则会销毁前执行的逻辑,只有一个方法要实现

	void destroy() throws Exception;
  • Implementing the org.springframework.beans.factory.DisposableBean interface lets a bean get a callback when the container that contains
    it is destroyed. The DisposableBean interface specifies a single
    method:

  • We recommend that you do not use the DisposableBean callback interface, because it unnecessarily couples the code to Spring.
    Alternatively, we suggest using the @PreDestroy annotation or
    specifying a generic method that is supported by bean definitions.
    With XML-based configuration metadata, you can use the destroy-method
    attribute on the . With Java configuration, you can use the
    destroyMethod attribute of @Bean.

对于DisposableBean,我们也引用官方文档的解释:

  • DisposableBean是一个销毁回调(Destruction Callbacks),实现该接口可以让容器关闭的时候,让该Bean获得一个方法回调去做一些事情
  • 同样,我们也不推荐使用DisposableBean回调接口,而是使用@PreDestroy注解方式xml方式destroy-method属性、Java Configuration方式的@Bean的destroyMethod属性去代替。这样可以减少项目代码与Spring的耦合
destroy-method

destroy-method指定的方式是Bean自身的方法,作用是自定义的Bean销毁前操作,是Bean生命周期中最后一个执行的操作

小贴士:
详细可以看文章下面的相关问题章节的init-method、@ProConstruct、@Bean(initMethod = “xxxMethod”)、initializingBean接口的区别


Bean生命周期的代码实现分析


生命周期阶段的模拟

我们这里使用5个类来初步模拟一下Bean的生命周期的各个阶段:

前提声明:这里使用了lombok插件,强烈推荐使用。如果没有使用的小伙伴,可以把get/set方法加上,把log方法换成传统实现或者使用System.out.println输出

  • Hello类
  • HelloBeanPostProcessor类
  • HelloBeanFactoryPostProcessor类
  • HelloInstantiationAwareBeanPostProcessor类
  • SpringBoot启动引导类
模拟了什么?
  • 我们这里模拟了生命周期中各阶段的实现顺序,验证生命周期的流程
  • 同时这里模拟了BeanFactoryPostProcessor工厂后处理器修改Bean元数据的操作
  • 模拟了InstantiationAwareBeanPostProcessor是如何在bean注入属性阶段修改属性的操作
  • 模拟了手动关闭Spring容器,触发Bean消耗的操作
模拟流程图

因为位置有限,只能省略了部分东西
在这里插入图片描述

代码模拟

hello类
@Slf4j
@Data
@Component
public class Hello implements InitializingBean, DisposableBean {


    /**
     * 静态代码块,类加载时输出
     */
    static { log.warn("【static】 Hello class was loading"); }

    /**
     * 成员属性
     */
    public String helloworld = "helloworld";

    /**
     * 无参构造函数
     */
    public Hello() {
        log.warn("【Constructor】 - initialization ");
    }

    /**
     * 自定义Bean初始化方法
     */
    @PostConstruct
    public void postInit() {
        log.warn("【PostConstruct】 - postInit");
    }

    /**
     * 自定义Bean注销方法
     */
    @PreDestroy
    public void postDestroy(){
        log.warn("【PreDestroy】 - postDestroy");
    }


    /**
     * 实现InitializingBean的方法,自定义初始化逻辑
     *
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        log.warn("【InitializingBean】 - afterPropertiesSet");
    }

    /**
     * 实现DisposableBean的方法,自定义销毁逻辑
     * @throws Exception
     */
    @Override
    public void destroy() throws Exception {
        log.warn("【DisposableBean】 - destroy");
    }
}

  • 这是我们要进行模拟生命周期的Bean
  • 实现了InitializingBean和DisposableBean 接口,提供了的初始化后和销毁前的自定义逻辑
  • 使用了注解方式的@PosConstruct和@PreDestroy,用于对比InitializingBean和DisposableBean
  • 实现了类加载时输出和构造实例时输出
HelloBeanFactoryPostProcessor
/**
 * 工厂后处理器
 */
@Component
@Slf4j
public class HelloBeanFactoryPostProcessor implements BeanFactoryPostProcessor {


    /**
     * 可以对bean实例化前,通过Ioc容器获取到bean的元数据,并对其元数据进行修改
     * @param beanFactory
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //通过ioc容器,获取com.example.demo3.bean.Hello类的元数据
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("hello");

        //从元数据中获得该Bean的成员参数(成员属性...)
        MutablePropertyValues mutablePropertyValues = beanDefinition.getPropertyValues();

        //修改元数据中的属性
        mutablePropertyValues.add("helloworld","HelloBeanFactoryPostProcessor - helloworld");
        log.warn("【HelloBeanFactoryPostProcessor】 - postProcessBeanFactory");
    }
}

  • 这是自定义的工厂后处理器,只有一个postProcessBeanFactory方法。从流程图中,我们知道,它是第一个回调的地方
  • 我们在postProcessBeanFactory方法中模拟修改Hello Bean的元数据信息,原本Hello类的helloworld属性的值是helloworld。我们把它的元数据记录的helloworld属性修改成HelloBeanFactoryPostProcessor - helloworld,看是否会影响该Bean的实例化
HelloInstantiationAwareBeanPostProcessor

@Slf4j
@Component
public class HelloInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {


    /**
     * postProcessBeforeInstantiation方法的作用在目标对象被实例化之前调用的方法,可以返回目标实例的一个代理用来代替目标实例
     * 相比BeanFacotoryPostProcessor的方法,都是在实例化前调用,但是这里不能操作bean的元数据(BeanDefinition)
     * 返回值类型是Object,如果返回值的是非null对象,那么接下来只有postProcessAfterInitialization方法会被执行
     * 返回值如果是null,剩下的过程都会执行
     *
     * @param beanClass 目标对象的类型
     * @param beanName 目标实例在Spring容器中的name
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {

        if ("hello".equals(beanName)) {
            log.warn("【HelloInstantiationAwareBeanPostProcessor】 - before");
            return null;
        }
        return null;
    }

    /**
     * postProcessAfterInstantiation方法的作用在目标对象被实例化之后并且在属性值被populate之前调用
     *
     * @param bean   拦截的bean实体
     * @param beanName 目标bean的id
     * @return
     * @throws BeansException
     */
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if ("hello".equals(beanName)) {
            log.warn("【HelloInstantiationAwareBeanPostProcessor】 - after");
            return true;
        }
        return true;
    }

    /**
     * postProcessPropertyValues方法的作用在属性中注入到目标实例之前调用,可以修改属性的设置
     * 虽然是在注入属性前修改的属性,但最终的情况仍然是以本次修改为结果,即后面发生的注入行为是根据PropertyValues 属性进注入的
     *
     * @param pvs bean元数据中的MutablePropertyValues
     * @param bean 当前实例化好的对象
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if ("hello".equals(beanName)) {

            //PropertyValues的修改才会呈现最后效果的
            ((MutablePropertyValues)pvs).addPropertyValue("helloworld","postProcessProperties - helloworld");
/*            ((MutablePropertyValues)pvs).addPropertyValue("name",new Name("hhhh"));*/

            //bean的修改是没有任何效果的
            ((Hello)bean).setHelloworld("helloworld");
            log.warn("【HelloInstantiationAwareBeanPostProcessor】 - postProcessProperties");

            return pvs;
        }
        return pvs;
    }
}
  • 这是HelloInstantiationAwareBeanPostProcessor 的实现类,它有三个方法
  • 一个是在实例化前操作,另一个是在实例化后操作。还有一个是在填充属性前操作
  • 这里只处理名字为hello的Bean,其他的Bean直接放行
HelloBeanPostProcessor

/**
 * 后处理器
 */
@Component
@Slf4j
public class HelloBeanPostProcessor implements BeanPostProcessor {

    /**
     * 在bean实例化、属性注入之后,自定义初始化操作之前进行,可以对已经实例化好的bean进行修改
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("hello".equals(beanName)){
            log.warn("【HelloBeanPostProcessor】 - before");
            return bean;
        }
        return bean;
    }


    /**
     * 在bean实例化、属性注入、自定义初始化操作之后进行,可以对自定义初始化后的bean进行修改
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if ("hello".equals(beanName)){
            log.warn("【HelloBeanPostProcessor】 - after");
            return bean;
        }
        return bean;
    }
}

  • 这是后处理器的实现类,它有两个方法
  • 一个是在自定义初始化逻辑前执行,另一个实在自定义初始化后执行
  • 这里只处理名字为hello的Bean,其他的Bean直接放行
SpringBoot启动引导类
@Slf4j
@SpringBootApplication
public class Demo3Application {

    public static void main(String[] args) {
    	//获取Spring应用上下文
        ApplicationContext applicationContext = SpringApplication.run(Demo3Application.class, args);
        
        //获取Hello在Spring容器中的单例对象
        Hello hello = applicationContext.getBean(Hello.class);
        
        //输出修改后的hello对象
        log.warn(hello.toString());
		
		//关闭Spring容器,为了触发Bean的注销
        ((ConfigurableApplicationContext) applicationContext).close();
    }

}

  • 启动类中,我们会获取到Spring应用上下文,并通过上下文获取容器中的Hello bean,把他输出处理,查看变化
  • 我们还启动类中通过上下文来关闭容器,模拟触发Bean销毁
结果
2019-01-09 11:32:12.660 【HelloBeanFactoryPostProcessor】 - postProcessBeanFactory
2019-01-09 11:32:13.019 【HelloInstantiationAwareBeanPostProcessor】 - before
2019-01-09 11:32:13.019static】 Hello class was loading
2019-01-09 11:32:13.019 【Constructor】 - initialization 
2019-01-09 11:32:13.019 【HelloInstantiationAwareBeanPostProcessor】 - after
2019-01-09 11:32:13.019 【HelloInstantiationAwareBeanPostProcessor】 - postProcessProperties
2019-01-09 11:32:13.019 【HelloBeanPostProcessor】 - before
2019-01-09 11:32:13.019 【PostConstruct】 - postInit
2019-01-09 11:32:13.019 【InitializingBean】 - afterPropertiesSet
2019-01-09 11:32:13.019 【HelloBeanPostProcessor】 - after
2019-01-09 11:32:13.270  Hello(helloworld=postProcessProperties - helloworld)
2019-01-09 11:32:13.270 【PreDestroy】 - postDestroy
2019-01-09 11:32:13.270 【DisposableBean】 - destroy

从控制台输出的结果我们可以看到:

1. BeanFactoryPostProcessor 工厂后处理器最早执行,而且比第二执行的早了整整1.5s
2. InstantiationAwareBeanPostProcessor的前置操作第二执行,甚至比Bean的类加载还早
3. 类加载第三执行
4. 构造器第四执行
5. InstantiationAwareBeanPostProcessor的后置操作第五执行
6. InstantiationAwareBeanPostProcessor属性修改第六执行
7. Spring的属性注入第七执行(当然,从输出中,是看不到的)
8. BeanPostProcessor后处理器前置操作第八执行
9. @PostConstruct注解指定的初始化方法第九执行
10. InitializingBean的自定义初始化第十执行
11. BeanPostProcessor后处理器的后置操作第十一执行
12. 然后Bean进行准备就绪状态,我们将它输出,发现属性helloworld的确在工厂后处理器中被修改了,后面模拟了关闭容器
13. 容器关闭,首先触发@PreDestroy注解指定的方法,第十二执行
14. DisposableBean 的销毁前操作最后执行

所以我们能够得出重点结论:

  • BeanFactoryPostProcessor 最早被执行,而且早非常多
  • InstantiationAwareBeanPostProcessor的前置操作比类加载还早执行
  • @PostConstruct初始化比InitializingBean方式更早执行

相关问题


init-method、@ProConstruct、@Bean(initMethod = “xxxMethod”)、initializingBean接口的区别

其实这是不仅仅是讨论init,也会说道destory回调。总是在Spring的看法中,本小结所说的方法都是Bean生命周期中发生在初始化和销毁阶段的一些回调方法,只不过,Spring可以支持多种形式的表现形式:

  • 接口方式 | InitializingBean接口 ,DisposableBean 接口
  • Xml方式 | init-method 属性,destroy-method属性
  • Java 配置 | @Bean(initMethod = “” , destroyMethod= “”)
  • 注解方式 | @PostConstruct、@PreDestroy

按Spring的说法,为了减少代码与Spring的耦合,官方是不推荐使用Spring提供的回调接口InitializingBean。而是推荐使用注解,Xml或者Java配置的方式去指定Bean的特定方法去实现

注解方式 | @PostConstruct、@PreDestroy

注解的方式的本质是通过后处理器(InitDestroyAnnotationBeanPostProcessor)去实现的

InitDestroyAnnotationBeanPostProcessor后处理器负责对标记了@PostConstruct、@PreDestroy注解的Bean进行拦截处理,在Bean初始化后和销毁前执行注解指定的相应逻辑

    /**
     * 自定义Bean初始化方法
     */
    @PostConstruct
    public void init() {
        log.warn("hello PostConstruct init");
    }

    /**
     * 自定义Bean注销方法
     */
    @PreDestroy
    public void destroy(){
        log.warn("hello PreDestroy destroy");
    }

Xml方式 | init-method 属性,destroy-method属性

Xml的方式目前一般发生在旧项目中,是曾经常见的使用方式,它可以在xml配置中指定某个Bean的初始化和销毁方法

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="destory"/>
Java 配置 | @Bean(initMethod = “” , destroyMethod= “”)

Java配置方式通常是用于通过@Bean方式注册到Spring容器中Bean中,它相比注解方式更加的类似Xml方式,可以说是Xml方式的替代品。

public class Hello{

 	public void beanInit(){
        log.warn("hello Bean init");
        
    }
	public void beanDestory(){
        log.warn("hello bean destory");
    }
}

@Configuration
public class HelloConfig{

@Configuration
public class HelloConfig {

    @Bean(initMethod = "beanInit",destroyMethod = "beanDestory")
    public Hello hello(){
        return new Hello();
    }
    
}


如果它们同时存在,那么他们的执行顺序是怎么样的呢?

这里我还真测试了一下,但是没有测试Xml方式,因为嫌麻烦,同时根据下面官方文档的描述,我们也可以知道:

Multiple lifecycle mechanisms configured for the same bean, with different initialization methods, are called as follows:

  1. Methods annotated with @PostConstruct

  2. afterPropertiesSet() as defined by the InitializingBean callback interface

  3. A custom configured init() method

Destroy methods are called in the same order:

  1. Methods annotated with @PreDestroy

  2. destroy() as defined by the DisposableBean callback interface

  3. A custom configured destroy() method

翻译如下:如多种生命周期机制实现在同一个Bean中,辣么顺序如下:
自定义初始化方法的顺序:

  • @PostConstruct
  • InitializingBean
  • init方法(@Bean或Xml)

自定义的销毁方法的顺序:

  • @PreDestory
  • @DisposableBean
  • destroy方法(@Bean或Xml)

BeanFactoryPostProcessor的方法和InstantiationAwareBeanPostProcessor的前置方法是否有重叠实现之处?

非也,BeanFactoryPostProcessor主要是对Bean的元数据进行操作;而InstantiationAwareBeanPostProcessor的前置方法虽然也是在实例化前操作,但是它并不修改元数据


BeanFactoryPostProcessor和BeanPostProcessor的区别?

BeanFactoryPostProcessor和BeanPostProcessor都是容器级接口,但是BeanFactoryPostProcessor本质不会去拦截所有的Bean,而是通过beanFactory按需获取想要更改的Bean元数据,然后进行修改。

  • 即一个BeanFactoryPostProcessor只会执行一次,而一个BeanPostProcessor实现类则会执行很多次(Spring容器中有多少个Bean,它就会执行多少次),即BeanFactoryPostProcessor是与Bean无相关的,这是他们之间的一个很重要的区别
  • 通常BeanFactoryPostProcessor是用于在实例化前修改Bean的元数据的(修改Bean的蓝图)。而BeanPostProcessor通常是对实例化后,获取Bean进行操作的,即为Bean的后续提供加工处理的切入点(当然不一定是实例化后,也可以实例化前,但不操作蓝图)

当Bean依赖其他Bean的时候,生命周期阶段又是怎么样执行的?

没有存在依赖,就会像上面所说的一样,按顺序执行每一个阶段。如果Bean之间有相互依赖呢?
我们假设一个场景:A依赖B

public class A{
	@Autowired
	public B b;
}

分两种情况,因为谁先加载的问题就是随机的:

  • B先与A被虚拟机加载
  • A先于B被虚拟机加载
B先与A被虚拟机加载

因为被依赖方先加载,所以就是个正常顺序流程,没什么好说的

A先于B被虚拟机加载
1.  【(A)InstantiationAwareBeanPostProcessor】 - before
2.  【(A)static 】 A class was loading
3.  【(A)Constructor】- initialization 
4.  【(A)InstantiationAwareBeanPostProcessor】 - after
5.  【(A)InstantiationAwareBeanPostProcessor】 - postProcessProperties
6.  A类属性注入触发B类的加载
7.  【(B)InstantiationAwareBeanPostProcessor】 - before
8.  【(B)static 】 B class was loading
9.  【(B)Constructor】- initialization 
10. 【(B)InstantiationAwareBeanPostProcessor】 - after 
11. 【(B)InstantiationAwareBeanPostProcessor】 - postProcessProperties
12. 【(B)BeanPostProcessor】 - before
13. 【(B)InitializingBean】 - afterPropertiesSet
14. 【(B)BeanPostProcessor】 - after
15.  B类Bean进入就绪状态,A类Bean属性注入完成
16. 【(A)BeanPostProcessor】 - before
17. 【(A)InitializingBean】 - afterPropertiesSet
18. 【(A)BeanPostProcessor】 - after
19. A类Bean就绪

因为当A开始Bean的生命周期时,被依赖的B有可能还没有进入就绪状态,甚至还没有进行类的加载。如果此时A已经进入到属性注入阶段,那么这就会迫使触发B的类加载进程


Bean生命周期的属性注入阶段囊括了什么呢?什么是属性注入?

也许,你刚刚开始了解到Bean生命周期的属性注入阶段时(有的也称数据填充阶段),很容易就会联系到Spring的依赖注入,然后将它们联系起来。嗯 , maybe ~

所以本节的重要就是我们得了解这个属性注入阶段,到底做了什么?是否跟我们脑子里想的属性注入的意思一致呢?

属性注入阶段跟依赖注入有关系吗?

  • 关系是有的,但该阶段不能囊括所有的依赖注入方式。

属性注入阶段做了什么?

  • Bean实例化后,到达属性注入阶段,曾经Xml方式的手动注入的属性值在此阶段会被注入Bean中
  • 自动依赖注入(@Autowired)的属性注入设值注入的方式会在此阶段将所依赖的Bean注入

要注意的地方:

  • 类的成员属性是在实例化时就已经初始化好了,所以在属性注入之前就已经完成属性初始化了,即构造方法的属性初始化已该阶段无关
  • 自动依赖注入的构造注入的方式也与属性注入阶段无关,在属性注入之前的实例化阶段就会判断构造方法中是否有@Autowired注解,有的话就会调用特殊方法完成依赖注入。与属性注入阶段无关

参考资料


  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值