阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 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. [转帖]Active Session History (ASH)

    Introduction V$ACTIVE_SESSION_HISTORY DBA_HIST_ACTIVE_SESS_HISTORY Enterprise Manager Performance Pa ...

  2. [转帖]Kafka常见使用场景与Kafka高性能之道

    https://juejin.cn/post/6958997115012186119 消息队列使用场景 队列,在数据结构中是一种先进先出的结构,消息队列可以看成是一个盛放消息的容器,这些消息等待着各种 ...

  3. [转帖]一问带你掌握通过storcli做RAID

    因为系统不支持直接做raid,所以需要使用storcli这个工具来操作.首先把工具上传到服务器任意目录,并使用命令chmod +x storcli64修改文件权限为可执行. 另外可通过命令ln -s ...

  4. [转帖]军备芯片14nm对比5nm,在战场上差距在哪里?

    https://www.eet-china.com/mp/a207185.html 现在全球已经打响科技之战,每个国家都在力求让自己做到足够拔尖.美国商务部长就曾自曝家底说,美国制定两套战略应对在芯片 ...

  5. 最小化安装的CentOS7 上面安装Oracle12C的简单过程

    首先声明自己对静默安装不熟,也害怕初问题,所以不使用静默安装的方式. 因为是最小化安装,所以必须安装GUI界面才可以,以下是过程(早上回忆的,全文字,无截图) 1. 安装GUI界面 yum group ...

  6. Chrome 下载地址

    今天同事找到一个网页 感觉非常好用 这里保存并且推荐一下 https://www.chromedownloads.net/chrome64win-stable/

  7. ClickHouse(06)ClickHouse建表语句DDL详细解析

    目录 当前服务器上创建表(单节点) 语法形式 使用显式架构 从相同结构的表复制创建 从表函数创建 从选择查询创建 分布式集群创建表 临时表 分区表 创建表语句关键字解析 空值或非空修饰符 默认值表达式 ...

  8. Spring源码之XML文件中Bean标签的解析1

    读取XML文件,创建对象 xml文件里包含Bean的信息,为了避免多次IO,需要一次性读取xml文件中所有bean信息,加入到Spring工厂. 读取配置文件 new ClassPathResourc ...

  9. 升级到win11 22h2的体验

    win11 22h2更稳定了 在win11 22h2发布后没多久,我就升级到了这个版本,截止目前已经使用半个月了,谈谈我的使用感受. 总体要比之前的版本更稳定,表现为笔记本风扇不会突然响,突然卡顿,不 ...

  10. go中bufio使用小结

    bufio 前言 例子 bufio 源码解析 Reader对象 实例化 ReadSlice ReadString ReadLine Peek Scanner Give me more data Err ...