阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。

写在开始前的话:

阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了:

  • beans
  • core
  • context

实际上我在博客里贴出来的还只是一部分内容,更多的内容,我放在了个人,fork自 spring 官方源码仓了; 而且对源码的学习,必须是要跟着实际代码层层递进的,不然只是干巴巴的文字味同嚼蜡。

https://gitee.com/bokerr/spring-framework-5.0.x-study

这个仓设置的公共仓,可以直接拉取。

Spring源码阅读系列--全局目录.md

createBean() 的面纱

我们在 AbstractBeanFactory 里找到的 createBean() 只是个 抽象方法,如果使用 IDEA 的朋友,可以:

  • Ctrl + Alt + 鼠标左键

最终在它的子类 AbstractAutowireCapableBeanFactory 中找到的 createBean() 方法的实现

  • String beanName: bean名称
  • RootBeanDefinition mbd: bean配置元数据
  • @Nullable Object[] args: 不认识的一律当作空的处理,它可能只在某些特殊场景下被使用

下边是官方源码 + 个人添加的注释:

/**
* Central method of this class: creates a bean instance,
* populates the bean instance, applies post-processors, etc.
* @see #doCreateBean
*/
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException { if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
// 解析过程实际用到的 bean元数据
RootBeanDefinition mbdToUse = mbd; // Make sure bean class is actually resolved at this point, and
// clone the bean definition in case of a dynamically resolved Class
// which cannot be stored in the shared merged bean definition.
// 笼统的说:根据bean元数据(BeanDefinition) 获取 bean 的class
// 1 若元数据中包含了 类class对象,直接返回。
// 2 否则获取线程的 ClassLoader 根据 bean的 ClassName 得到“类的Class对象”,这个解析过程同样涉及到了缓存,
// 这个 “类的Class”对象,会被当作缓存保存到 bean的元数据(BeanDefinition 中),
// 也就是第一不提到的情形,实际上可能就是 “多例”bean 加载是在重复利用 bean元数据。
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
// 套娃验证 + 类缓存机制; spring 代码的健壮性可见一斑
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
} // Prepare method overrides.
try {
// replace-method / lookup-method 的有效性校验 <若存在>
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
} // 实例化 | 初始化
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
// 调用 "实例化前 - 后置处理器", 解析指定 bean 是否存在初始化前的短路操作
// 因为我们并没有往 XmlBeanFactory 配置,代表任何行为的 "后置处理器" 所以这一步可以跳过
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
// 短路处理,如果:实例化-前置处理, 返回结果不为空,直接将其作为处理结果返回,不再执行: doCreateBean(....)
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
} try {
// 见到 doCreateBean() 代表进入正文了,前边讲循环依赖,讲单例bean时,提到了"三级缓存",其中优先级最低的 "第三级" 缓存就是从
// doCreateBean() 方法中注入的
Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 创建 bean
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}

上边的代码有提到一个概念:实例化前 - 后置处理器,若不了解相关概念可参见下文

7、后置处理器-PostProcessor

本节的介绍止于: doCreateBean()

接下来我们继续......

createBean() 的承包者: doCreateBean()

它除了是 createBean() 的包工头之外,它还是 "三级缓存" 机制的起点;

"三级缓存" 和 "循环依赖的消解" 也是个大活呢。

老规矩,同样是代码加 + 注释的方式展开:

/**
* Actually create the specified bean. Pre-creation processing has already happened
* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
* <p>Differentiates between default bean instantiation, use of a
* factory method, and autowiring a constructor.
*
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @param args explicit arguments to use for constructor or factory method invocation
* @return a new instance of the bean
* @throws BeanCreationException if the bean could not be created
* @see #instantiateBean
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
*/
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException { // Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// ConcurrentMap<String, BeanWrapper> factoryBeanInstanceCache // 1 并发场景下起手式: 单例bean先尝试捞缓存
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); }
if (instanceWrapper == null) { // 2 缓存中没捞到?唔好意思,只能从头开始解析了。根据 beanName 使用对应策略创建 bean, 例如:工厂方法、构造函数自动注入
// 简单来说就是:bean元数据(BeanDefinition),合法性校验: 匹配参数、构造函数/factory-bean, 应用BeanFactory
// 的实例化策略, 实例化指定bean // 返回的bean 是由 Cglib 动态代理生成的,可以参考包装器模式去理解它
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 从包装器中获取 bean 实例
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
// 这里对 bean元数据属性的设置,八九不离十是缓存 (我们在前边搞不好已经见过它也说不定)
mbd.resolvedTargetType = beanType;
} // MergedBeanDefinitionPostProcessor 应用
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) { // 对 BeanDefinition 对象上锁
if (!mbd.postProcessed) { // 双重锁机制,避免重复
try {
// PostProcessor ? 那么明显是个 "后置处理器",不妨从命名猜测下:
// MergedBeanDefinition ? 走到这一步我们已经获取到了bean的实例,那么八九不离十,这里是根据bean的解析结果对
// mbd(bean元数据) 进行类似缓存设置相关的操作、或者进行一些检查性质类似的操作
// 接下来我们进去一探究竟
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
} catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
// 双重锁释放
mbd.postProcessed = true;
}
} // 4 依赖处理
// 这里做了一件事:利用条件进行判断,当前指定加载的bean是否允许提早曝光 // Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 浅浅的按个人的理解翻译下这句话:急切的进行单例缓存,即使在bean的较早的生命周期环节,也允许被循环依赖。
// 这里提到了生命周期,那么可能是在警示 allowCircularReferences 的使用 ,如果开启它,可能导致 bean 在不完全加载时被循环依赖 // 条件: bean是单例 && 允许循环依赖 && bean正在创建 >>> 结果: 是否允许提早曝光
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 三级缓存:它不就来了么? addSingletonFactory方法操作的: singletonFactories 难道不是老熟人么?
// 如果已经忘了可以再去看看,本系列第五篇:单例bean缓存的获取 // 提早曝光: 初始化完成前,将创建包含bean实例的 ObjectFactory对象提早曝光加入工厂
// -> (beanName,new ObjectFactory())
addSingletonFactory(beanName, () -> {
// 应用 InstantiationAwareBeanPostProcessor,在此过程中涉及将AOP的advice,动态织入bean,若无AOP配置则直接返回bean
// 记得全局搜索这个关键词: (SmartInstantiationAwareBeanPostProcessor) 不止提前暴露bean的时候执行这些 "后置处理器"
// 我们定会重逢的 // 此处,当三级缓存被获取时,ObjectFactory().getObject() 执行的内容,最终将由如下的方法承包
return getEarlyBeanReference(beanName, mbd, bean);
});
} // Initialize the bean instance. 【初始化:实例化得到 bean 对象,然后通过初始化操作,进行bean属性的填充。】
Object exposedObject = bean;
try {
// 虽然:bean = instanceWrapper.getWrappedInstance(); 但是需要注意的是,如果bean 被提前暴露了,这里的 bean 可能是被:
// 后置处理器: SmartInstantiationAwareBeanPostProcessor 加工之后的结果
// 这取决于 SmartInstantiationAwareBeanPostProcessor 返回的结果是否还是原始的bean对象。 (记录为保留问题) // 5 属性填充 bean (初始化) 将依赖属性注入,如果引用别的 bean 则递归的去进行 bean 的 初始化
// 方法名直译就是:填充bean,这里的操作基本上,就是从 bean 的元数据 mdb上读取相关属性,并设置到新的 bean 上
populateBean(beanName, mbd, instanceWrapper); // 调用 bean.xml中定义的初始化方法,例如: method-init 等等
exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
} else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
} // 6 循环依赖 检查
// 检查已经加载的bean 是否存在 非 setter 循环依赖
// 单例循环依赖检查 可通过配置消解
if (earlySingletonExposure) { // 如果当前bean 被提前暴露
// 应用三级缓存,读取被提前暴露的 bean
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 缓存读取结果不为空,那么说明存在循环依赖
if (exposedObject == bean) {
// 这里的判断结果表明: 处理循环依赖时提前暴露的bean 跟 被一系列后置处理器加工过后的bean 的 "对象堆内存地址一致"
// 说明 "后置处理器" 的加工,没有导致原始 bean 对象,没有被替换
exposedObject = earlySingletonReference;
}
// allowRawInjectionDespiteWrapping : 在循环引用的情况下,是否需要注入原始bean实例,即使注入的bean最终被包装。
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// 对象地址不等,说明加工过程中,原始的 bean 对象,已经被偷梁换柱了 // 获取当前指定bean 依赖的别的bean名称
String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
// 依赖检测
// dependentBeans 包含的时当前bean 所依赖其它bean
// 1. removeSingletonIfCreatedForTypeCheckOnly = true 表示当前 bean,循环依赖了别的尚处于创建的中的 bean
// <等同提前暴露> // 2. removeSingletonIfCreatedForTypeCheckOnly = false, 表示依赖结构中无环,当前指定bean依赖的其它bean
// 都已经被完全成功的加载 if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
// actualDependentBeans 为空说明依赖不成环,或者依赖的bean 全部都已经被加载成功
// actualDependentBeans 不为空,说明有依赖的bean 未完全加载,那么必定存在循环依赖
if (!actualDependentBeans.isEmpty()) {
// 当前指定bean 存在循环依赖,依赖的其它bean未加载完全 // allowRawInjectionDespiteWrapping 为 false 时:
// 若原始bean对象被后置处理器替换 && 原始bean 已经被当作循环依赖注入了别的bean中
// 那么抛出异常,bean 加载失败
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
} // Register bean as disposable.
try {
// 如果配置了 destroy-method 这里需要注册,以保证对象实例销毁时调用
registerDisposableBeanIfNecessary(beanName, bean, mbd);
} catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
} return exposedObject;
}

别看只有这一个方法,但是里面包含的东西太多太多了:

  1. bean 实例化,默认实例化策略: Cglib <动态代理>
  • 根据 factory-method 或者 factory-bean实例化; 若如相关配置,则配置的应用构造函数,实例化bean;

    若没有配置,构造函数,则应用无参构造函数实例化bean。

虽然这里只有轻飘飘的一句话: 实例化,但是实际操作时却包罗万象; 比如:

a. 应用缓存策略,先确定该 bean 是否被解析过;如果则进入如下的流程;

b. 根据参数个数从 元数据(BeanDefinition) 中匹配: factory-method / 构造函数

c. 参数 [ 类型转换 / 值解析 ]

  1. bean 元数据解析后,如若是第一次被解析,将解析的中间结果,以缓存的形式,在 (BeanDefinition) 中保存

  2. 单例bean: 判断是否允许循环依赖, 如若允许提前暴露,将其置入三级缓存

  3. 属性填充、应用: "实例化后置处理器"

populateBean

  • autowireByName
  • autowireByType

详细内容见:

9、bean属性填充

  1. 初始化方法应用: bean.xml 配置的 init-method

initializeBean

  • invokeAwareMethods
  • applyBeanPostProcessorBeforeInitialization
  • invokeInitMethod
  • applyBeanPostProcessorAfterInitialization

10、initialize-初始化bean

  1. 如果 bean 已经被提前暴露,那么判断:三级缓存中的bean对象 和 被后置处理器加工之后的bean对象,

    堆内存地址是否一致:

    • 如若不一致,还需要应用BeanFactory的配置:allowRawInjectionDespiteWrapping,该配置决定:

      • "实例化后置处理器" 替换了原始bean时,是否允许循环依赖
  2. 如果元数据 bean.xml 中配置了,destroy-method,那么需要同样需要注册该方法到 bean中

    以确保bean的生命周期结束后,能应用正确的销毁动作

总结

本文到此为止,几乎已经讲完了,如何从零开始创建一个 bean;由于篇幅问题,更多细节并未在上述文中完全得到体现。

如下是本人一边阅读源码,一边做笔记的个人 spring 代码仓;如若想要了解更多细节,不妨拉取下方源码,一探究竟。

https://gitee.com/bokerr/spring-framework-5.0.x-study

  • 声明:代码中的注释仅仅代表个人观点,该代码仓也仅用作个人学习所用,如有谬误感谢指正。

《系列二》-- 6、从零开始的 bean 创建的更多相关文章

  1. Spring框架系列(二)--装配和注入Bean

    企业日常开发中,几乎都是Spring系的框架,无论是SSM.还是现在大火的SpringBoot+JPA/MyBatis,使用最大的目的就是简化开发 基本模块: 核心容器:Beans.Core.Cont ...

  2. 【微服务】之二:从零开始,轻松搞定SpringCloud微服务系列--注册中心(一)

    微服务体系,有效解决项目庞大.互相依赖的问题.目前SpringCloud体系有强大的一整套针对微服务的解决方案.本文中,重点对微服务体系中的服务发现注册中心进行详细说明.本篇中的注册中心,采用Netf ...

  3. Spring源码系列(二)--bean组件的源码分析

    简介 spring-bean 组件是 Spring IoC 的核心,我们可以使用它的 beanFactory 来获取所需的对象,对象的实例化.属性装配和初始化等都可以交给 spring 来管理. 本文 ...

  4. Storm系列(二):使用Csharp创建你的第一个Storm拓扑(wordcount)

    WordCount在大数据领域就像学习一门语言时的hello world,得益于Storm的开源以及Storm.Net.Adapter,现在我们也可以像Java或Python一样,使用Csharp创建 ...

  5. [Unity3D插件]2dtoolkit系列二 动画精灵的创建以及背景图的无限滚动

    经过昨天2dtoolkit系列教程一的推出,感觉对新手还有有一定的启发作用,引导学习使用unity 2dToolKit插件的使用过程,今天继续系列二——动画精灵的创建,以及背景图的无限循环滚动,在群里 ...

  6. VSTO之旅系列(二):创建Excel解决方案

    原文:VSTO之旅系列(二):创建Excel解决方案 本专题概要 引言 创建VSTO项目 Excel对象模型 创建Excel外接程序 创建Excel文档级自定义项 小结 一.引言 也许很多朋友都没有听 ...

  7. windows下mongodb基础玩法系列二CURD操作(创建、更新、读取和删除)

    windows下mongodb基础玩法系列 windows下mongodb基础玩法系列一介绍与安装 windows下mongodb基础玩法系列二CURD操作(创建.更新.读取和删除) windows下 ...

  8. Spring读书笔记——bean创建(下)

    有关Spring加载bean系列,今天这是最后一篇了,主要接上篇对于从Spring容器中获取Bean的一些细节实现的补充. <Spring读书笔记--bean加载>--Spring如何加载 ...

  9. Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能

    Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 我的本意是第二篇写Mob的shareSD ...

  10. swoft| 源码解读系列二: 启动阶段, swoft 都干了些啥?

    date: 2018-8-01 14:22:17title: swoft| 源码解读系列二: 启动阶段, swoft 都干了些啥?description: 阅读 sowft 框架源码, 了解 sowf ...

随机推荐

  1. 使用buildx在x86机器上面编译arm64架构的Docker镜像

    buildx 多架构编译 安装docker 下载docker 下载buildx 安装架构支持 docker run --privileged --rm tonistiigi/binfmt --inst ...

  2. 【转帖】SmartNIC — TSO、GSO、LRO、GRO 技术

    目录 文章目录 目录 TSO(TCP Segmentation Offload) GSO(Generic Segmentation Offload) LRO(Large Receive Offload ...

  3. Intel 第四代志强可扩展SKU

  4. OpenPower机器上面搭建RabbitMQ 以及简单进行用户配置的方法

    OpenPower机器上面搭建RabbitMQ 以及简单进行用户配置的方法 公司有一台性能比较好的power机器. 同事要求安装rabbitmq 今天尝试进行了一下处理 公司里面有网络有相应的源 性能 ...

  5. 【Mysql】复合主键和联合主键的区别

    复合主键: create table index_test ( a int not null, b int not null, c int not null, d int null, primary ...

  6. 强化学习从基础到进阶-常见问题和面试必知必答[7]:深度确定性策略梯度DDPG算法、双延迟深度确定性策略梯度TD3算法详解

    强化学习从基础到进阶-常见问题和面试必知必答[7]:深度确定性策略梯度DDPG算法.双延迟深度确定性策略梯度TD3算法详解 1.核心词汇 深度确定性策略梯度(deep deterministic po ...

  7. 4.3 Windows驱动开发:监控进程与线程对象操作

    在内核中,可以使用ObRegisterCallbacks这个内核回调函数来实现监控进程和线程对象操作.通过注册一个OB_CALLBACK_REGISTRATION回调结构体,可以指定所需的回调函数和回 ...

  8. 驱动开发:WinDBG 枚举SSDT以及SSSDT地址

    在前面的博文<驱动开发:内核读取SSDT表基址>中已经教大家如何寻找SSDT表基地址了,今天给大家分享两个适用于WinDBG调试器上的脚本文件,该脚本文件可以很好的枚举出当前系统内的SSD ...

  9. Python 字符串与字节数组转换

    整数之间的进制转换: 10进制转16进制: hex(16) ==> 0x10 16进制转10进制: int('0x10', 16) ==> 16 类似的还有oct(), bin() 字符串 ...

  10. P9989 [Ynoi Easy Round 2023] TEST_69 题解

    题目链接: [Ynoi Easy Round 2023] TEST_69 首先GCD有比较良好的一些性质.我们观察到一次 \(GCD(a_i,x)\) 操作,会有以下两种变化. 如果 \(x \bmo ...