《系列二》-- 6、从零开始的 bean 创建

系列,从零开始,bean,创建 · 浏览次数 : 7

小编点评

**代码解析摘要** 这代码主要涉及以下几个方面: * **bean 实例化**:使用工厂方法或构造函数实例化bean。 * **默认实例化策略**:使用 `Cglib` 的 `factory-method` 或 `factory-bean` 方法进行默认实例化。 * **参数转换和值解析**:使用 ``bean` 元数据解析参数,并进行类型转换和值解析。 * **缓存策略**:使用三级缓存将bean对象缓存起来。 * **``bean` 元数据解析**:使用 ``bean` 元数据解析参数,并进行缓存和应用。 * **``bean` 元数据初始化**:在 ``bean` 元数据中保存单例bean的初始化参数。 * **销毁策略**:使用 ``bean` 元数据进行销毁。 **个人代码笔记** * 这代码的注释比较少,缺少详细的说明。 * 代码中有许多复杂的逻辑,需要对代码进行深入的理解才能完全解析。 * 需要阅读相关代码库的文档,才能完全了解代码的功能。 **其他资源** * 代码的 Gitee 仓库:`bokerr` * Spring Framework 5.0.x 的官方文档:`docs` * Spring 框架的学习资源:`learning`

正文

阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 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 创建相似的内容:

《系列二》-- 6、从零开始的 bean 创建

[TOC] > 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。 写在开始前的话: 阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了

C#的基于.net framework的Dll模块编程(二) - 编程手把手系列文章

今天继续这个系列博文的编写。接上次的篇幅,这次介绍关于C#的Dll类库的创建的内容。因为是手把手系列,所以对于需要入门的朋友来说还是挺好的,下面开始咯: 一、新建Dll类库; 这里直接创建例子的Dll类库项目,至于项目文件目录的存放布局后面的例子中会介绍。 在解决方案资源管理器上鼠标右键,选择“添加

[转帖]Django系列3-Django常用命令

文章目录 一. Django常用命令概述二. Django常用命令实例2.1 help命令2.2 version2.3 check2.4 startproject2.5 startapp2.6 runserver2.7 shell2.8 migrations2.8.1 makemigrations2

机器学习算法(一):1. numpy从零实现线性回归

系列文章目录 机器学习算法(一):1. numpy从零实现线性回归 机器学习算法(一):2. 线性回归之多项式回归(特征选取) @目录系列文章目录前言一、理论介绍二、代码实现1、导入库2、准备数据集3、定义预测函数(predict)4 代价(损失)函数5 计算参数梯度6 批量梯度下降7 训练8 可视

[转帖]总结:Springboot监控Actuator相关

一、介绍 由于项目中使用的仍然是比较老旧的1.5.6版本,所以本文是基于此版本进行描述。 二、Actuator使用 ActuatorActuator是Spring Boot提供的对应用系统的监控和管理的集成功能,可以查看应用配置的详细信息,例如自动化配置信息、创建的Spring beans信息、系统

Cilium系列-6-从地址伪装从IPtables切换为eBPF

## 系列文章 * [Cilium 系列文章](https://ewhisper.cn/tags/Cilium/) ## 前言 将 Kubernetes 的 CNI 从其他组件切换为 Cilium, 已经可以有效地提升网络的性能. 但是通过对 Cilium 不同模式的切换/功能的启用, 可以进一步提

< Python全景系列-6 > 掌握Python面向对象编程的关键:深度探索类与对象

Python全景系列的第六篇,本文将深入探讨Python语言中的核心概念:类(Class)和对象(Object)。我们将介绍这些基本概念,然后通过示例代码详细展示Python中的类和对象如何工作,包括定义、实例化和修改等操作。本文将帮助您更深入地理解Python中的面向对象编程(OOP),并从中提出一些不常见但很有用的技术观点。

云原生最佳实践系列 6:MSE 云原生网关使用 JWT 进行认证鉴权

01 方案概述 MSE 网关可以为后端服务提供转发路由能力,在此基础上,一些敏感的后端服务需要特定认证授权的用户才能够访问。MSE 云原生网关致力于提供给云上用户体系化的安全解决方案,其中 JWT 认证能力是在 Json Web Token 这种结构化令牌的基础上实现了一套基于用户体系对用户的 AP

详解Web应用安全系列(6)安全配置错误

Web攻击中的安全配置错误漏洞是一个重要的安全问题,它涉及到对应用程序、框架、应用程序服务器、Web服务器、数据库服务器等组件的安全配置不当。这类漏洞往往由于配置过程中的疏忽或错误,使得攻击者能够未经授权地访问系统数据或执行系统功能。 安全配置错误类漏洞是指在对Web应用及相关组件进行安全配置时,由

MQ系列7:消息通信,追求极致性能

MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 MQ系列6:消息的消费 1 介绍 前面的章节我学习了 NameServer的原理,消息的生产发送,以及消息