阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 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. [转帖]Oracle 如何列出正在运行的定时任务

    https://geek-docs.com/oracle/oracle-questions/569_oracle_how_can_i_list_the_scheduled_jobs_running_i ...

  2. 【转帖】读懂什么是RDMA

    一.什么是RDMA1.RDMA主要体现     2.如何理解RDMA和TCP技术的区别?3.使用RDMA的好处包括: 二.什么是RoCE?1. RDMA协议包含:Infiniband(IB)2. 为什 ...

  3. [转帖]一张图搞定redis内存优化及配置

    https://www.jianshu.com/p/3195663af83e   Redis内存优化及配置.png Redis优化及配置 Redis所有的数据都在内存中,而内存又是非常宝贵的资源.常用 ...

  4. buildkit ctr 与 k3s的简单学习

    摘要 前面一部分学习了 buildkit的简单搭建 也学习会了如果build images的简单处理 但是搭建镜像只是万里长征第一步. 如何进行微服务部署,才是关键的第二步. 公司最近使用基于K3S的 ...

  5. echarts控制柱状图柱条的宽度

    barWidth属性 series: [{ name: '销量', type: 'bar', barWidth : 30,//柱图宽度 data: [5, 20, 36, 10, 10, 20] }]

  6. 【踩了一个坑】为什么 golang struct 中的 slice 无法原子赋值

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 有这样一个结构体: type MySt struct{ F ...

  7. ABP-VNext 用户权限管理系统实战02---用户权限表的创建与迁移

    一.表实体建立 1.菜单表 [Comment("菜单表")] [Table("t_identity_menu")] public class Menu : Au ...

  8. linux root用户密码输入正确还是提示access denied

    问题:之前用远程工具连接一直都是好的,第二天上班找开远程工具要输root的密码了,输入用户密码后还是无效,可以确定用户密码是对的,其中有一个远程工具一直是连着的就没有问题. 排查问题: 1.相接用pa ...

  9. Vue基础系列文章05----babel安装与使用

    1.安装babel-node包,运行: 1) npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node 2 ...

  10. vim 从嫌弃到依赖(15)——寄存器

    在计算机里面也有寄存器,计算机中的寄存器是看得见,摸得着的实体,寄存器中存储需要经常访问的一些数据.而vim中也有寄存器的概念,vim中的寄存器是一个虚拟的概念,更像是一块专门用来存储数据的内存缓冲区 ...