美文网首页
3-bean 实例化——3-2 创建 Bean 实例详细逻辑解析

3-bean 实例化——3-2 创建 Bean 实例详细逻辑解析

作者: 鹏程1995 | 来源:发表于2020-02-25 15:56 被阅读0次

概要

过度

我们上节大概过了一下获得 bean 实例的总体思路,本节从单例、原型两种生命周期情况切入,对创建 bean实例 及填充属性、初始化的所有内部逻辑做详细介绍。

内容简介

对创建 bean实例 及填充属性、初始化的所有内部逻辑做详细介绍。

所属环节

创建 Bean 实例及初始化

上下环节

上文:完成 beanId 的一些基本校验,满足了前置 Bean 的实例化和初始化

下文:对获得的初始化好的实例进行FactoryBean&的一通折腾

源码解析

入口

我们直接引一个代码段,包括了单例生命周期和原型生命周期的创建 Bean 的准备操作:

if (mbd.isSingleton()) {
    // 完成对应 bean 实例的创建
    // 单例 bean 需要一系列工序,例如加锁、缓存、注册单例等等
    sharedInstance = getSingleton(beanName, () -> {
        try {
            // 创建 bean 实例
            return createBean(beanName, mbd, args);
        } catch (BeansException ex) {
            // Explicitly remove instance from singleton cache: It might have been put there
            // eagerly by the creation process, to allow for circular reference resolution.
            // Also remove any beans that received a temporary reference to the bean.
            // 构建失败记得清一下你预存的引用,防止出错
            destroySingleton(beanName);
            throw ex;
        }
    });
    // 转化成我们需要的实例类型 【 FactoryBean ?Bean?】
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
    // It's a prototype -> create a new instance.
    // 原型 bean 保存一下就行,不是为了解决循环依赖,只是为了在创建时发现正在创建中,能快速失败
    Object prototypeInstance = null;
    try {
        // 这个保存操作我真 TM 醉了,真的是一切靠约定
        // 好在是 ThreadLocal ,不会出现多个线程同时创建多个导致最后访问出现问题
        // TODO: 这个和单例一样的缓存标记不清楚有什么扩展作用,后面注意一下
        // 至少能确定的是,可以用来做监视器扩展。。。。
        beforePrototypeCreation(beanName);// 存一下,在创建中
        // 原型模式,不用 getSingleton() 那样加锁了,直接创建
        prototypeInstance = createBean(beanName, mbd, args);
    } finally {
        afterPrototypeCreation(beanName);// 创建完,删了标记
    }
    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

单例 Bean

创建 Bean 实例的操作很简单,因为和单例有关的创建钩子、创建完成后的单例注册,都在之前介绍的getSingleton()中记录了(DefaultSingletonBeanRegistory)。我们之前在介绍那个函数时基本都说明白了,这里我们穿进去的工厂方法很简单——调用createBean(),调用失败就销毁它。

原型 Bean

调用createBean,因为没有getSingleton()那样用singletonsCurrentlyInCreation有封装好的状态展示集合。自行调用两个钩子,用线程变量来做一层限制——注意,是线程变量,原型就是用来重复创建的,所以不用跨线程的限制,这里仅仅是为了防止循环依赖。

ThreadLocal变量

private final ThreadLocal<Object> prototypesCurrentlyInCreation =
  new NamedThreadLocal<>("Prototype beans currently in creation");

创建前后的钩子,防止此线程因循环依赖进入循环创建。

/**
 * Callback before prototype creation.
 * <p>The default implementation register the prototype as currently in creation.
 *
 * @param beanName the name of the prototype about to be created
 * @see #isPrototypeCurrentlyInCreation
 */
@SuppressWarnings("unchecked")
protected void beforePrototypeCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    if (curVal == null) {
        this.prototypesCurrentlyInCreation.set(beanName);
    } else if (curVal instanceof String) {
        Set<String> beanNameSet = new HashSet<>(2);
        beanNameSet.add((String) curVal);
        beanNameSet.add(beanName);
        this.prototypesCurrentlyInCreation.set(beanNameSet);
    } else {
        Set<String> beanNameSet = (Set<String>) curVal;
        beanNameSet.add(beanName);
    }
}

/**
 * Callback after prototype creation.
 * <p>The default implementation marks the prototype as not in creation anymore.
 *
 * @param beanName the name of the prototype that has been created
 * @see #isPrototypeCurrentlyInCreation
 */
@SuppressWarnings("unchecked")
protected void afterPrototypeCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    if (curVal instanceof String) {
        this.prototypesCurrentlyInCreation.remove();
    } else if (curVal instanceof Set) {
        Set<String> beanNameSet = (Set<String>) curVal;
        beanNameSet.remove(beanName);
        if (beanNameSet.isEmpty()) {
            this.prototypesCurrentlyInCreation.remove();
        }
    }
}

当然,这里的钩子仅做了记录,防止循环创建请参见上一篇文章。在读单例缓存失败后直接调用了isPrototypeCurrentlyInCreation(beanName)

createBean()创建实例、初始化封装接口

/**
 * Central method of this class: creates a bean instance,
 * populates the bean instance, applies post-processors, etc.
 *
 * @see #doCreateBean
 */
// 此时已经完成了 bean scope 的判断、bean 循环依赖的判断,现在应该是安心创建 bean 实例的环节了
// 创建 bean 实例、初始化、执行后处理方法
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {

    if (logger.isDebugEnabled()) {
        logger.debug("Creating instance of bean '" + beanName + "'");
    }
    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.
    // 1. 确认指向的 bean Class 可以被加载
    // 2. 将 BeanDefinition 复制一份,原因与 CopyOnWriteArrayList 相同,防止访问过程中被修改
    Class<?> resolvedClass = resolveBeanClass(mbd, beanName);

    // 理论上加载完成后如果获得结果不为空,mbd 中缓存的 beanClass 应该存在,但是这里没有
    // 是有其他线程的影响,所以我们进行复制
    if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
        mbdToUse = new RootBeanDefinition(mbd);
        mbdToUse.setBeanClass(resolvedClass);
    }

    // Prepare method overrides.
    try {
        mbdToUse.prepareMethodOverrides(); // 将 bean 中要覆盖的方法进行一次统计及修饰
    } 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 实例不一定是目标的实例,可能是一层代理
        // TODO: 这里是 AOP 的织入点,现在没看懂,后面可以继续看
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
    } catch (Throwable ex) {
        throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                "BeanPostProcessor before instantiation of bean failed", ex);
    }

    try {
        // 根据 beanName,BeanDefinition,入参 进行 bean 实例的创建
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        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);
    }
}

此处的思路和之前 Spring 的常用思路一样——先进行一些创建前的校验、准备工作,然后把具体的创建操作委托给对应的具体类进行:

  • 先看看resolveBeforeInstantiation行不行
  • 上面的不行,就委托给doCreateBean()来进行

这里做的准备操作主要有以下几种:

  1. 确认 Bean 对应的 Class 可加载
  2. 对 BD 中定义的方法覆盖做一下校验,看:
    1. 配置是否合理
    2. 如果情况及其简单就顺手打标,减少后面遍历的开销

校验方法覆盖

/**
 * Validate and prepare the method overrides defined for this bean.
 * Checks for existence of a method with the specified name.
 *
 * @throws BeanDefinitionValidationException in case of validation failure
 */
public void prepareMethodOverrides() throws BeanDefinitionValidationException {
    // Check that lookup methods exists.
    if (hasMethodOverrides()) {
        Set<MethodOverride> overrides = getMethodOverrides().getOverrides();
        synchronized (overrides) {
            for (MethodOverride mo : overrides) {
                prepareMethodOverride(mo);
            }
        }
    }
}

/**
 * Validate and prepare the given method override.
 * Checks for existence of a method with the specified name,
 * marking it as not overloaded if none found.
 *
 * @param mo the MethodOverride object to validate
 * @throws BeanDefinitionValidationException in case of validation failure
 */
protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
    int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
    if (count == 0) {
        throw new BeanDefinitionValidationException(
                "Invalid method override: no method with name '" + mo.getMethodName() +
                        "' on class [" + getBeanClassName() + "]");
    } else if (count == 1) {
        // 只有一个,说明我们要覆盖的方法不需要重写
        // Mark override as not overloaded, to avoid the overhead of arg type checking.
        mo.setOverloaded(false);
    }
}

主要是看一下方法存不存在,如果存在且只有一个就打个标记就好,如果不存在就直接报错,也省的跑一大片创建逻辑了。

doCreateBean()创建实例、初始化实际逻辑

/**
 * 此方法进行 Bean 实例的:
 * 1. 创建【通过工厂方法、根据参数指定构造器、默认构造器】
 * 2. 实例值填充【解决了依赖问题,也解决了循环依赖问题(单例的)】
 * 3. Bean 实例初始化钩子调用
 * 4. 检查循环依赖
 * 5. 注册 Bean 销毁方法/钩子
 *
 * @param beanName bean 的Id
 * @param mbd      BD ,拍平之后的
 * @param args     创建所用的入参
 * @return bean的一个创建完成的实例,初始化好的那种
 * @throws BeanCreationException if the bean could not be created
 * @see #instantiateBean
 * @see #instantiateUsingFactoryMethod
 * @see #autowireConstructor
 */
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
        throws BeanCreationException {

    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        // TODO 此处存疑,需要专门看一下 factoryBeanInstanceCache 的作用及存取
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        // 1. 创建【通过工厂方法、根据参数指定构造器、默认构造器】
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    final Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
        mbd.resolvedTargetType = beanType;
    }

    // Allow post-processors to modify the merged bean definition.
    // 对 mbd 做一些修饰
    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            } catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Post-processing of merged bean definition failed", ex);
            }
            mbd.postProcessed = true; // 标记,此 mbd 完成后处理器的修饰
        }
    }

    // Eagerly cache singletons to be able to resolve circular references
    // even when triggered by lifecycle interfaces like BeanFactoryAware.
    // 如果要解决循环依赖问题,需要提前将实例引用暴露。
    // 暴露出去之后慢慢再进行实例填值、初始化、后处理钩子调用
    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");
        }
        // 把找到的 bean 实例增加到缓存到 ObjectFactory 里去
        // 注意,这里使用在本 Factory 中注册的 SmartInstantiationAwareBeanPostProcessor 的方法获得
        // 对应的提前引用
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        // 填充一些 bean 的属性
        populateBean(beanName, mbd, instanceWrapper);
        // 初始化 bean
        // 这里才叫初始化,初始化特指调用初始化和那些钩子方法,其他的包括填充属性,都叫创建实例
        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);
        }
    }


    // 提前暴露出去了实例引用,但是我们在后续调用初始化钩子时可能会改变bean的地址,这里要做一下修复和回归
    //
    // 注意,我们只是完成了创建这个 bean 实例的操作,但是并没有改变 singleton 的注册状态,也就是说我们现在用 getSingleton()获得的
    // 还是之前放进去的用于提前暴露的引用
    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false); //得到之前提前暴露的实例引用
        if (earlySingletonReference != null) { // 刚开始觉得有点冗余,看了后面的实现感觉还是有必要的
            if (exposedObject == bean) { // 我们一通操作,没有改变引用地址,说明我们后面的操作能即使同步出去
                exposedObject = earlySingletonReference;
            } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { // 我们的一通操作,改变了引用地址【出现了代理或者包裹】
                // 1. 是否提前注入原始的 bean 实例来防止循环引用,即使最终这个 bean 会被包裹【否】
                // 2. 这个提前暴露出去的 bean 已经被人依赖了
                String[] dependentBeans = getDependentBeans(beanName); // 获得依赖这个 bean 的 bean 列表,一个一个修改
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { // 将还没有正式使用的 bean 实例删除
                        // 删除失败,当时创建这个 bean 是有正经用途的
                        actualDependentBeans.add(dependentBean);
                    }
                }
                // 经过层层筛选,我们发现提前暴露的 bean 里是有正经用途的,不能直接删,那就直接 fail-fast 失败,防止出现隐藏的bug造成更大的损失
                if (!actualDependentBeans.isEmpty()) {
                    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 " +
                                    "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                }
            }
        }
    }


    // Register bean as disposable.
    // bean 销毁可能还有一些钩子,进行一些注册
    // 这里先注册销毁的钩子,再注册创建的钩子,防止出现在注册的间隙提前暴露出去并立刻进行销毁的极端情况
    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    } catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }
    // 注册销毁的钩子完成,对外暴露最终的 bean
    // 注意:如果是提前暴露的单例 bean ,是从 singletonFactories 的缓存转移到 earlySingletonObjects 就不再动了,此函数只负责创建 bean ,
    // 最外面我们是把 createBean 当作 singletonFacotry 传到 getSingleton 里面去的,在完成此方法后,外面的 getSingleton 会完成将 对应的
    // bean 实例缓存至 singletonObjects 的。
    return exposedObject;
}

思路总体来说还是比较明确的:

1.png

其中:

  • "解决循环依赖"和"检测解决循环依赖是否成功"两部分直接在doCreateBean中直接体现了。思路比较简单,不再赘述。
  • "修饰BD"这个我们后面专门介绍一下后处理器相关的操作。

扩展

单例提前暴露为何采用 factory形式

  1. 使用函数,更方便定制【站在DefaultSingletonBeanRegistry
  2. 闭包,保证在调用时尽可能穿出此线程执行的最新的引用【纯个人意淫,感觉执行速度这么快,这种分秒必争的情况应该很少】

问题遗留

resolveBeforeInstantiation

传闻这里是 AOP织入点,后面回头看。

factoryBeanInstanceCache的作用、存取场景

后处理器相关的操作

参考文献

相关文章

网友评论

      本文标题:3-bean 实例化——3-2 创建 Bean 实例详细逻辑解析

      本文链接:https://www.haomeiwen.com/subject/axitchtx.html