Spring 源码阅读 27:Bean 实例初始化

简介: 本文通过阅读源码分析了 Spring 初始化 Bean 实例过程中执行 Bean 初始化方法的过程。

image.png

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 26:Bean 早期实例处理及属性注入

前情提要

上一篇分析了 Spring 完成早期 Bean 实例的创建后的一部分流程,其中包括很重要的属性注入的部分,这一篇接着分析之后的代码。在doCreateBean方法中,属性注入的部分在populateBean方法中完成,本文分析initializeBean方法的源码,它在populateBean执行完成之后执行,完成 Bean 实例的初始化。

// Initialize the bean instance.ObjectexposedObject=bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject=initializeBean(beanName, exposedObject, mbd);
}
catch (Throwableex) {
if (exinstanceofBeanCreationException&&beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
   }
else {
thrownewBeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
   }
}

Bean 实例的初始化

进入initializeBean方法查看源码。

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)protectedObjectinitializeBean(finalStringbeanName, finalObjectbean, @NullableRootBeanDefinitionmbd) {
if (System.getSecurityManager() !=null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
returnnull;
      }, getAccessControlContext());
   }
else {
invokeAwareMethods(beanName, bean);
   }
ObjectwrappedBean=bean;
if (mbd==null||!mbd.isSynthetic()) {
wrappedBean=applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }
try {
invokeInitMethods(beanName, wrappedBean, mbd);
   }
catch (Throwableex) {
thrownewBeanCreationException(
            (mbd!=null?mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
   }
if (mbd==null||!mbd.isSynthetic()) {
wrappedBean=applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }
returnwrappedBean;
}

虽然也是一个很重要的步骤,但是其中的代码并不多,而且仔细分析之后,发现关键代码就是以下的 4 行,从调用的方法名称,也能知道它们的具体作用:

  1. invokeAwareMethods(beanName, bean)执行感知接口的方法。
  2. applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)执行后处理器中初始化之前的处理
  3. invokeInitMethods(beanName, wrappedBean, mbd)执行初始化方法
  4. applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)执行后处理器中初始化之后的处理

接下来逐一分析。

执行感知接口的方法

先看invokeAwareMethods方法。

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethodsprivatevoidinvokeAwareMethods(finalStringbeanName, finalObjectbean) {
if (beaninstanceofAware) {
if (beaninstanceofBeanNameAware) {
         ((BeanNameAware) bean).setBeanName(beanName);
      }
if (beaninstanceofBeanClassLoaderAware) {
ClassLoaderbcl=getBeanClassLoader();
if (bcl!=null) {
            ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
         }
      }
if (beaninstanceofBeanFactoryAware) {
         ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
      }
   }
}

这里的逻辑很简单,如果 Bean 实现了 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware 三个接口中的一个或多个,就调用接口的方法,将对应的属性注入到 Bean 实例中。这里只处理了这三个跟 Bean 本身有关的感知接口,其他的比如 ApplicationContextAware 等感知接口的处理逻辑,其实在容器初始化的过程中,已经通过 BeanPostProcessor 的方式注册到了容器中,可以参考 Spring 源码阅读 12:BeanFactory 预处理 中的「添加感知接口的后处理器」小节。

执行后处理器在初始化前后的处理逻辑

接下来看后处理器的处理逻辑,分别在执行 Bean 初始化方法的前后执行了 BeanPostProcessor 的postProcessBeforeInitializationpostProcessAfterInitialization方法。

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization@OverridepublicObjectapplyBeanPostProcessorsBeforeInitialization(ObjectexistingBean, StringbeanName)
throwsBeansException {
Objectresult=existingBean;
for (BeanPostProcessorprocessor : getBeanPostProcessors()) {
Objectcurrent=processor.postProcessBeforeInitialization(result, beanName);
if (current==null) {
returnresult;
      }
result=current;
   }
returnresult;
}
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization@OverridepublicObjectapplyBeanPostProcessorsAfterInitialization(ObjectexistingBean, StringbeanName)
throwsBeansException {
Objectresult=existingBean;
for (BeanPostProcessorprocessor : getBeanPostProcessors()) {
Objectcurrent=processor.postProcessAfterInitialization(result, beanName);
if (current==null) {
returnresult;
      }
result=current;
   }
returnresult;
}

这两个方法的逻辑除了调用了后处理器的不同方法以外,其他都没什么区别。

在分析 Spring 容器初始化的代码时,曾经详细介绍过 BeanPostProcessor,如果我们需要自定义一些 Bean 初始化前后的处理逻辑,可以开发一个 BeanPostProcessor 的实现类,并将其作为 Bean 添加到配置文件中,在 Spring 初始化容器的过程中,会将所有 BeanPostProcessor 类型的 Bean 都作为后处理器注册到容器中,并且在注册前还进行了排序。因此,在这里执行后处理器逻辑的时候,直接按列表中的顺序执行就可以了。

关于 BeanPostProcessor 的更详细内容,以及 BeanPostProcessor 是如何注册到容器中的,可以参考这篇:Spring 源码阅读 14:注册 BeanPostProcessor

执行 Bean 的初始化方法

接下来看invokeInitMethods方法。

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethodsprotectedvoidinvokeInitMethods(StringbeanName, finalObjectbean, @NullableRootBeanDefinitionmbd)
throwsThrowable {
booleanisInitializingBean= (beaninstanceofInitializingBean);
if (isInitializingBean&& (mbd==null||!mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '"+beanName+"'");
      }
if (System.getSecurityManager() !=null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
               ((InitializingBean) bean).afterPropertiesSet();
returnnull;
            }, getAccessControlContext());
         }
catch (PrivilegedActionExceptionpae) {
throwpae.getException();
         }
      }
else {
         ((InitializingBean) bean).afterPropertiesSet();
      }
   }
if (mbd!=null&&bean.getClass() !=NullBean.class) {
StringinitMethodName=mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&!(isInitializingBean&&"afterPropertiesSet".equals(initMethodName)) &&!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
      }
   }
}

这段代码的逻辑也很简单,主要是两部分:

  1. 如果当前处理的 Bean 实例实现了 InitializingBean 接口,那么调用它的afterPropertiesSet方法。
  2. 从 BeanDefinition 中获取initMethodName,也就是初始化方法名称,在 Bean 实例上执行这个方法。

这两部分分开来讲。

InitializingBean

先看 InitializingBean 接口是什么。

publicinterfaceInitializingBean {
voidafterPropertiesSet() throwsException;
}

接口中只定义了一个afterPropertiesSet方法,也就是说,如果一个 Bean 实现了 InitializingBean 接口,那么它的afterPropertiesSet将会在这个时候被调用。因此,如果我们需要一个 Bean 在初始化的时候执行一段特定的逻辑,可以通过实现 InitializingBean 接口,在afterPropertiesSet实现这些逻辑。

这一部分只针对实现了 InitializingBean 接口的 Bean 实例。

自定义初始化方法

接下来看第二部分。首先会获取 BeanDefinition 的initMethodName成员变量,它其实对应的 XML 配置文件中bean标签的init-method属性,这个属性的值对应类中的一个同名的无参方法。配置方法如下:

<beanid="user"class="xxx.User"init-method="init"/>

获取到方法名,在执行之前,还要进行以下一系列判断:

  • 方法名不能是空的
  • 如果当前的 Bean 实现了 InitializingBean 接口,且这里配置的方法名是afterPropertiesSet,那么,这个方法在之前已经执行过了,这里不在执行。
  • 通过isExternallyManagedInitMethod方法判断,如果是外部管理的初始化方法,则不在此处执行。

判断无误后,会通过invokeCustomInitMethod执行这个自定义的初始化方法。这个方法的代码比较多,但是逻辑比较简单,就是通过反射,执行方法。

总结

至此,initializeBean方法中的源码就分析完了。完成了初始化的方法之后,Spring 创建和初始化 Bean 实例的流程也接近了尾声,后面还有一些最后的处理工作,留到下一篇中进行分析。

目录
相关文章
|
14天前
|
Java 应用服务中间件 Nacos
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
28 0
|
17天前
|
监控 数据可视化 安全
一套成熟的Spring Cloud智慧工地平台源码,自主版权,开箱即用
这是一套基于Spring Cloud的智慧工地管理平台源码,具备自主版权,易于使用。平台运用现代技术如物联网、大数据等改进工地管理,服务包括建设各方,提供人员、车辆、视频监控等七大维度的管理。特色在于可视化管理、智能报警、移动办公和分布计算存储。功能涵盖劳务实名制管理、智能考勤、视频监控AI识别、危大工程监控、环境监测、材料管理和进度管理等,实现工地安全、高效的智慧化管理。
|
7天前
|
消息中间件 安全 Java
在Spring Bean中,如何通过Java配置类定义Bean?
【4月更文挑战第30天】在Spring Bean中,如何通过Java配置类定义Bean?
16 1
|
8天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
|
8天前
|
消息中间件 安全 Java
探索|Spring并行初始化加速的思路和实践
作者通过看过的两篇文章发现实现Spring初始化加速的思路和方案有很多类似之处,通过本文记录一下当时的思考和实践。
|
10天前
|
前端开发 Java 数据格式
【Spring系列笔记】定义Bean的方式
在Spring Boot应用程序中,定义Bean是非常常见的操作,它是构建应用程序的基础。Spring Boot提供了多种方式来定义Bean,每种方式都有其适用的场景和优势。
29 2
|
12天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
13天前
|
安全 Java Maven
[AIGC] Spring Boot中的切面编程和实例演示
[AIGC] Spring Boot中的切面编程和实例演示
|
2月前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
49 0
|
2月前
|
缓存 安全 Java
Spring Boot 面试题及答案整理,最新面试题
Spring Boot 面试题及答案整理,最新面试题
131 0