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

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

  • 声明:代码中的注释仅仅代表个人观点,该代码仓也仅用作个人学习所用,如有谬误感谢指正。
posted @ 2023-06-24 10:05  bokerr  阅读(18)  评论(0编辑  收藏  举报