生命周期流程

Spring 中的一个 Bean 从被创建到被销毁,需要经历很多个阶段的生命周期,下图是一个 Bean 从创建到销毁的生命周期流程:

在 Bean 的各个生命周期流程点,Spring 都提供了对应的接口或者注解,以便开发者在各个生命周期的流程点能够做一些自己的操作。

案例解析

定义 Spring 上下文工具类

Spring 中生命周期最常见的应用可能是定义一个 Spring 上下文的工具类。这个工具类也使用 @Component 注解修饰,表明它也是一个 Bean ,其次它实现了 ApplicationContextAware 接口,则说明它作为一个 Bean 被创建以及初始化的过程中需要调用 setApplicationContext() 方法,设置它所在的 Spring 上下文。代码如下:

@Component
public class SpringContextUtils implements ApplicationContextAware { private static ApplicationContext applicationContext; /**
* Spring会自动调用这个方法,注入ApplicationContext
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtils.applicationContext = applicationContext;
} /**
* 获取ApplicationContext
* @return ApplicationContext
*/
public static ApplicationContext getApplicationContext() {
if (applicationContext == null) {
throw new IllegalStateException("ApplicationContext is not set. Make sure SpringContextUtils is properly initialized.");
}
return applicationContext;
} /**
* 通过名称获取Bean
* @param name Bean的名称
* @return Bean实例
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
} /**
* 通过名称和类型获取Bean
* @param name Bean的名称
* @param requiredType Bean的类型
* @param <T> Bean的类型
* @return Bean实例
*/
public static <T> T getBean(String name, Class<T> requiredType) {
return getApplicationContext().getBean(name, requiredType);
} /**
* 通过类型获取Bean
* @param requiredType Bean的类型
* @param <T> Bean的类型
* @return Bean实例
*/
public static <T> T getBean(Class<T> requiredType) {
return getApplicationContext().getBean(requiredType);
}
}

在 Bean 的依赖注入之后执行初始化操作

比如下面的案例中,MyService 这个 Bean 需要在它的依赖 MyRepository 这个 Bean 注入完成之后,调用依赖的 loadInitialData() 方法加载初始数据。代码如下:

@Service
public class MyService { private MyRepository myRepository; private List<String> initialData; @Autowired
public void setMyRepository(MyRepository myRepository) {
this.myRepository = myRepository;
} // 依赖注入完成后执行的初始化方法
@PostConstruct
public void init() {
this.initialData = myRepository.loadInitialData();
} public void doBusinessLogic() {
}
} @Service
class MyRepository {
public List<String> loadInitialData() {
}
}

@PostConstruct 注解是 JSR-250 标准定义的注解,它与 Spring 框架的耦合度比较低。除此之外还可以实现 InitializingBean 接口,在它的 afterPropertiesSet() 方法中来完成初始化;通过 XML 配置 init-method 或者 @Bean 注解的 initMethod 属性来指定任意的方法作为初始化方法来完成初始化。

Bean 创建源码解析

在 Spring 源码实现中实际上分为了三个大的步骤:实例化 -> 填充属性 -> 初始化。填充属性可以看前面的文章Spring 中@Autowired,@Resource,@Inject 注解实现原理。在上面生命周期图片中的从 XXXAware 的 setXXXAware() 方法到 postProcessAfterInitialization() 都属于初始化的这个步骤中。

在 AbstractAutowireCapableBeanFactory 中提供的 doCreateBean() 方法中提现了这三个大的步骤,其中的 createBeanInstance() 方法完成 Bean 的实例化;populateBean() 方法完成 Bean的属性填充;initializeBean() 方法完成 Bean 的初始化。代码如下:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd,
@Nullable Object[] args) throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null; if (instanceWrapper == null) {
//实例化Bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance(); // Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton()
&& this.allowCircularReferences
&& isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName,
() -> getEarlyBeanReference(beanName, mbd, bean));
} // Initialize the bean instance.
Object exposedObject = bean;
try {
//填充Bean的属性,比如处理@Autowired,@Resource,@Inject注解
populateBean(beanName, mbd, instanceWrapper); //初始化Bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch {
}
}

initializeBean()方法流程

在 initializeBean() 方法中又分为:调用 invokeAwareMethods() 方法 -> 调用 applyBeanPostProcessorsBeforeInitialization() 方法 -> 调用 invokeInitMethods() 方法 -> 调用 applyBeanPostProcessorsAfterInitialization() 方法,代码如下:

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
//调用Aware()方法
invokeAwareMethods(beanName, bean); Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//调用BeanPostProcessor的postProcessBeforeInitialization()方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
} try {
//调用初始化方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);
}
if (mbd == null || !mbd.isSynthetic()) {
//调用BeanPostProcessor的postProcessAfterInitialization()方法
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
} return wrappedBean;
}

invokeAwareMethods()方法流程

需要注意的是 invokeAwareMethods() 方法中仅仅只调用实现了 BeanNameAware,BeanClassLoaderAware,BeanFactoryAware 接口的方法。而常见的 ApplicationContextAware 接口的 setApplicationContext() 方法则是在 ApplicationContextAwareProcessor 的 postProcessBeforeInitialization() 方法中调用的。代码如下:

public abstract class AbstractAutowireCapableBeanFactory {
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware beanNameAware) {
//调用setBeanName()方法
beanNameAware.setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware beanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
//调用setBeanClassLoader()方法
beanClassLoaderAware.setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware beanFactoryAware) {
//调用setBeanFactory()方法
beanFactoryAware.setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
} class ApplicationContextAwareProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Aware) {
this.invokeAwareInterfaces(bean);
} return bean;
} private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware environmentAware) {
environmentAware.setEnvironment(this.applicationContext.getEnvironment());
} if (bean instanceof EmbeddedValueResolverAware embeddedValueResolverAware) {
embeddedValueResolverAware.setEmbeddedValueResolver(this.embeddedValueResolver);
} if (bean instanceof ResourceLoaderAware resourceLoaderAware) {
resourceLoaderAware.setResourceLoader(this.applicationContext);
} if (bean instanceof ApplicationEventPublisherAware applicationEventPublisherAware) {
applicationEventPublisherAware.setApplicationEventPublisher(this.applicationContext);
} if (bean instanceof MessageSourceAware messageSourceAware) {
messageSourceAware.setMessageSource(this.applicationContext);
} if (bean instanceof ApplicationStartupAware applicationStartupAware) {
applicationStartupAware.setApplicationStartup(this.applicationContext.getApplicationStartup());
} if (bean instanceof ApplicationContextAware applicationContextAware) {
//这里调用的setApplicationContext()方法
applicationContextAware.setApplicationContext(this.applicationContext);
} }
}

applyBeanPostProcessorsBeforeInitialization() 方法流程

在该方法中主要就是查找所有实现了 BeanPostProcessor 接口的对象,然后循环调用其 postProcessBeforeInitialization() 方法。代码如下:

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}

在 Spring 中提供了 CommonAnnotationBeanPostProcessor(@Resource 注解也是它处理的) 实现了 BeanPostProcessor 接口,在它的构造函数里面初始化了要处理 @PostConstruct 注解。代码如下:

public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3); // Jakarta EE 9 set of annotations in jakarta.annotation package
addInitAnnotationType(loadAnnotationType("jakarta.annotation.PostConstruct"));
addDestroyAnnotationType(loadAnnotationType("jakarta.annotation.PreDestroy")); // Tolerate legacy JSR-250 annotations in javax.annotation package
addInitAnnotationType(loadAnnotationType("javax.annotation.PostConstruct"));
addDestroyAnnotationType(loadAnnotationType("javax.annotation.PreDestroy"));
}

然后在它的子类 InitDestroyAnnotationBeanPostProcessor 的 postProcessBeforeInitialization() 实现了查找 @PostConstruct 注解修饰的方法,然后调用的逻辑。代码如下:

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}

invokeInitMethods() 方法流程

在该方法中会先判断 Bean 是否实现了 InitializingBean 接口,如果实现了则调用其 afterPropertiesSet() 方法,然后查看 Bean 定义中是否有自定义的初始化方法,如果有的话,则调用自定义的初始化方法。代码如下:

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
//调用afterPropertiesSet()方法
((InitializingBean) bean).afterPropertiesSet();
} if (mbd != null && bean.getClass() != NullBean.class) {
String[] initMethodNames = mbd.getInitMethodNames();
if (initMethodNames != null) {
for (String initMethodName : initMethodNames) {
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
//调用自定义初始化方法
invokeCustomInitMethod(beanName, bean, mbd, initMethodName);
}
}
}
}
} protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition mbd, String initMethodName)
throws Throwable {
Class<?> beanClass = bean.getClass();
MethodDescriptor descriptor = MethodDescriptor.create(beanName, beanClass, initMethodName);
String methodName = descriptor.methodName(); Method initMethod = (mbd.isNonPublicAccessAllowed() ?
BeanUtils.findMethod(descriptor.declaringClass(), methodName) :
ClassUtils.getMethodIfAvailable(beanClass, methodName));
//省略代码 Method methodToInvoke = ClassUtils.getPubliclyAccessibleMethodIfPossible(initMethod, beanClass); try {
ReflectionUtils.makeAccessible(methodToInvoke);
//这里通过反射的方式调用初始化方法
methodToInvoke.invoke(bean);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}

applyBeanPostProcessorsBeforeInitialization() 方法流程

在该方法中主要就是查找所有实现了 BeanPostProcessor 接口的对象,然后循环调用其 postProcessAfterInitialization() 方法。代码如下:

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}

一个 Bean 就这样走完了它的一生之 Bean 的出生的更多相关文章

  1. 一个简单的Java代码生成工具—根据数据源自动生成bean、dao、mapper.xml、service、serviceImpl

    目录结构 核心思想 通过properties文件获取数据源—>获取数据表的字段名称.字段类型等—>生成相应的bean实体类(po.model).dao接口(基本的增删改查).mapper. ...

  2. Spring Bean生命周期,好像人的一生。。

    大家好,我是老三,上节我们手撸了一个简单的IOC容器五分钟,手撸一个Spring容器!,这节我们来看一看Spring中Bean的生命周期,我发现,和人的一生真的很像. 简单说说IoC和Bean IoC ...

  3. Spring竟然可以创建“重复”名称的bean?—一次项目中存在多个bean名称重复问题的排查

    作者:京东科技 韩国凯 一.项目中存在了名称重复的bean 众所周知,在Spring中时不能够创建两个名称相同的bean的,否则会在启动时报错: 但是我却在我们的spring项目中发现了两个相同名称的 ...

  4. [原创]java WEB学习笔记98:Spring学习---Spring Bean配置及相关细节:如何在配置bean,Spring容器(BeanFactory,ApplicationContext),如何获取bean,属性赋值(属性注入,构造器注入),配置bean细节(字面值,包含特殊字符,引用bean,null值,集合属性list map propert),util 和p 命名空间

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  5. Spring(九):Spring配置Bean(二)自动装配的模式、Bean之间的关系

    XML配置里的Bean自动装配 Spring IOC容器可以自动装配Bean,需要做的仅仅是在<bean>的autowire属性里指定自动装配的模式,模式包含:byType,byName, ...

  6. 7 -- Spring的基本用法 -- 4... 使用 Spring 容器:Spring 容器BeanFactory、ApplicationContext;ApplicationContext 的国际化支持;ApplicationContext 的事件机制;让Bean获取Spring容器;Spring容器中的Bean

    7.4 使用 Spring 容器 Spring 有两个核心接口:BeanFactory 和 ApplicationContext,其中ApplicationContext 是 BeanFactory ...

  7. 报错org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class path resource [bean.xml]

    报这种错的原因基本上是applicationContext.xml文件中bean配置错误,错误如下: org.springframework.beans.factory.BeanCreationExc ...

  8. IDEA02 利用Maven创建Web项目、为Web应用添加Spring框架支持、bean的创建于获取、利用注解配置Bean、自动装配Bean、MVC配置

    1 环境版本说明 Jdk : 1.8 Maven : 3.5 IDEA : 专业版 2017.2 2 环境准备 2.1 Maven安装及其配置 2.2 Tomcat安装及其配置 3 详细步骤 3.1 ...

  9. Spring课程 Spring入门篇 4-5 Spring bean装配之基于java的容器注解说明--@Bean

    1 解析 2.1 @bean注解定义 2.2 @bean注解的使用 2 代码演练 2.1 @bean的应用不带name 2.2 @bean的应用带name   2.3 @bean注解调用initMet ...

  10. Bean的一生(Bean的生命周期)

    1. 什么是Bean? Bean是spring中组成应用程序的主体及由spring IoC容器所管理的对象(IoC容器初始化.装配及管理的对象).如果把spring比作一座大型工厂,那么bean就是该 ...

随机推荐

  1. DeepSeek 官方推出的提示词库,AI内容生成的精准导航仪!

    前言 在当今数字化时代,人工智能(AI)正以前所未有的速度改变着我们的生活方式和工作模式.从简单的数据处理到复杂的创意生成,AI技术正逐渐渗透到各个领域,成为推动社会进步的重要力量.然而,如何高效地利 ...

  2. Coordinate Spaces

    Coordinate Spaces 本主题包含以下部分: 根空间 用户空间 像素空间 任何VisionPro图像支持一系列坐标空间,以提供一个数值框架来表达特定特征的位置.最有用的空间是根空间,它将点 ...

  3. 使用QT开发远程linux服务器过程

    1.添加设备为通用linux 2.设置ip用户名 3.创建私钥文件,原来有的qtc那俩个文件删掉. 4.部署公钥,前提是测试链接要出现成功 5.在kits里添加编译环境设置编译器为32位或者64 6. ...

  4. 告别手动敲代码!VSCODE 风格在线可视化开发平台,效率飙升!

    2025 年 2 月 21 日消息,对于广大开发者而言,今天迎来一则重大利好消息!一款操作布局与 VSCODE 极为相似的可视化开发神器 --Joker 智能可视化开发平台正式上线.无论你是深耕前端领 ...

  5. Windows下PostgreSQL设置远程连接以及备份和恢复数据库

    一.设置远程连接 修改安装路径下的postgresql.conf,定位到listen_address  = '*',确保其值为'*'(Windows下默认是这样的,可不用修改) 修改安装路径下的pg_ ...

  6. Ubuntu 下查看 ip

    博客地址:https://www.cnblogs.com/zylyehuo/ ip a

  7. 文件批量重命名神器:Bulk Rename Utility

    内容摘要: 你还在手动给文件重命名吗?介绍一款免费而强大的批量重命名神器:Bulk Rename Utility,它将满足你对批量改名的所有期待.让它将你从痛苦的重命名工作中解放吧! 软件获取地址 云 ...

  8. docx4j转换pdf样式问题~Java Libreoffice转换pdf

    背景 本篇文章主要是介绍我在使用docx4j过程中遇到的问题,并最终如何通过Libreoffice来实现pdf的转换. 问题 在使用docx4j转换pdf过程中发现word文档中表格.加粗样式无法实现 ...

  9. Windows Server 2012 配置 FTP

    环境 Windows Server 2012 安装步骤 打开服务器管理器 管理 - 添加角色和功能 开始之前 选择安装类型 服务器选择 选择对应的服务器... 服务器角色 功能 确认 安装进度 配置步 ...

  10. javascript 数值交换技巧

    需求 现在存在var a = 1,b = 2;,要求使用javascript实现a & b数值的交换,让a = 2,b = 1. 中间变量(常规实现方式) var a = 1, b = 2; ...