写点什么

大厂高频面试题 Spring Bean 生命周期最详解

作者:Tom弹架构
  • 2021 年 12 月 08 日
  • 本文字数:9429 字

    阅读完需:约 31 分钟

Spring 作为当前 Java 最流行、最强大的轻量级框架。Spring Bean 的生命周期也是面试高频题,了解 Spring Bean 周期也能更好地帮助我们解决日常开发中的问题。程序员应该都知道 Spring 的基础容器是 ApplicationContext。应很多粉丝的强烈建议,本文我来分析分析 ApplicationContext 中 Bean 的生命周期。ApplicationContext 是顶层容器接口 BeanFactory 的实现类,因此,我们了解了 ApplicationContext 的生命周期逻辑,也基本上了解了其他类型容器的生命周期逻辑。

1 Spring 生命周期流程图

下面先来看一张 Spring Bean 完整的生命周期流程图,下图描述的是从 Spring 容器初始化 Bean 开始直到 Spring 容器销毁 Bean,所经历的关键节点。



从上图可以看出,Spring Bean 的生命周期管理的基本思路是:在 Bean 出现之前,先准备操作 Bean 的 BeanFactory,然后操作完 Bean,所有的 Bean 也还会交给 BeanFactory 进行管理。在所有 Bean 操作准备 BeanPostProcessor 作为回调。在 Bean 的完整生命周期管理过程中,经历了以下主要几个步骤:

1.1 Bean 创建前的准备阶段

步骤 1: Bean 容器在配置文件中找到 Spring Bean 的定义以及相关的配置,如 init-method 和 destroy-method 指定的方法。步骤 2: 实例化回调相关的后置处理器如 BeanFactoryPostProcessor、BeanPostProcessor、InstantiationAwareBeanPostProcessor 等

1.2 创建 Bean 的实例

步骤 3: Srping 容器使用 Java 反射 API 创建 Bean 的实例。步骤 4:扫描 Bean 声明的属性并解析。

1.3 开始依赖注入

步骤 5:开始依赖注入,解析所有需要赋值的属性并赋值。步骤 6:如果 Bean 类实现 BeanNameAware 接口,则将通过传递 Bean 的名称来调用 setBeanName()方法。步骤 7:如果 Bean 类实现 BeanFactoryAware 接口,则将通过传递 BeanFactory 对象的实例来调用 setBeanFactory()方法。步骤 8:如果有任何与 BeanFactory 关联的 BeanPostProcessors 对象已加载 Bean,则将在设置 Bean 属性之前调用 postProcessBeforeInitialization()方法。步骤 9:如果 Bean 类实现了 InitializingBean 接口,则在设置了配置文件中定义的所有 Bean 属性后,将调用 afterPropertiesSet()方法。

1.4 缓存到 Spring 容器

步骤 10: 如果配置文件中的 Bean 定义包含 init-method 属性,则该属性的值将解析为 Bean 类中的方法名称,并将调用该方法。步骤 11:如果为 Bean Factory 对象附加了任何 Bean 后置处理器,则将调用 postProcessAfterInitialization()方法。

1.5 销毁 Bean 的实例

步骤 12:如果 Bean 类实现 DisposableBean 接口,则当 Application 不再需要 Bean 引用时,将调用 destroy()方法。步骤 13:如果配置文件中的 Bean 定义包含 destroy-method 属性,那么将调用 Bean 类中的相应方法定义。

2 代码实战演示

下面我们用一个简单的 Bean 来演示并观察一下 Spring Bean 完整的生命周期。

2.1 准备 Author 类

1、首先是一个简单的 Bean,调用 Bean 自身的方法和 Bean 级生命周期接口方法,为了方便演示,它实现了 BeanNameAware、BeanFactoryAware、InitializingBean 和 DiposableBean 这 4 个接口,同时添加 2 个 init-method 和 destory-method 方法,对应配置文件中<bean>的 init-method 和 destroy-method。具体代码如下:



package com.tom.lifecycle;
import lombok.Data;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.BeansException;import org.springframework.beans.factory.*;
@Slf4j@Datapublic class Author implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean { private String name; private String address; private int age;
private BeanFactory beanFactory;
private String beanName;
public Author() { log.info("【构造器】调用Tom类的构造器实例化"); }
public void setName(String name) { log.info("【注入属性】name"); this.name = name; }
public void setAddress(String address) { log.info("【注入属性】address"); this.address = address; }
public void setAge(int age) { log.info("【注入属性】age"); this.age = age; }
// 实现BeanFactoryAware接口的方法 public void setBeanFactory(BeanFactory beanFactory) throws BeansException { log.info("【BeanFactoryAware接口】调用setBeanFactory方法"); this.beanFactory = beanFactory; }
// 实现BeanNameAware接口的方法 public void setBeanName(String beanName) { log.info("【BeanNameAware接口】调用setBeanName方法"); this.beanName = beanName; }
// 实现DiposibleBean接口的方法 public void destroy() throws Exception { log.info("【DiposibleBean接口】调用destroy方法"); }
// 实现InitializingBean接口的方法 public void afterPropertiesSet() throws Exception { log.info("【InitializingBean接口】调用afterPropertiesSet方法"); }
// 通过<bean>的init-method属性指定的初始化方法 public void beanInit() { log.info("【init-method】调用<bean>的init-method属性指定的初始化方法"); }
// 通过<bean>的destroy-method属性指定的初始化方法 public void beanDestory() { log.info("【destroy-method】调用<bean>的destroy-method属性指定的初始化方法"); }}
复制代码


在配置 Spring 配置文件中加入如下内容:



<bean id="author" class="com.tom.lifecycle.Author" init-method="beanInit" destroy-method="beanDestory" scope="singleton" p:name="Tom" p:address="湖南长沙" p:age="18"/>
复制代码

2.2 演示 BeanFactoryPostProcessor 的执行

1.创建 GPBeanFactoryPostProcessor 类,并实现 BeanFactoryPostProcessor 接口,具体代码如下:



package com.tom.lifecycle;
import lombok.extern.slf4j.Slf4j;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@Slf4jpublic class GPBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
public GPBeanFactoryPostProcessor() { super(); log.info("调用BeanFactoryPostProcessor实现类构造器!!"); }
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { log.info("BeanFactoryPostProcessor调用postProcessBeanFactory方法"); BeanDefinition bd = configurableListableBeanFactory.getBeanDefinition("author"); bd.getPropertyValues().addPropertyValue("age", "16"); }}
复制代码


2.在配置 Spring 配置文件中加入如下内容:



<bean id="beanFactoryPostProcessor" class="com.tom.lifecycle.GPBeanFactoryPostProcessor" />
复制代码


3.编写测试类 BeanLifeCycleTest,具体代码如下:



package com.tom.lifecycle;
import lombok.extern.slf4j.Slf4j;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;
@Slf4jpublic class BeanLifeCycleTest {
public static void main(String[] args) {
log.info("====== 开始初始化Spring容器 ========");
ApplicationContext factory = new ClassPathXmlApplicationContext("application-beans.xml");
log.info("====== 初始化Spring容器成功 ========");
//获取Author实例 Author author = factory.getBean("author", Author.class);
log.info(author.toString());
log.info("====== 开始销毁Spring容器 ========");
((ClassPathXmlApplicationContext) factory).registerShutdownHook(); }
}
复制代码


4.运行结果


运行结果如下:



15:49:12.477 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - 调用BeanPostProcessor实现类构造器!!15:49:12.494 [main] INFO com.tom.lifecycle.Author - 【构造器】调用Tom类的构造器实例化15:49:12.527 [main] INFO com.tom.lifecycle.Author - 【注入属性】address15:49:12.528 [main] INFO com.tom.lifecycle.Author - 【注入属性】age15:49:12.528 [main] INFO com.tom.lifecycle.Author - 【注入属性】name15:49:12.528 [main] INFO com.tom.lifecycle.Author - 【BeanNameAware接口】调用setBeanName方法15:49:12.528 [main] INFO com.tom.lifecycle.Author - 【BeanFactoryAware接口】调用setBeanFactory方法15:49:12.528 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - BeanPostProcessor接口方法postProcessBeforeInitialization对属性进行更改15:49:12.528 [main] INFO com.tom.lifecycle.Author - 【InitializingBean接口】调用afterPropertiesSet方法15:49:12.528 [main] INFO com.tom.lifecycle.Author - 【init-method】调用<bean>的init-method属性指定的初始化方法15:49:12.528 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - BeanPostProcessor接口方法postProcessAfterInitialization对属性进行更改15:49:12.531 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - ====== 初始化Spring容器成功 ========15:49:12.531 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - Author(name=Tom, address=湖南长沙, age=18, beanFactory=org.springframework.beans.factory.support.DefaultListableBeanFactory@26653222: defining beans [beanPostProcessor,author]; root of factory hierarchy, beanName=author)15:49:12.531 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - ====== 开始销毁Spring容器 ========15:49:12.532 [Thread-0] INFO com.tom.lifecycle.Author - 【DiposibleBean接口】调用destroy方法15:49:12.533 [Thread-0] INFO com.tom.lifecycle.Author - 【destroy-method】调用<bean>的destroy-method属性指定的初始化方法
复制代码


我们看到,整个执行和我们一开始绘制的流程图一致。但是为什么我们要实现 BeanFactoryPostProcessor 接口呢?我们进入到 BeanFactoryPostProcessor 的源码如下:



package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
public interface BeanFactoryPostProcessor { void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;}
复制代码


BeanFactoryPostProcessor 接口只有一个 postProcessBeanFactory()方法,BeanFactoryPostProcessor:在 BeanFactory 标准初始化之后可以进行修改。将加载所有 Bean 定义,但是还没有实例化 Bean。这个方法允许重新覆盖或者添加属性甚至快速的初始化 bean。初次看到可能不知道 postProcessBeanFactory()到底是干嘛的。要想透彻理解这个方法的作用,下面来进入到 BeanFactoryPostProcessor 的源码,理解一下 postProcessBeanFactory()的参数,我们可以利用这些参数做一些操作。


通过参数来看,只有一个 ConfigurableListableBeanFactory 类,这个类的可以提供分析、修改 Bean 定义和预先实例化单例的功能。我们再进入到 ConfigurableListableBeanFactory 的源码中:



public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {
//忽略被给定注入依赖类型 ,例如String void ignoreDependencyType(Class<?> var1);
//略被给定注入依赖接口 。这个通常被使用由ApplicationContext去注册依赖,可以以多种方式实现。例如BeanFactory通过BeanFactoryAware,ApplicationContext 通过ApplicationContextAware。默认情况下,仅BeanFactoryAware接口是被忽略,需要忽略其他接口,调用此方法 void ignoreDependencyInterface(Class<?> var1);
//注册一个特定类型依赖伴随着相应的Autowired值。这个是准备被用于应该可以Autowire而不是在这个工厂被定义的Bean的工厂/上下文引用。例如 将ApplicationContext类型的依赖项解析为Bean所在的ApplicationContext实例。注意~在普通的BeanFactory中没有注册这样的默认类型,甚至连BeanFactory接口本身都没有 void registerResolvableDependency(Class<?> var1, Object var2);
//确认这个被指定的Bean是否是一个Autowire候选,将被注入到其他声明匹配类型的依赖的Bean中 boolean isAutowireCandidate(String var1, DependencyDescriptor var2) throws NoSuchBeanDefinitionException;
//根据指定的beanName返回被注册的Bean定义,允许访问其属性值和构造函数参数值(可以在BeanFactory后期处理期间被修改)。这个被返回的BeanDefinition对象不应该是副本而是原始在工厂被注册的。这意味着如果需要它可以被转换为更具体的实现类型。注意这个方法只能获得本地工厂BeanDefinition BeanDefinition getBeanDefinition(String var1) throws NoSuchBeanDefinitionException;
//冻结全部Bean定义,给被注册的Bean定义发信号告诉它们今后不再被修改和进一步后续处理。它允许Factory去积极缓存Bean定义元数据 void freezeConfiguration();
//返回该工厂的BeanDefinnition是否被冻结 boolean isConfigurationFrozen();
//确保所有非懒加载的单例Bean被实例化,包括FactoryBean void preInstantiateSingletons() throws BeansException;}
复制代码


通过以上演示和分析,我们应该大概能够了解 ConfigurableListableBeanFactory 的作用,基本就都是对于 Bean 定义的操作。至此我们还没有看到 BeanPostProcessor 和 InstantiationAwareBeanPostProcessor 的调用。下面我们把 BeanPostProcessor 和 InstantiationAwareBeanPostProcessor 的实现补充上来,再看完整的执行流程

2.3 实现 BeanPostProcessor

创建 GPBeanPostProcessor 类,并实现 BeanPostProcessor 接口,具体代码如下:



package com.tom.lifecycle;
import lombok.extern.slf4j.Slf4j;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;
@Slf4jpublic class GPBeanPostProcessor implements BeanPostProcessor {
public GPBeanPostProcessor(){ log.info("调用BeanPostProcessor实现类构造器!!"); } public Object postProcessBeforeInitialization(Object o, String s) throws BeansException { log.info("BeanPostProcessor接口方法postProcessBeforeInitialization对属性进行更改"); return o; }
public Object postProcessAfterInitialization(Object o, String s) throws BeansException { log.info("BeanPostProcessor接口方法postProcessAfterInitialization对属性进行更改"); return o; }}
复制代码


ApplicationContext 可以在 BeanDefinition 中自动检测到实现了 BeanPostProcessor 的 Bean,并且把这些 Bean 应用于随后的 Bean 创建。普通的 BeanFactory 允许对后处理器进行程序化注册,通过工厂应用于所有 Bean 创建。BeanPostProcessor 接口中主要有两个方法:


2.4 实现 InstantiationAwareBeanPostProcessor

创建 GPInstantiationAwareBeanPostProcessor 类,并实现 InstantiationAwareBeanPostProcessorAdapter 接口,具体代码如下:



package com.tom.lifecycle;
import lombok.extern.slf4j.Slf4j;import org.springframework.beans.BeansException;import org.springframework.beans.PropertyValues;import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import java.beans.PropertyDescriptor;
@Slf4jpublic class GPInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
public GPInstantiationAwareBeanPostProcessor() { super(); log.info("调用InstantiationAwareBeanPostProcessorAdapter实现类构造器!!"); }
// 接口方法、实例化Bean之前调用 @Override public Object postProcessBeforeInstantiation(Class beanClass,String beanName) throws BeansException { log.info("InstantiationAwareBeanPostProcessor调用postProcessBeforeInstantiation方法"); return null; }
// 接口方法、实例化Bean之后调用 @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { log.info("InstantiationAwareBeanPostProcessor调用postProcessAfterInitialization方法"); return bean; }
// 接口方法、设置某个属性时调用 @Override public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { log.info("InstantiationAwareBeanPostProcessor调用postProcessPropertyValues方法"); return pvs; }}
复制代码


实现 InstantiationAwareBeanPostProcessorAdapter 的 Bean 之后,可以在实例化成功之后,做一些校验或者补充些内容或者把 Bean 包装代理注入。实现 InstantiationAwareBeanPostProcessorAdapter 的 Bean 之后,不会影响容器正常处理每一个实例化的 Bean,其子类仅仅只是根据需要覆盖父类的方法。


注意,只有在实际需要 InstantiationAwareBeanPostProcessor 功能时才推荐此基类。如果我们所需要的只是简单的 BeanPostProcessor 功能,那么直接实现更简单的接口即可。


下面详细介绍一下 InstantiationAwareBeanPostProcessorAdapter 接口中的所有方法:


2.5 修改配置文件

完整的配置文件内容如下:



<?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:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<bean id="beanPostProcessor" class="com.tom.lifecycle.GPBeanPostProcessor" />
<bean id="beanFactoryPostProcessor" class="com.tom.lifecycle.GPBeanFactoryPostProcessor" />
<bean id="instantiationAwareBeanPostProcessor" class="com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor" />

<bean id="author" class="com.tom.lifecycle.Author" init-method="beanInit" destroy-method="beanDestory" scope="singleton" p:name="Tom" p:address="湖南长沙" p:age="18"/>
</beans>
复制代码

2.6 运行结果

最后,我们再次运行 BeanLifeCycleTest 测试类,看到如下运行结果:


15:56:20.030 [main] INFO com.tom.lifecycle.GPBeanFactoryPostProcessor - 调用BeanFactoryPostProcessor实现类构造器!!15:56:20.045 [main] INFO com.tom.lifecycle.GPBeanFactoryPostProcessor - BeanFactoryPostProcessor调用postProcessBeanFactory方法15:56:20.046 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - 调用BeanPostProcessor实现类构造器!!15:56:20.047 [main] INFO com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor - 调用InstantiationAwareBeanPostProcessorAdapter实现类构造器!!15:56:20.051 [main] INFO com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor - InstantiationAwareBeanPostProcessor调用postProcessBeforeInstantiation方法15:56:20.052 [main] INFO com.tom.lifecycle.Author - 【构造器】调用Tom类的构造器实例化15:56:20.069 [main] INFO com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor - InstantiationAwareBeanPostProcessor调用postProcessPropertyValues方法15:56:20.092 [main] INFO com.tom.lifecycle.Author - 【注入属性】address15:56:20.092 [main] INFO com.tom.lifecycle.Author - 【注入属性】age15:56:20.092 [main] INFO com.tom.lifecycle.Author - 【注入属性】name15:56:20.092 [main] INFO com.tom.lifecycle.Author - 【BeanNameAware接口】调用setBeanName方法15:56:20.092 [main] INFO com.tom.lifecycle.Author - 【BeanFactoryAware接口】调用setBeanFactory方法15:56:20.093 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - BeanPostProcessor接口方法postProcessBeforeInitialization对属性进行更改15:56:20.093 [main] INFO com.tom.lifecycle.Author - 【InitializingBean接口】调用afterPropertiesSet方法15:56:20.093 [main] INFO com.tom.lifecycle.Author - 【init-method】调用<bean>的init-method属性指定的初始化方法15:56:20.093 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - BeanPostProcessor接口方法postProcessAfterInitialization对属性进行更改15:56:20.093 [main] INFO com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor - InstantiationAwareBeanPostProcessor调用postProcessAfterInitialization方法15:56:20.097 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - ====== 初始化Spring容器成功 ========15:56:20.098 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - Author(name=Tom, address=湖南长沙, age=16, beanFactory=org.springframework.beans.factory.support.DefaultListableBeanFactory@26653222: defining beans [beanPostProcessor,beanFactoryPostProcessor,instantiationAwareBeanPostProcessor,author]; root of factory hierarchy, beanName=author)15:56:20.098 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - ====== 开始销毁Spring容器 ========15:56:20.099 [Thread-0] INFO com.tom.lifecycle.Author - 【DiposibleBean接口】调用destroy方法15:56:20.100 [Thread-0] INFO com.tom.lifecycle.Author - 【destroy-method】调用<bean>的destroy-method属性指定的初始化方法
复制代码

3 Spring Bean 生命周期运行时序图

最后我们来看一下完整的执行时序图:



关注微信公众号『 Tom 弹架构 』回复“Spring”可获取完整源码。


本文为“Tom 弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!

如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。关注微信公众号『 Tom 弹架构 』可获取更多技术干货!

发布于: 57 分钟前阅读数: 5
用户头像

Tom弹架构

关注

不只做一个技术者,更要做一个思考者 2021.10.22 加入

畅销书作者,代表作品:《Spring 5核心原理》、《Netty 4核心原理》、《设计模式就该这样学》

评论

发布
暂无评论
大厂高频面试题Spring Bean生命周期最详解