面试官再问Spring的生命周期,你这么回答他

创作内容丰富的干货文章很费心力,感谢点过此文章的读者,点一个关注鼓励一下作者,激励他分享更多的精彩好文,谢谢大家!


        面试官经常会问:“请说一下Spring中的Bean的生命周期”。这个问题即考察对Spring的微观了解,又考察对Spring的宏观认识,想要答好并不容易!本文希望能够从源码角度入手,帮助面试者彻底搞定Spring Bean的生命周期。

         首先Spring的生命周期分为四个阶段。要想彻底搞清楚Spring的生命周期,首先要把这四个阶段牢牢记住。实例化和属性赋值对应构造方法和setter方法的注入,初始化和销毁是用户能自定义扩展的两个阶段。在这四步之间穿插的各种扩展点,稍后会讲。

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

        各个阶段的工作:

  1. 实例化,创建一个Bean对象。
  2. 填充属性,为属性赋值。
  3. 初始化,如果实现了 xxxAware 接口,通过不同类型的Aware接口拿到Spring容器的资源。
    如果实现了 BeanPostProcessor 接口,则会回调该接口的 postProcessBeforeInitialzation 和 postProcessAfterInitialization 方法。如果配置了 init - method 方法,则会执行 init - method 配置的方法。
  4. 销毁, 容器关闭后,如果 Bean 实现了 DisposableBean 接口,则会回调该接口的 destroy 方法 如果配置了 destroy - method 方法,则会执行 destroy - method 配置的方法。

        源码学习:

        前三个阶段,主要逻辑都在doCreate()方法中,逻辑很清晰,就是顺序调用以下三个方法,这三个方法与三个生命周期阶段一一对应,非常重要,在后续扩展接口分析中也会涉及。

  1. createBeanInstance() -> 实例化
  2. populateBean() -> 属性赋值
  3. initializeBean() -> 初始化
    注: bean 的生命周期是从将 bean 定义全部注册到 BeanFacotry 中以后开始的。

        源码如下,能证明实例化,属性赋值和初始化这三个生命周期的存在。关于本文的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);
    }
}
         上面这些这个实例化Bean 的方法是在 getBean() 方法中调用的,而 getBean 是在
finishBeanFactoryInitialization 方法中调用的,用来实例化单例非懒加载 Bean ,源码如下:
@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);
                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);
                // Register bean processors that intercept bean creation.
                // 所有BeanPostProcesser初始化的调用点。
                registerBeanPostProcessors(beanFactory);
                // Initialize message source for this context.
                initMessageSource();
                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();
                // Initialize other special beans in specific context subclasses.
                onRefresh();
                // Check for listener beans and register them.
                registerListeners();
                // Instantiate all remaining (non-lazy-init) singletons.
                // 所有单例非懒加载Bean的调用点
                finishBeanFactoryInitialization(beanFactory);
                // Last step: publish corresponding event.
                finishRefresh();
        }
    }
}
        销毁Bean 阶段 :
        至于销毁,是在容器关闭时调用的,详见 ConfigurableApplicationContext#close()
        高分答题的技巧

    如果回答了上面的答案可以拿到100分的话,加上下面的内容,就是120分。

        命周期常用扩展点

        Spring生命周期相关的常用扩展点非常多,所以问题不是不知道,而是记不住或者记不牢。其实记不住的根本原因还是不够了解,这里通过源码+分类的方式帮大家记忆。区分影响一个bean或者多个bean是从源码分析得出的。

        以BeanPostProcessor为例:

  1. refresh方法来看,BeanPostProcessor 实例化比正常的bean早。
  2. initializeBean 方法看, 每个 bean 初始化前后都调用所有 BeanPostProcessor
    postProcessBeforeInitialization postProcessAfterInitialization 方法。

        第一大类:影响多个Bean的接口

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

  • InstantiationAwareBeanPostProcessor
  • BeanPostProcessor
    这两兄弟可能是Spring 扩展中 最重要 的两个接口! InstantiationAwareBeanPostProcessor 作用于 实例 阶段的前后, BeanPostProcessor 作用于 初始化 阶段的前后。正好和第一、第三个生命周期阶段对应。通过图能更好理解:
    InstantiationAwareBeanPostProcessor
    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
    关于 BeanPostProcessor 执行阶段的源码穿插在下文 Aware 接口的调用时机分析中,因为部分 Aware 功能的就是通过他实现的! 只需要先记住 BeanPostProcessor 在初始化前后调用就可以了。
    接口源码:
    public interface BeanPostProcessor {
        //bean初始化之前调用
        @Nullable
        default Object postProcessBeforeInitialization(Object bean, String beanName) 
            throws BeansException {
                return bean;
        }
        //bean初始化之后调用
        @Nullable
        default Object postProcessAfterInitialization(Object bean, String beanName) 
            throws BeansException {
                return bean;
        }
    }
    第二大类:只调用一次的接口
    这一大类接口的特点是功能丰富,常用于用户自定义扩展。
    第二大类中又可以分为两类:
    1. Aware 类型的接口
    2. 生命周期接口
    无所不知的 Aware
    Aware 类型的接口的作用就是让我们能够拿到 Spring 容器中的一些资源。基本都能够见名知意, Aware之前的名字就是可以拿到什么资源,例如BeanNameAware 可以拿到 BeanName ,以此类推。调用时机需要注意:所有的 Aware 方法都是在初始化阶段之前调用的!
    Aware 接口众多,这里同样通过分类的方式帮助大家记忆。
    Aware 接口具体可以分为两组,至于为什么这么分,详见下面的源码分析。如下排列顺序同样也是Aware接口的执行顺序,能够见名知意的接口不再解释。
    Aware Group1
    1. BeanNameAware
    2. BeanClassLoaderAware
    3. BeanFactoryAware
    Aware Group2
    1. EnvironmentAware
    2. EmbeddedValueResolverAware 这个知道的人可能不多,实现该接口能够获取 Spring EL 解析器,用户的自定义注解需要支持spel 表达式的时候可以使用,非常方便。
    3.ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware) 这几个接口可能让人有点懵,实际上这几个接口可以一起记,其返回值实质上都是当前的ApplicationContext 对象,因为 ApplicationContext 是一个复合接口,如下:
    public interface ApplicationContext extends EnvironmentCapable,
        ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher,     
        ResourcePatternResolver {}
    这里涉及到另一道面试题, ApplicationContext BeanFactory 的区别,可以从 ApplicationContext 继承的这几个接口入手,除去BeanFactory 相关的两个接口就是 ApplicationContext 独有的功能,这里不详细说明。
    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 都是在代码中直接调用的,而ApplicationContext 相关的 Aware 都是通过 BeanPostProcessor#postProcessBeforeInitialization() 实现的。感兴趣的可以自己看一下ApplicationContextAwareProcessor 这个类的源码,就是判断当前创建的Bean 是否实现了相关的 Aware 方法,如果实现了会调用回调方法将资源传递给 Bean 。至于Spring 为什么这么实现,应该没什么特殊的考量。也许和 Spring 的版本升级有关。基于对修改关闭,对扩展开放的原则,Spring 对一些新的 Aware 采用了扩展的方式添加。BeanPostProcessor的调用时机也能在这里体现,包围住 invokeInitMethods 方法,也就说明了在初始化阶段的前后执行。关于Aware 接口的执行顺序,其实只需要记住第一组在第二组执行之前就行了。每组中各个 Aware 方法
    的调用顺序其实没有必要记,有需要的时候点进源码一看便知。
    简单的两个生命周期接口
    至于剩下的两个生命周期接口就很简单了,实例化和属性赋值都是 Spring 帮助我们做的,能够自己实现的有初始化和销毁两个生命周期阶段。
    InitializingBean 接口
    InitializingBean 顾名思义,是初始化 Bean 相关的接口。
    接口定义
    public interface InitializingBean {
        void afterPropertiesSet() throws Exception;
    }
    看方法名,是在读完 Properties 文件,之后执行的方法。 afterPropertiesSet() 方法是在初始化过程中被调用的。
    InitializingBean 对应生命周期的初始化阶段,在上面源码的 invokeInitMethods(beanName,
    wrappedBean, mbd); 方法中调用。
    有一点需要注意,因为 Aware 方法都是执行在初始化方法之前,所以可以在初始化方法中放心大胆的使用Aware 接口获取的资源,这也是我们自定义扩展 Spring 的常用方式。
    除了实现 InitializingBean 接口之外还能通过注解( @PostConstruct )或者 xml 配置的方式指定初始化方法(init-method ),至于这几种定义方式的调用顺序其实没有必要记。因为这几个方法对应的都是同一个生命周期,只是实现方式不同,我们一般只采用其中一种方式。
    三种实现指定初始化方法的方法:
    • 使用 @PostConstruct 注解,该注解作用于 void 方法上
    • 在配置文件中配置 init-method 方法
      <bean id="student" class="com.demo.Student" init-method="init2">
          <property name="name" value="小明"></property>
          <property name="age" value="20"></property>
          <property name="school" ref="school"></property>
      </bean>
  • 将类实现InitializingBean接口
       
@Component("student")
public class Student implements InitializingBean {
    private String name;
    private int age;
}

执行:

@Component("student")
public class Student implements InitializingBean{
    private String name;
    private int age;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    //1.使用postconstrtct注解
    @PostConstruct
    public void init(){
        System.out.println("执行 init方法");
    }

    //2.在xml配置文件中配置init-method方法
    public void init2(){
        System.out.println("执行init2方法 ");
    }

    //3.实现InitializingBean接口
    public void afterPropertiesSet() throws Exception {
        System.out.println("执行init3方法");
    }

}

通过测试我们可以得出结论,三种实现方式的执行顺序是:

Constructor > @PostConstruct > InitializingBean > init-method

DisposableBean接口

DisposableBean 类似于InitializingBean,对应生命周期的销毁阶段,ConfigurableApplicationContext#close()方法作为入口,实现是通过循环获取所有实现了 DisposableBean接口的Bean然后调用其destroy()方法 。

接口定义:

public interface DisposableBean {
    void destroy() throws Exception;
}

定义一个实现了DisposableBean接口的Bean

public class IndexBean implements InitializingBean,DisposableBean {
    public void destroy() throws Exception {
        System.out.println("destroy");
    }
    
    public void afterPropertiesSet() throws Exception {
        System.out.println("init-afterPropertiesSet()");
    }

    public void test(){
        System.out.println("init-test()");
    }
}

执行:

public class Main {
    public static void main(String[] args) {
        AbstractApplicationContext applicationContext=new
            ClassPathXmlApplicationContext("classpath:application-usertag.xml");
        System.out.println("init-success");
        applicationContext.registerShutdownHook();
    }
}

执行结果:

init - afterPropertiesSet ()
init - test ()
init - success
destroy

也就是说,在对象销毁的时候,会去调用DisposableBean的destroy方法。在进入到销毁过程时先去调用一下DisposableBeandestroy方法,然后后执行 destroy-method声明的方法(用来销毁Bean中的各项数据)。

扩展阅读: BeanPostProcessor注册时机与执行顺序

首先要明确一个概念,在spring中一切皆bean所有的组件都会被作为一个bean装配到spring容器中,过程如下图:

所以我们前面所讲的那些拓展点,也都会被作为一个个bean装配到spring容器中

注册时机

我们知道BeanPostProcessor也会注册为Bean,那么Spring是如何保证BeanPostProcessor在我们的业务Bean之前初始化完成呢?

请看我们熟悉的refresh()方法的源码,省略部分无关代码(refresh的详细注解见refresh()

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);
            // Register bean processors that intercept bean creation.
            // 注册所有BeanPostProcesser的方法
            registerBeanPostProcessors(beanFactory);
            // Initialize message source for this context.
            initMessageSource();
            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();
            // Initialize other special beans in specific context subclasses.
            onRefresh();
            // Check for listener beans and register them.
            registerListeners();
            // Instantiate all remaining (non-lazy-init) singletons.
            // 所有单例非懒加载Bean的创建方法
            finishBeanFactoryInitialization(beanFactory);
            // Last step: publish corresponding event.
            finishRefresh();
        }
}

可以看出,Spring是先执行registerBeanPostProcessors()进行BeanPostProcessors的注册,然后再执行finishBeanFactoryInitialization创建我们的单例非懒加载的Bean

执行顺序

BeanPostProcessor有很多个,而且每个BeanPostProcessor都影响多个Bean,其执行顺序至关重 要,必须能够控制其执行顺序才行。关于执行顺序这里需要引入两个排序相关的接口: PriorityOrdered、Ordered

PriorityOrdered是一等公民,首先被执行,PriorityOrdered公民之间通过接口返回值排序

Ordered是二等公民,然后执行,Ordered公民之间通过接口返回值排序

都没有实现是三等公民,最后执行

在以下源码中,可以很清晰的看到Spring注册各种类型BeanPostProcessor的逻辑,根据实现不同排序接口进行分组。优先级高的先加入,优先级低的后加入。

// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
// 首先,加入实现了PriorityOrdered接口的BeanPostProcessors,顺便根据PriorityOrdered排了序
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        currentRegistryProcessors.add(beanFactory.getBean(ppName,             
          BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
    }
}

sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors,registry);
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
// 然后,加入实现了Ordered接口的BeanPostProcessors,顺便根据Ordered排了序
postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
    if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)){
    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
    processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors,
registry);
currentRegistryProcessors.clear();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no
further ones appear.
// 最后加入其他常规的BeanPostProcessors
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class,
true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName,
BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors,
registry);
currentRegistryProcessors.clear();
}

根据排序接口返回值排序,默认升序排序,返回值越低优先级越高。

/**
* Useful constant for the highest precedence value.
* @see java.lang.Integer#MIN_VALUE
*/
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
/**
* Useful constant for the lowest precedence value.
* @see java.lang.Integer#MAX_VALUE
*/
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

PriorityOrdered、Ordered接口作为Spring整个框架通用的排序接口,在Spring中应用广泛,也是非常重要的接口。

Bean的生命周期流程图

总结

Spring Bean的生命周期分为 四个阶段 多个扩展点 。扩展点又可以分为 影响多个Bean 影响单个Bean 。

整理如下:四个阶段

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

多个扩展点

影响多个Bean

BeanPostProcessor

InstantiationAwareBeanPostProcessor

影响单个Bean Aware

Aware Group1

BeanNameAware BeanClassLoaderAware BeanFactoryAware

Aware Group2

EnvironmentAware EmbeddedValueResolverAware ApplicationContextAware

(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware)

生命周期

InitializingBean DisposableBean

      
       

  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值