前言

本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本。因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析。

本篇文章主要介绍 Spring IoC 容器中 bean 的初始化阶段。

正文

我们在Spring IoC bean 的创建一文中分析创建 bean 实例的主要流程,此时创建出来的 bean 还是个属性未赋值的实例,在创建完之后会进入 populateBean() 方法,即进入属性赋值阶段。我们简单回顾一下,上次分析过的 doCreateBean() 方法:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {

    // 实例化 bean
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// 如果bean的作用域是singleton,则需要移除未完成的FactoryBean实例的缓存
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 通过构造函数反射创建bean的实例,但是属性并未赋值,见下文详解
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 获取bean的实例
final Object bean = instanceWrapper.getWrappedInstance();
// 获取bean的类型
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
} synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// BeanDefinition 合并后的回调,见下文详解
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
// 省略异常处理...
mbd.postProcessed = true;
}
} // bean的作用域是单例 && 允许循环引用 && 当前bean正在创建中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
// 如果允许bean提前曝光
if (earlySingletonExposure) {
// 将beanName和ObjectFactory形成的key-value对放入singletonFactories缓存中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
} Object exposedObject = bean;
try {
// 给 bean 的属性赋值
populateBean(beanName, mbd, instanceWrapper);
// 初始化 bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
// 省略部分代码
}

上篇文章分析了 populateBean() 方法,这次我们总店分析 initializeBean() 方法。

bean 的初始化

AbstractAutoCapableBeanFactory#initializeBean

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {

    // BeanAware的接口回调,见下文详解
invokeAwareMethods(beanName, bean); Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// BeanPostProcessor的postProcessBeforeInitialization()回调,也就是bean初始化前的回调
// 在 ApplicationContextAwareProcessor实现的postProcessBeforeInitialization方法中会执行
// ApplicationContext Aware的接口回调。
// InitDestoryAnnotationBeanPostProcessor的postProcessBeforeInitialization()中会执行
// 标注了@PostConstruct注解的方法。
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
} try {
// 调用bean的自定义初始化方法,如afterPropertiesSet,XML中的init属性指定的方法等
invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// BeanPostProcessor的postProcessAfterInitialization()回调,也就是bean初始化后的回调
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
} return wrappedBean;
}

Aware 接口回调

AbstractAutowireCapableBeanFactory#invokeAwareMethods

private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
// BeanNameAware接口方法回调
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
// BeanClassLoaderAware接口方法回调
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
// BeanFactoryAware接口方法回调
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware)bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}

通过实现这些 Aware 接口的 bean 的被初始化之前,可以取得一些相对应的资源,比如 beanNamebeanFactory 等。

bean 的初始化前回调

AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {

    Object result = existingBean;
// 遍历所有注册的BeanPostProcessor实现类,调用postProcessBeforeInitialization方法
for (BeanPostProcessor processor : getBeanPostProcessors()) {
// 在bean初始化方法执行前,调用postProcessBeforeInitialization方法
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}

上面方法主要是调用了 BeanPostProcessorpostProcessBeforeInitialization() 方法,下面我们看一下 BeanPostProcessor 接口的定义:

public interface BeanPostProcessor {

    /**
* bean初始化前调用,此时bean已经实例化并且属性已经赋值,Aware接口已经回调;返回非 {@code null} 会使用返回的bean
*/
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
} /**
* bean初始化后调用,返回非 {@code null} 会使用返回的bean
*/
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
} }

调用初始化方法

AbstractAutowireCapableBeanFactory#invokeInitMethods

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable {

    // bean是否实现InitializingBean接口
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
// 调用afterPropertiesSet方法
((InitializingBean) bean).afterPropertiesSet();
} // 调用自定义的init方法,例如XML中init-method属性设置的方法
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}

我们知道设置 bean 的初始化方法其实有三种方式 @PostConstructInitializingBean、自定义初始化方法,一个 bean 同时实现这三种方式时,调用顺序如下:

  1. @PostConstruct
  2. InitializingBean#afterPropertiesSet()
  3. 自定义初始化方法

从上面方法可以很容易的看出 InitializingBean 接口的 afterPropertiesSet() 方法先于自定义初始化方法调用,那么 @PostConstruct 注解标注的方法在何时调用的呢?玄机就在上面介绍的 BeanPostProcessor 接口,InitDestroyAnnotationBeanPostProcessor 实现了该接口并重写了 postProcessBeforeInitialization() 方法调用了标注 @PostConstruct 注解的方法。我会在后续文章分析其实现。

bean 初始化后回调

AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {

    Object result = existingBean;
// 遍历所有注册的BeanPostProcessor实现类,调用postProcessAfterInitialization方法
for (BeanPostProcessor processor : getBeanPostProcessors()) {
// 在bean初始化方法执行后,调用postProcessBeforeInitialization方法
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}

总结

本篇文章主要分析了 Spring IoC bean 的初始化阶段流程,Spring 在此阶段也提供了2个扩展点;分别是 bean 的初始化前和初始化后,也就是 BeanPostProcessor 接口,该接口十分重要其它 processor 接口都是直接或间接在此接口上扩展出来的。

最后,我模仿 Spring 写了一个精简版,代码会持续更新。地址:https://github.com/leisurexi/tiny-spring

Spring IoC bean 的初始化的更多相关文章

  1. Spring IoC容器的初始化过程

    Spring IoC容器的初始化包括 BeanDefinition的Resource定位.载入和注册 这三个基本的过程.IoC容器的初始化过程不包含Bean依赖注入的实现.Bean依赖的注入一般会发生 ...

  2. Spring IoC bean 的创建(上)

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ...

  3. 小马哥讲Spring栈核心编程思想 Spring IoC+Bean+Framework

    小马哥出手的Spring栈核心编程思想课程,可以说是非常专业和权威的Spring课程.课程主要的方向与核心是Spring Framework总览,带领同学们重新认识重新认识IoC,Spring IoC ...

  4. Spring IOC容器的初始化-(二)BeanDefinition的载入和解析

    前言 1.在讲BeanDefinition的载入和解析之前,我们先来看看什么是BeanDefinition. Bean对象在Spring中是以BeanDefinition来描述的,也就是说在Sprin ...

  5. Spring IOC容器的初始化—(一)Resource定位

    前言 上一篇博文“ Spring IOC是怎样启动的 ”中提到了refresh()方法,这个就是容器初始化的入口.容器初始化共有三个阶段: 第一阶段:Resource定位 第二阶段:BeanDefin ...

  6. Spring IoC bean 的加载

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ...

  7. Spring 源码 (1)Spring IOC Bean 创建的整体流程

    Spring IOC 中涉及的重要接口 BeanDefinition Bean的描述信息,实现类包括 RootBeanDefinition 和 GenericBeanDefinition,Bean的描 ...

  8. Spring IOC bean加载过程

    首先我们不要在学习Spring的开始产生畏难情绪.Spring没有臆想的那么高深,相反,它帮我们再项目开发中制定项目框架,简化项目开发.它的主要功能是将项目开发中繁琐的过程流程化,模式化,使用户仅在固 ...

  9. Spring IOC容器的初始化-(三)BeanDefinition的注册

    ---恢复内容开始--- 前言 在上一篇中有一处代码是BeanDefiniton注册的入口,我们回顾一下. 1.BeanDefiniton在IOC容器注册 首先我们回顾两点,1. 发起注册的地方:2. ...

随机推荐

  1. postman接口超时设置,用于debug等断点调试

    Settings->General->Request Timeout in ms(0 for infinity):设置请求超时的时间,默认为0

  2. pdf文件处理--QPDF

    1.分割pdf文件 从原文件中取出n-m页,保留原文件的目录格式: qpdf infile.pdf --pages . n-m -- outfile.pdf 从原文件中取出n-m页,不保留原文件的目录 ...

  3. 0.大话Spring Cloud

    天天说Spring cloud ,那到底它是什么? 定义 它不是云计算解决方案 它是一种微服务开发框架 它是(快速构建分布式系统的通用模式的)工具集 它基于Spring boot 构建开发 它是云原生 ...

  4. Thread和Runnable

    继承Thread类不能实现资源共享.(程序启动了三个线程,但是3个线程却分别卖了各自的5张票,并没有达到资源共享的目的) 实现Runnable接口可以资源共享.(程序启动了三个线程,但是3个线程一共才 ...

  5. Linux中tomcat的部署

    红帽7如何配置tomcat 1.下载tomcat9.0和java-1.8 tomcat的下载地址: https://tomcat.apache.org/ java1.8是radhat7自带: [roo ...

  6. turtle 画国旗

    代码实现: import turtle import time import os def draw_square(org_x, org_y, x, y): turtle.setpos(org_x, ...

  7. 用python做时间序列预测九:ARIMA模型简介

    本篇介绍时间序列预测常用的ARIMA模型,通过了解本篇内容,将可以使用ARIMA预测一个时间序列. 什么是ARIMA? ARIMA是'Auto Regressive Integrated Moving ...

  8. 西门子S7-300 PLC视频教程(百度网盘)收集于网络-供参考学习

    百度网盘地址: 西门子300 PLC视频教程 群文件里面可以找到. 下载: https://blog.csdn.net/txwtech/article/details/93016190

  9. PHP丨PHP基础知识之流程控制WHILE循环「理论篇」

    昨天讲完FOR循环今天来讲讲他的兄弟WHILE循环!进入正题: while是计算机的一种基本循环模式.当满足条件时进入循环,进入循环后,当条件不满足时,跳出循环.while语句的一般表达式为:whil ...

  10. 基于node的前端项目编译时内存溢出问题

    解决方法: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory JavaScript堆内存不足,这里说的 Jav ...