关于spring:一文读懂-Spring-Bean-的生命周期

41次阅读

共计 12942 个字符,预计需要花费 33 分钟才能阅读完成。

欢送大家关注我的微信公众号【老周聊架构】,Java 后端支流技术栈的原理、源码剖析、架构以及各种互联网高并发、高性能、高可用的解决方案。

一、前言

明天咱们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中常常遇到,这是失常景象。因为 Spring Bean 的生命周期是除了 IoC、AOP 几个外围概念之外最重要概念,大家务必拿下。可 Spring 源代码又比较复杂,跟着跟着就不晓得跟到哪里去了,不太好拿下呀。这倒是真的,而且网上一上来就各种贴流程源码,对初学者来说是真的一脸懵逼,就像字都看的懂,但连在一块就不晓得意思了,太绕了。

本文老周试着讲的通俗易懂些,让更多的小伙伴们轻松的读懂 Spring Bean 的生命周期,并有对它有持续钻研学习的想法,那我写此文的目标也就达到了。

咱们讲 Spring Bean 的生命周期之前先来理解两个概念:

1.1 什么是 Bean

咱们来看下 Spring Framework 的官网文档:

In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.

简而言之,bean 是由 Spring IoC 容器实例化、组装和治理的对象。

1.2 什么是 Spring Bean 的生命周期

对于一般的 Java 对象,当 new 的时候创建对象,而后该对象就可能应用了。一旦该对象不再被应用,则由 Java 主动进行垃圾回收。

而 Spring 中的对象是 bean,bean 和一般的 Java 对象没啥大的区别,只不过 Spring 不再本人去 new 对象了,而是由 IoC 容器去帮忙咱们实例化对象并且治理它,咱们须要哪个对象,去问 IoC 容器要即可。IoC 其实就是解决对象之间的耦合问题,Spring Bean 的生命周期齐全由容器管制。

二、Spring Bean 的生命周期

这里老周必须要提一下,这里咱们说的 Spring Bean 的生命周期次要指的是 singleton bean,对于 prototype 的 bean,Spring 在创立好交给使用者之后则不会再治理后续的生命周期。

咱们也来温习下 Spring 中的 bean 的作用域有哪些?

  • singleton : 惟一 bean 实例,Spring 中的 bean 默认都是单例的。
  • prototype : 每次申请都会创立一个新的 bean 实例。
  • request : 每一次 HTTP 申请都会产生一个新的 bean,该 bean 仅在以后 HTTP request 内无效。
  • session : 每一次 HTTP 申请都会产生一个新的 bean,该 bean 仅在以后 HTTP session 内无效。
  • global-session:全局 session 作用域,仅仅在基于 Portlet 的 web 利用中才有意义,Spring5 曾经没有了。Portlet 是可能生成语义代码(例如:HTML)片段的小型 Java Web 插件。它们基于 portlet 容器,能够像 servlet 一样解决 HTTP 申请。然而,与 servlet 不同,每个 portlet 都有不同的会话。

咱们晓得对于一般的 Java 对象来说,它们的生命周期就是:

  • 实例化
  • 该对象不再被应用时通过垃圾回收机制进行回收

而对于 Spring Bean 的生命周期来说:

  • 实例化 Instantiation
  • 属性赋值 Populate
  • 初始化 Initialization
  • 销毁 Destruction

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

只有四个步骤,这样拆解的话是不是感觉也不难?不像其他人写的那样间接一上来就各种 BeanPostProcessor、BeanFactoryPostProcessor 全副怼进流程里去,别说读者看着头大,本人写的可能短时间内还记得流程,隔个一段时间,你可能都不晓得本人写了个啥。

原本老周想通过 Bean 创立流程入口
AbstractApplicationContext#refresh() 办法的 finishBeanFactoryInitialization(beanFactory) 处带大家跟一下源码,想了想还是不带入过多的代码进来,间接给到最终的次要逻辑。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
    }

    if (instanceWrapper == null) {
        // 实例化阶段
        instanceWrapper = this.createBeanInstance(beanName, mbd, args);
    }

    ...

    Object exposedObject = bean;

    try {
        // 属性赋值阶段
        this.populateBean(beanName, mbd, instanceWrapper);
        // 初始化阶段
        exposedObject = this.initializeBean(beanName, exposedObject, mbd);
    } catch (Throwable var18) {...}

    ...
}

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

是不是很清新了?至于 BeanPostProcessor、BeanFactoryPostProcessor 以及其余的类,在老周看来,只不过是对主流程四个步骤的一系列扩大点而已。

三、Spring Bean 的生命周期的扩大点

Spring Bean 的生命周期的扩大点超级多,老周这里不可能全部列进去,只说外围的扩大点。这也就是为什么 Spring 的扩展性很好的起因,开了很多的口子,尽可能让某个性能高内聚松耦合,用户须要哪个性能就用哪个,而不是间接来一个大而全的货色。

3.1 Bean 本身的办法

比方构造函数、getter/setter 以及 init-method 和 destory-method 所指定的办法等,也就对应着上文说的实例化 -> 属性赋值 -> 初始化 -> 销毁四个阶段。


3.2 容器级的办法(BeanPostProcessor 一系列接口)

次要是后处理器办法,比方下图的 InstantiationAwareBeanPostProcessorBeanPostProcessor 接口办法。这些接口的实现类是独立于 Bean 的,并且会注册到 Spring 容器中。在 Spring 容器创立任何 Bean 的时候,这些后处理器都会产生作用。


3.2.1 InstantiationAwareBeanPostProcessor 源码剖析

咱们翻一下源码发现 InstantiationAwareBeanPostProcessor 是继承了 BeanPostProcessor


  • InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 调用点

Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
返回值:如果返回的不为 null,那么后续的 Bean 的创立流程【实例化、初始化 afterProperties】都不会执行,而是间接应用返回的快捷 Bean,此时的失常执行程序如下:
InstantiationAwareBeanPostProcessor 接口中的 postProcessBeforeInstantiation,在实例化之前调用。
BeanPostProcessor 接口中的 postProcessAfterInitialization,在实例化之后调用。





总之,postProcessBeforeInstantiation 在 doCreateBean 之前调用,也就是在 bean 实例化之前调用的,英文源码正文解释道该办法的返回值会替换本来的 Bean 作为代理,这也是 AOP 等性能实现的关键点。

  • InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation 调用点

boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException
失常状况下在实例化之后在执行 populateBean 之前调用
返回值:如果有指定的 bean 的时候返回 false,那么后续的属性填充和属性依赖注入【populateBean】将不会执行,同时后续的 postProcessPropertyValues 将不会执行, 然而初始化和 BeanPostProcessor 的依然会执行。

public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
实例化之后调用,在办法 applyPropertyValues【属性填充】之前
返回值:如果返回 null,那么将不会进行后续的属性填充,比方依赖注入等,如果返回的 pvs 额定的增加了属性,那么后续会填充到该类对应的属性中。
pvs:PropertyValues 对象,用于封装指定类的对象,简略来说就是 PropertyValue 的汇合,外面相当于以 key-value 模式寄存类的属性和值。
pds:PropertyDescriptor 对象数组,PropertyDescriptor 相当于存储类的属性,不过能够调用 set,get 办法设置和获取对应属性的值。


3.2.2 BeanPostProcessor 源码剖析

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean


进入初始化接口:


咱们先来看

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization

  • 首先获取到所有的后置处理器 getBeanPostProcessors()
  • 在 for 循环中顺次调用后置处理器的办法 processor.postProcessBeforeInitialization(result, beanName);
  • 进入 postProcessBeforeInitialization 办法

org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization

进入 invokeAwareInterfaces(bean); 办法,以后 bean 实现了 ApplicationContextAware 接口。

  • ApplicationContextAwareProcessor#postProcessBeforeInitialization 首先判断此 bean 是不是各种的 Aware,如果是它列举的那几个 Aware 就获取 Bean 工厂的权限,能够向容器中导入相干的上下文环境,目标是为了 Bean 实例可能获取到相干的上下文,如果不是它列举的几个 Aware,那就调用 invokeAwareInterfaces(bean),向容器中增加相干接口的上下文环境。

3.3 工厂后处理器办法(BeanFactoryProcessor 一系列接口)

包含 AspectJWeavingEnablerCustomAutowireConfigurerConfigurationClassPostProcessor 等。这些都是 Spring 框架中曾经实现好的 BeanFactoryPostProcessor,用来实现某些特定的性能。

咱们晓得 Spring IoC 容器初始化的关键环节就在 org.springframework.context.support.AbstractApplicationContext#refresh 办法中,容器创立的主体流程都在这个办法外面,这个办法是真的重要!!!

对于工厂后处理器办法老周这里间接带你看 invokeBeanFactoryPostProcessors(beanFactory); 办法,这个办法解决的是 BeanFactoryPostProcessor 接口的 Bean。调用办法如下:


跟到最重要的办法里去,代码虽长,但逻辑中规中矩。

BeanFactoryPostProcessor:所有解决 BeanFactory 的父接口
BeanDefinitionRegistryPostProcessor:实现了 BeanFactoryPostProcessor 接口的接口


流程阐明:

  • 调用 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry(registry) 办法。参数 beanFactoryPostProcessors 传入的优先解决掉。而后获取容器注册的,对于这些 Bean 依照 PriorityOrdered 接口、Ordered、没有排序接口的实例别离进行解决。
  • 调用 BeanFactoryPostProcessor#postProcessBeanFactory(beanFactory) 办法。备注:BeanDefinitionRegistryPostProcessor 属于 BeanFactoryPostProcessor 子接口。先解决属于 BeanDefinitionRegistryPostProcessor 接口实例的 postProcessBeanFactory(beanFactory) 办法,而后获取容器注册的。对于这些 Bean 依照 PriorityOrdered 接口、Ordered、没有排序接口的实例别离进行解决。

3.4 Bean 级生命周期办法

能够了解为 Bean 类间接实现接口的办法,比方 BeanNameAwareBeanFactoryAwareApplicationContextAwareInitializingBeanDisposableBean 等办法,这些办法只对以后 Bean 失效。

3.4.1 Aware 类型的接口

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

Aware 接口泛滥,这里同样通过分类的形式帮忙大家记忆。Aware 接口具体能够分为两组,至于为什么这么分,详见上面的源码剖析。如下排列程序同样也是 Aware 接口的执行程序,可能见名知意的接口不再解释。

Aware Group1

  • BeanNameAware
  • BeanClassLoaderAware
  • BeanFactoryAware

Aware Group2

  • EnvironmentAware
  • EmbeddedValueResolverAware
    这个晓得的人可能不多,实现该接口可能获取 Spring EL 解析器,用户的自定义注解须要反对 SPEL 表达式的时候能够应用,十分不便。
  • ApplicationContextAware(ResourceLoaderAware/ApplicationEventPublisherAware/MessageSourceAware)
    这几个接口可能让人有点懵,实际上这几个接口能够一起记,其返回值本质上都是以后的 ApplicationContext 对象,因为 ApplicationContext 是一个复合接口,如下:

Aware 调用机会源码剖析


能够看到并不是所有的 Aware 接口都应用同样的形式调用。Bean××Aware 都是在代码中间接调用的,而 ApplicationContext 相干的 Aware 都是通过 BeanPostProcessor#postProcessBeforeInitialization() 实现的。感兴趣的能够本人看一下 ApplicationContextAwareProcessor 这个类的源码,就是判断以后创立的 Bean 是否实现了相干的 Aware 办法,如果实现了会调用回调办法将资源传递给 Bean。

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

对于 Aware 接口的执行程序,其实只须要记住第一组在第二组执行之前就行了。

3.4.2 生命周期接口

至于剩下的两个生命周期接口就很简略了,实例化和属性赋值都是 Spring 帮忙咱们做的,可能本人实现的有初始化和销毁两个生命周期阶段。

  • InitializingBean 对应生命周期的初始化阶段,在下面源码的 invokeInitMethods(beanName, wrappedBean, mbd);办法中调用。

有一点须要留神,因为 Aware 办法都是执行在初始化办法之前,所以能够在初始化办法中放心大胆的应用 Aware 接口获取的资源,这也是咱们自定义扩大 Spring 的罕用形式。
除了实现 InitializingBean 接口之外还能通过注解或者 xml 配置的形式指定初始化办法,至于这几种定义形式的调用程序其实没有必要记。因为这几个办法对应的都是同一个生命周期,只是实现形式不同,咱们个别只采纳其中一种形式。

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

3.5 Spring Bean 生命周期流程图

四、罕用接口阐明

4.1 BeanNameAware

该接口只有一个办法 setBeanName(String name),用来获取 bean 的 id 或者 name

4.2 BeanFactoryAware

该接口只有一个办法 setBeanFactory(BeanFactory beanFactory),用来获取 以后环境中的 BeanFactory

4.3 ApplicationContextAware

该接口只有一个办法 setApplicationContext(ApplicationContext applicationContext),用来获取 以后环境中的 ApplicationContext

4.4 InitializingBean

该接口只有一个办法 afterPropertiesSet(),在 属性注入实现后调用

4.5 DisposableBean

该接口只有一个办法 destroy(),在容器销毁的时候调用,在 用户指定的 destroy-method 之前调用

4.6 BeanPostProcessor

该接口有两个办法:

  • postProcessBeforeInitialization(Object bean, String beanName):在 初始化之前 调用此办法
  • postProcessAfterInitialization(Object bean, String beanName):在 初始化之后 调用此办法

通过办法签名咱们能够晓得,咱们能够通过 beanName 来筛选出咱们须要进行个性化定制的 bean。

4.7 InstantiationAwareBeanPostProcessor

该类是 BeanPostProcessor 的子接口,罕用的有如下三个办法:

  • postProcessBeforeInstantiation(Class beanClass, String beanName):在 bean实例化之前 调用
  • postProcessProperties(PropertyValues pvs, Object bean, String beanName):在 bean实例化之后、设置属性前 调用
  • postProcessAfterInstantiation(Class beanClass, String beanName):在 bean实例化之后 调用

五、代码演示

思路:创立一个类 UserBean,让其实现几个非凡的接口,并别离在接口实现的结构器、接口办法中断点,察看线程调用栈,剖析出 Bean 对象创立和治理关键点的触发机会。

5.1 UserBean 类

@Component
public class UserBean implements InitializingBean, BeanNameAware, DisposableBean, ApplicationContextAware {
    private int id;

    private String name;

    public UserBean(int id, String name) {
        this.id = id;
        this.name = name;
        System.out.println("2. 调用构造函数");
    }

    public int getId() {return id;}

    public void setId(int id) {
        this.id = id;
        System.out.println("5. 属性注入 id");
    }

    public String getName() {return name;}

    public void setName(String name) {
        this.name = name;
        System.out.println("5. 属性注入 name");
    }

    @Override
    public void setBeanName(String name) {System.out.println("6. 调用 BeanNameAware.setBeanName() 办法");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {UserBean userBean = (UserBean) applicationContext.getBean("userBean");
        System.out.println(userBean);
        System.out.println("7. 调用 BeanNameAware.setBeanName() 办法");
    }

    @Override
    public void afterPropertiesSet() throws Exception {System.out.println("9. 调用 InitializingBean.afterPropertiesSet() 办法");
    }

    public void myInit() {System.out.println("10. 调用 init-method 办法");
    }

    @Override
    public void destroy() throws Exception {System.out.println("12. 调用 DisposableBean.destroy() 办法");
    }

    public void myDestroy() {System.out.println("13. 调用 destroy-method 办法");
    }

    @Override
    public String toString() {
        return "UserBean{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

5.2 InstantiationAwareBeanPostProcessor 接口实现类

@Component
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {if ("userBean".equals(beanName)) {System.out.println("1. 调用 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation() 办法");
        }
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {if ("userBean".equals(beanName)) {UserBean userBean = (UserBean) bean;
            System.out.println("3. 调用 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation() 办法");
            System.out.println(userBean);
        }
        return true;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {if ("userBean".equals(beanName)) {System.out.println("4. 调用 InstantiationAwareBeanPostProcessor.postProcessProperties() 办法");
        }
        return null;
    }
}

5.3 BeanPostProcessor 接口实现类

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if ("userBean".equals(beanName)) {System.out.println("8. 调用 BeanPostProcessor.postProcessBeforeInitialization() 办法");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if ("userBean".equals(beanName)) {System.out.println("11. 调用 BeanPostProcessor.postProcessAfterInitialization() 办法");
        }
        return bean;
    }
}

5.4 BeanFactoryPostProcessor 接口实现类

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("0. 调用 BeanFactoryPostProcessor.postProcessBeanFactory() 办法");
    }
}

5.5 applicationContext.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"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
">

    <bean class="com.riemann.test.MyInstantiationAwareBeanPostProcessor" />

    <bean id="userBean" class="com.riemann.test.UserBean" init-method="myInit" destroy-method="myDestroy">
        <!-- 构造函数注入 -->
        <constructor-arg index="0" type="int">
            <value>1</value>
        </constructor-arg>
        <constructor-arg index="1" type="java.lang.String">
            <value> 微信公众号【老周聊架构】</value>
        </constructor-arg>

        <!-- setter 办法注入 -->
        <property name="id" value="2"/>
        <property name="name" value="riemann"/>
    </bean>

    <bean class="com.riemann.test.MyBeanPostProcessor" />

    <bean class="com.riemann.test.MyBeanFactoryPostProcessor" />
    
</beans>

5.6 测试类

public class BeanLifeCycleTest {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        UserBean user = (UserBean) applicationContext.getBean("userBean");
        ((AbstractApplicationContext) applicationContext).close();}
}

5.7 控制台后果打印

正文完
 0