21--Spring通過無參構(gòu)造方法實(shí)例化單例bean

上一節(jié)分析了Spring實(shí)例化單例bean的準(zhǔn)備工作,而且已經(jīng)接觸到了真正創(chuàng)建bean的方法doCreateBean,本小節(jié)分析Spring是如何實(shí)例化bean的。

引言,doCreateBean方法簡析
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {

    // Instantiate the bean.
    // ① 實(shí)例化bean
    BeanWrapper instanceWrapper = null;
    // 注意factoryBeanInstanceCache是ConcurrentMap,remove方法會返回刪除的鍵值(如果不存在返回null)
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    // 如果factoryBeanInstanceCache沒有緩存對應(yīng)的BeanWrapper,則重新創(chuàng)建bean實(shí)例
    if (instanceWrapper == null) {
        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.
    // ② 允許MergedBeanDefinitionPostProcessor后處理器修改已合并的bean定義。
    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;
        }
    }

    // Eagerly cache singletons to be able to resolve circular references
    // even when triggered by lifecycle interfaces like BeanFactoryAware.
    // ③ 提前緩存ObjectFactory以解決bean之間的循環(huán)依賴
    // mbd.isSingleton()->是否單例
    // allowCircularReferences->是否允許循環(huán)依賴
    // isSingletonCurrentlyInCreation->該bean是否創(chuàng)建中
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // Initialize the bean instance.
    // ④ 初始化bean實(shí)例 這里大家要與第①步區(qū)分開,到這里bean已經(jīng)完成了實(shí)例化,但是還沒有完成初始化的操作,例如bean的屬性填充
    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);
        }
    }

    // ⑤ 循環(huán)依賴檢查
    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }
                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.
    try {
        // ⑥ 根據(jù)bean的作用域注冊bean
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }
    // ⑦ 返回bean實(shí)例
    return exposedObject;
}

步驟如下:

  • ① 實(shí)例化bean
  • ② 允許MergedBeanDefinitionPostProcessor后處理器修改已合并的bean定義。
  • ③ 提前緩存ObjectFactory以解決bean之間的循環(huán)依賴
  • ④ 初始化bean實(shí)例 這里大家要與第①步區(qū)分開,到這里bean已經(jīng)完成了實(shí)例化,但是還沒有完成初始化的操作,例如bean的屬性填充
  • ⑤ 循環(huán)依賴檢查
  • ⑥ 根據(jù)bean的作用域注冊bean
  • ⑦ 返回bean實(shí)例

這些步驟中涉及的知識點(diǎn)很多,我們逐步分析。

1.實(shí)例化bean

createBeanInstance方法完成了對bean的實(shí)例化操作,打開該方法。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {

    // 確保此時(shí)beanClass已經(jīng)被解析
    Class<?> beanClass = resolveBeanClass(mbd, beanName);

    // beanClass不為空,且beanClass的修飾符為不為public,且不允許訪問非公共構(gòu)造函數(shù)和方法,則拋出異常
    if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
    }

    // ① Spring5.0新增的實(shí)例化策略,如果設(shè)置了該策略,將會覆蓋構(gòu)造方法和工廠方法實(shí)例化策略
    Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
    if (instanceSupplier != null) {
        return obtainFromSupplier(instanceSupplier, beanName);
    }

    // ② 如果有工廠方法的話,則使用工廠方法實(shí)例化bean
    if (mbd.getFactoryMethodName() != null)  {
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }

    // ③ 當(dāng)創(chuàng)建一個(gè)相同的bean時(shí),使用之間保存的快照
    // 這里可能會有一個(gè)疑問,什么時(shí)候會創(chuàng)建相同的bean呢?
    //      ③-->① 單例模式: Spring不會緩存該模式的實(shí)例,那么對于單例模式的bean,什么時(shí)候會用到該實(shí)例化策略呢?
    //                 我們知道對于IoC容器除了可以索取bean之外,還能銷毀bean,當(dāng)我們調(diào)用xmlBeanFactory.destroyBean(myBeanName,myBeanInstance),
    //                 銷毀bean時(shí),容器是不會銷毀已經(jīng)解析的構(gòu)造函數(shù)快照的,如果再次調(diào)用xmlBeanFactory.getBean(myBeanName)時(shí),就會使用該策略了.
    //      ③-->② 原型模式: 對于該模式的理解就簡單了,IoC容器不會緩存原型模式bean的實(shí)例,當(dāng)我們第二次向容器索取同一個(gè)bean時(shí),就會使用該策略了.
    boolean resolved = false;
    boolean autowireNecessary = false;
    if (args == null) {
        synchronized (mbd.constructorArgumentLock) {
            if (mbd.resolvedConstructorOrFactoryMethod != null) {
                resolved = true;
                autowireNecessary = mbd.constructorArgumentsResolved;
            }
        }
    }
    // 如果該bean已經(jīng)被解析過
    if (resolved) {
        // 使用已經(jīng)解析過的構(gòu)造函數(shù)實(shí)例化
        if (autowireNecessary) {
            return autowireConstructor(beanName, mbd, null, null);
        }
        // 使用默認(rèn)無參構(gòu)造函數(shù)實(shí)例化
        else {
            return instantiateBean(beanName, mbd);
        }
    }

    // ④ 確定需要使用的構(gòu)造函數(shù)
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null ||
            mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
            mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
        return autowireConstructor(beanName, mbd, ctors, args);
    }

    // ⑤ 無任何的特殊處理,則使用默認(rèn)的無參構(gòu)造函數(shù)實(shí)例化bean
    return instantiateBean(beanName, mbd);
}

從該方法里我們看到了Spring實(shí)例化bean的策略:

  • 工廠方法(實(shí)例工廠和靜態(tài)工廠)
  • 構(gòu)造函數(shù)實(shí)例化(無參構(gòu)造和有參構(gòu)造)
  • 通過實(shí)例提供者實(shí)例化(Spring5新增的實(shí)例化策略)

先從最簡單的無參構(gòu)造函數(shù)實(shí)例化分析,因?yàn)槠渌膶?shí)例化策略,如有參構(gòu)造函數(shù)實(shí)例化會涉及到構(gòu)造函數(shù)解析,該過程也是非常復(fù)雜,所以先分析最簡單的無參構(gòu)造函數(shù)實(shí)例化。也就是createBeanInstance方法中的第五步,歷經(jīng)前幾個(gè)步驟的處理之后仍然無法實(shí)例化bean并返回其實(shí)例的話,那么就采用默認(rèn)構(gòu)造函數(shù)實(shí)例化。

2.默認(rèn)構(gòu)造函數(shù)實(shí)例化Bean
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
    try {
        Object beanInstance;
        final BeanFactory parent = this;
        // 1、如果權(quán)限管理器不為空,需要校驗(yàn)
        if (System.getSecurityManager() != null) {
            beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
                    getInstantiationStrategy().instantiate(mbd, beanName, parent),
                    getAccessControlContext());
        }
        else {
            // 2、獲取實(shí)例化策略并實(shí)例化bean
            beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
        }
        // 3、實(shí)例并初始化BeanWrapper對象
        BeanWrapper bw = new BeanWrapperImpl(beanInstance);
        initBeanWrapper(bw);
        return bw;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
    }
}
  • 獲取實(shí)例化策略
    創(chuàng)建一個(gè)類的實(shí)例對象除了通過new關(guān)鍵字之外,還可以通過JDK的反射機(jī)制
    CGLIB動態(tài)代理來創(chuàng)建對象實(shí)例,這也是Spring實(shí)例化bean的兩種策略。所以首先通過getInstantiationStrategy方法來獲取實(shí)例化bean的策略。從下圖中可以看到,如果無特殊配置,Spring將采用CGLIB動態(tài)代理機(jī)制作為實(shí)例化bean的默認(rèn)策略。
image.png
  • 反射機(jī)制和CGLIB使用時(shí)機(jī)
    Spring何時(shí)使用反射何時(shí)使用CGLIB創(chuàng)建bean的實(shí)例呢?答案很簡單,如果沒有使用方法覆蓋(replace-method或lookup-method注入),則直接使用反射創(chuàng)建bean的實(shí)例;否則必須使用CGLIB機(jī)制。Spring通過instantiate方法來確定具體使用哪種機(jī)制。
3. instantiate方法獲取實(shí)例化機(jī)制
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
    // 1、如果沒有使用方法覆蓋(replace-method或lookup-method注入),則直接使用反射創(chuàng)建bean的實(shí)例
    if (!bd.hasMethodOverrides()) {
        Constructor<?> constructorToUse;
        synchronized (bd.constructorArgumentLock) {
            // 嘗試獲取已經(jīng)解析的構(gòu)造方法
            constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
            if (constructorToUse == null) {
                final Class<?> clazz = bd.getBeanClass();
                if (clazz.isInterface()) {
                    throw new BeanInstantiationException(clazz, "Specified class is an interface");
                }
                try {
                    if (System.getSecurityManager() != null) {
                        constructorToUse = AccessController.doPrivileged((PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
                    }
                    else {
                        // 未能獲取到已經(jīng)解析過的構(gòu)造方法,則通過getDeclaredConstructor方法獲取構(gòu)造方法
                        constructorToUse = clazz.getDeclaredConstructor();
                    }
                    bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                }
                catch (Throwable ex) {
                    throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                }
            }
        }
        // 通過BeanUtils類實(shí)例化bean
        return BeanUtils.instantiateClass(constructorToUse);
    }
    // 2、否則必須使用CGLIB實(shí)例化策略
    else {
        return instantiateWithMethodInjection(bd, beanName, owner);
    }
}

判斷的方式很簡單,通過BeanDefinition判斷有沒有replace-method或lookup-method注入即可;如果沒有則默認(rèn)使用反射機(jī)制實(shí)例化bean,否則必須使用CGLIB實(shí)例bean。

4.反射機(jī)制實(shí)例化bean
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
    try {
        ReflectionUtils.makeAccessible(ctor);
        // KotlinDetector,Spring5.0新增的類,用于檢測Kotlin的存在和識別Kotlin類型。
        return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
                KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
    }
    catch (InstantiationException ex) {
        throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
    }
    catch (IllegalAccessException ex) {
        throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
    }
    catch (IllegalArgumentException ex) {
        throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
    }
    catch (InvocationTargetException ex) {
        throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
    }
}

通過ctor.newInstance(args)方法創(chuàng)建了Bean的實(shí)例,后續(xù)代碼已經(jīng)屬于JDK源碼,感興趣的同學(xué)可以自行分析。

5.CGLIB實(shí)例bean

打開我們之前分析的test9(測試replace-method注入)和test10(測試replace-method注入),分析CGLIB實(shí)例化。傳送:07--lookup-method和replace-method注入,這里我們只分析一下replace_method方法。

@Override
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
    return instantiateWithMethodInjection(bd, beanName, owner, null);
}
@Override
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
        @Nullable Constructor<?> ctor, @Nullable Object... args) {
    // Must generate CGLIB subclass...
    return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
}
public Object instantiate(@Nullable Constructor<?> ctor, @Nullable Object... args) {
    // 1、生成增強(qiáng)子類
    Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
    Object instance;
    // 2、實(shí)例化增強(qiáng)子類
    if (ctor == null) {
        instance = BeanUtils.instantiateClass(subclass);
    }
    else {
        try {
            Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
            instance = enhancedSubclassConstructor.newInstance(args);
        }
        catch (Exception ex) {
            throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
                    "Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
        }
    }
    // 3、設(shè)置回調(diào)
    // SPR-10785: set callbacks directly on the instance instead of in the
    // enhanced class (via the Enhancer) in order to avoid memory leaks.
    Factory factory = (Factory) instance;
    factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
            new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
            new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
    return instance;
}

對于增強(qiáng)子類的實(shí)例化,依然采用了jdk的反射機(jī)制。我們回到測試類中。查看生成的實(shí)例。

  • 查看實(shí)例信息:


    image.png
  • 實(shí)例詳細(xì)信息


    image.png

當(dāng)代碼運(yùn)行到originalDog.sayHello("輸出結(jié)果已經(jīng)被替換了。。。");時(shí),會被CglibSubclassingInstantiationStrategy類的intercept方法攔截。

public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
    // 1、獲取覆蓋方法信息
    ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
    Assert.state(ro != null, "ReplaceOverride not found");
    // TODO could cache if a singleton for minor performance optimization
    // 2、實(shí)例化覆蓋方法
    MethodReplacer mr = this.owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
    // 3、調(diào)用覆蓋方法
    return mr.reimplement(obj, method, args);
}

當(dāng)代碼執(zhí)行到第三步時(shí)就會調(diào)用reimplement方法了。

/**
 * @author: LiYanChao
 * @create: 2018-09-06 00:02
 */
public class ReplaceDog implements MethodReplacer {
    @Override
    public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
        System.out.println("Hello, I am a white dog...");

        Arrays.stream(args).forEach(str -> System.out.println("參數(shù):" + str));
        return obj;
    }
}
6.總結(jié)

到這里通過無參構(gòu)造方法實(shí)例化bean就分析完了,這里大家需要記住Spring實(shí)例化bean的方式以及何時(shí)使用何種方式。如果使用了replace-method或lookup-method注入,則直接使用CGLIB實(shí)例化bean,否則直接使用反射實(shí)例化bean。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 春節(jié)期間跟一個(gè)好久不見的朋友敘舊,吃完飯壓了大半天的馬路,沒想到許久不回來,YC的冬天是格外地冷,索性找了個(gè)最近的...
    周先生的瘋?cè)嗽?/span>閱讀 301評論 0 0
  • 以后的以后,想讓我所有滿意的照片都有自己的故事,都有獨(dú)一無二的靈魂。 這是去年一月獨(dú)自留在大理喜洲古鎮(zhèn) ,...
    無雙小姐閱讀 1,390評論 21 19
  • 文字 踩著纖細(xì)的高雅 披著心扉的纓紅 在書頁的傳奇中漫步 那里有草綠花香 夢里都沒見過的糖果樹 書頁終于搭成了小山...
    喬木820閱讀 301評論 0 3
  • 期末考試終于考完了,中午去托輔跟兒子對題,有些題做得不對,也沒怎么說他自己在哪里噼里啪啦的掉眼淚,飯也不吃了,問...
    高譽(yù)恒閱讀 217評論 0 0
  • 開相關(guān)發(fā)中總會遇到短信驗(yàn)證這些操作,這周沒有來得及寫新的東西,借此分享一篇以前學(xué)習(xí)短信驗(yàn)證的筆記,本文使用的是 M...
    躬行之閱讀 559評論 0 0

友情鏈接更多精彩內(nèi)容