1. 程式人生 > >踩坑記:根據型別獲取Spring容器中的Bean

踩坑記:根據型別獲取Spring容器中的Bean

在專案開發中遇到了這樣的一個問題:有同事在BeanFactoryPostProcessor的實現方法中寫了類似這樣的一段程式碼:

@Component
public class BeanFactoryPostProcessorImpl implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //就是這一段程式碼
        beanFactory.getBean(GetBeanByType.class);
    }
}

然後導致了一些實現了FactoryBean介面的Bean被提前建立了,當時感覺很奇怪,我只是根據傳入的Class會獲取Bean,為什麼會導致一些實現了FactoryBean介面的Bean也被建立呢?其實即使我們是根據型別從Spring容器中獲取Bean,但是Spring容器在實現的時候,最底層還是會呼叫getBean(java.lang.String, java.lang.Class, java.lang.Object…)這個方法獲取Bean,那麼當我們根據型別從Spring容器中獲取Bean的時候就先找到這個型別對應的在Spring容器中的beanName,問題就出在獲取這個beanName的地方。真正出問題的是AbstractBeanFactory#isTypeMatch(java.lang.String, org.springframework.core.ResolvableType)這個方法。下面我們來分一下獲取beanName的這個過程。
其入口方法為:DefaultListableBeanFactory#getBeanNamesForType(java.lang.Class

    public String[] getBeanNamesForType(Class<?> type) {
        //這裡傳入的三個引數 type是bean的Class 第二個引數的意思是 是否包含非單例的bean
        //第三個引數很重要 這裡傳入的值為true 這裡傳入true 代表允許提前初始化Bean
        //如果這個值為false的話 代表不允許提前初始化Bean 問題就出在這個值為true!!!
        return getBeanNamesForType(type, true, true);
    }
    @Override
    public
String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) { //如果是在容器啟動過程中 這個值為false 如果在容器啟動完成之後 這個值會變為true 這個值的更改在 //DefaultListableBeanFactory#freezeConfiguration 可以看一下它的呼叫關係 //下面的內容就很簡單了 我們主要分析doGetBeanNamesForType這個方法 if (!isConfigurationFrozen() || type == null || !allowEagerInit) { return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit); } //這裡是從快取中獲取,includeNonSingletons是否包含非單例的方法 Map<Class<?>, String[]> cache = (includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType); String[] resolvedBeanNames = cache.get(type); if (resolvedBeanNames != null) { return resolvedBeanNames; } resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true); if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) { cache.put(type, resolvedBeanNames); } return resolvedBeanNames; }

DefaultListableBeanFactory#doGetBeanNamesForType

    private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
        List<String> result = new ArrayList<String>();
        // Check all bean definitions.
        // 注意這裡是迴圈Spring容器中所有的BeanDefinition
        for (String beanName : this.beanDefinitionNames) {
            // Only consider bean as eligible if the bean name
            // is not defined as alias for some other bean.
            //beanName不是別名
            if (!isAlias(beanName)) {
                try {
                    //這裡你可以暫時理解為根據BeanName獲取BeanDefinition
                    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                    // Only check bean definition if it is complete.
                    //下面這個判斷比較長 一個一個的來說一下
                    //mbd.isAbstract()判斷這個類是不是抽象類 預設是false
                    //allowEagerInit  是否允許提前初始化 這個值是傳入的值  我們這裡傳入的值是true 即允許提前初始化
                    //mbd.hasBeanClass()  是否已經有BeanClass了
                    //mbd.isLazyInit()是否允許延遲初始化 預設false
                    //isAllowEagerClassLoading() 是否允許提前載入  上面這四個值是或的關係  只有有一個為true就可以
                    //requiresEagerInitForType 檢查特殊的Bean是否需要提前被初始化 這裡傳入的是工廠方法的名字
                    //我們把這個條件總結一下 如果這個Bean不是一個抽象類,並且(被允許提前初始化 或者 (這個Bean的  
                    //BeanDefinition設定了BeanClass了 或者 這個Bean沒有設定延遲載入 或者 即使這個Bean標註為延遲載入了,也可以被提前載入(全域性變數)  並且 BeanDefinition中的FactoryBeanName是否允許被提前初始化))
                    //根據我們前面的分析 allowEagerInit 的值為true 這個Bean也不是個抽象類,所以會繼續下面的流程
                    if (!mbd.isAbstract() && (allowEagerInit ||
                            ((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&
                                    !requiresEagerInitForType(mbd.getFactoryBeanName()))) {
                        // In case of FactoryBean, match object created by FactoryBean.
                        // 判斷是不是FactoryBean
                        boolean isFactoryBean = isFactoryBean(beanName, mbd);
                        BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
                        // 這個地方是判斷是不是找到匹配的beanName 這裡關鍵的是isTypeMatch這個方法
                        // 在這個方法中會導致一些FactoryBean被提前例項化 所以這裡給我們的一個提示是:
                        // 在Spring容器的啟動階段,呼叫isTypeMatch這個方法去判斷Bean的型別的時候要慎重一些。 
                        // 我們要重點分析isTypeMatch這個方法
                        boolean matchFound =
                                (allowEagerInit || !isFactoryBean ||
                                        (dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
                                (includeNonSingletons ||
                                        (dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
                                isTypeMatch(beanName, type);
                        //如果型別不匹配 並且還是FactoryBean型別的BeanDefinition
                        if (!matchFound && isFactoryBean) {
                            // In case of FactoryBean, try to match FactoryBean instance itself next.
                            //這裡講beanName變為&beanName
                            beanName = FACTORY_BEAN_PREFIX + beanName;
                            //繼續判斷型別匹配不匹配
                            matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
                        }
                        //如果型別相匹配的話 則將beanName放入到結果集中
                        if (matchFound) {
                            result.add(beanName);
                        }
                    }
                }
                catch (CannotLoadBeanClassException ex) {
                }
                catch (BeanDefinitionStoreException ex) {
                }
            }
        }
        //這一部分的處理流程大致相同
        // Check manually registered singletons too.
        for (String beanName : this.manualSingletonNames) {
            try {
                // In case of FactoryBean, match object created by FactoryBean.
                if (isFactoryBean(beanName)) {
                    if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) {
                        result.add(beanName);
                        // Match found for this bean: do not match FactoryBean itself anymore.
                        continue;
                    }
                    // In case of FactoryBean, try to match FactoryBean itself next.
                    beanName = FACTORY_BEAN_PREFIX + beanName;
                }
                // Match raw bean instance (might be raw FactoryBean).
                if (isTypeMatch(beanName, type)) {
                    result.add(beanName);
                }
            }
            catch (NoSuchBeanDefinitionException ex) {
            }
        }
        return StringUtils.toStringArray(result);
    }

下面我們來看看isTypeMatch這個方法的內容


    @Override
    public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException {
        //這裡是對beanName進行轉換 如:如果傳入的beanName是&beanName,則將&去掉。如果傳入的beanName有別名的話,則替換為別名
        String beanName = transformedBeanName(name);

        // Check manually registered singletons.
        //根據beanName獲取bean的例項 前提是其對應的bean已經被例項化了 或者正在例項化中
        Object beanInstance = getSingleton(beanName, false);
        if (beanInstance != null) {
            //如果是FactoryBean型別的例項
            if (beanInstance instanceof FactoryBean) {
                //是否要獲取在FactoryBean中建立的Bean例項 呼叫 getObject getObjectType方法
                //這個地方的判斷條件是 傳入的beanName是否以&開頭 如果以&開頭,則返回true
                if (!BeanFactoryUtils.isFactoryDereference(name)) {
                    //這裡是呼叫FactoryBean的getObjectType方法 來獲取bean的型別
                    Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
                    return (type != null && typeToMatch.isAssignableFrom(type));
                }
                else {
                    //直接獲取FactoryBean型別的例項
                    return typeToMatch.isInstance(beanInstance);
                }
            }
            //判斷beanName是否以&開頭,如果以&開頭的話 則返回true
            else if (!BeanFactoryUtils.isFactoryDereference(name)) {
                //如果beanName不是以&開頭 則直接進行型別比較
                if (typeToMatch.isInstance(beanInstance)) {
                    // Direct match for exposed instance?
                    return true;
                }
                //泛型的處理
                else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
                    // Generics potentially only match on the target class, not on the proxy...
                    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                    Class<?> targetType = mbd.getTargetType();
                    if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance) &&
                            typeToMatch.isAssignableFrom(targetType)) {
                        // Check raw class match as well, making sure it's exposed on the proxy.
                        Class<?> classToMatch = typeToMatch.resolve();
                        return (classToMatch == null || classToMatch.isInstance(beanInstance));
                    }
                }
            }
            return false;
        }
        else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
            // null instance registered
            return false;
        }

        // No singleton instance found -> check bean definition.
        //這裡是從父BeanFactory中進行查詢匹配 查詢匹配的過程是一樣的。
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // No bean definition found in this factory -> delegate to parent.
            return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch);
        }

        // Retrieve corresponding bean definition.
        //根據beanName重新檢索BeanDefinition 這裡返回一個RootBeanDefinition 如果父子容器中存在相同型別的bean會進行合併
        RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
        //解析出 class的型別
        Class<?> classToMatch = typeToMatch.resolve();
        if (classToMatch == null) {
            classToMatch = FactoryBean.class;
        }
        //如果傳入的class型別為FactoryBean型別 則直接返回FactoryBean型別的陣列 否則會將傳入的class的型別和FactoryBean組裝在一起
        Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
                new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch});

        // Check decorated bean definition, if any: We assume it'll be easier
        // to determine the decorated bean's type than the proxy's type.
        //這個地方是獲取最原始的BeanDefinition 不是組裝之後的 RootBeanDefinition
        BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
        //如果dbd 存在並且 beanName不是以&開頭
        if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
            RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
            //這裡是取BeanDefinition中的bean型別
            Class<?> targetClass = predictBeanType(dbd.getBeanName(), tbd, typesToMatch);
            if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
                return typeToMatch.isAssignableFrom(targetClass);
            }
        }
        //獲取目標型別 通常是去BeanDefinition中的resolvedTargetType
        Class<?> beanType = predictBeanType(beanName, mbd, typesToMatch);
        if (beanType == null) {
            return false;
        }

        // Check bean class whether we're dealing with a FactoryBean.
        //如果BeanDefinition中的beanType是FactoryBean型別
        if (FactoryBean.class.isAssignableFrom(beanType)) {
            if (!BeanFactoryUtils.isFactoryDereference(name)) {
                // If it's a FactoryBean, we want to look at what it creates, not the factory class.
                //就是這個地方 會將FactoryBean型別的Bean進行例項化
                //我們來分析這個方法
                beanType = getTypeForFactoryBean(beanName, mbd);
                if (beanType == null) {
                    return false;
                }
            }
        }
        else if (BeanFactoryUtils.isFactoryDereference(name)) {
            // Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean
            // type but we nevertheless are being asked to dereference a FactoryBean...
            // Let's check the original bean class and proceed with it if it is a FactoryBean.
            //這個地方需要注意的是 SmartInstantiationAwareBeanPostProcessor型別的BeanPostProcessor會對返回的Bean型別做修改
            beanType = predictBeanType(beanName, mbd, FactoryBean.class);
            if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType)) {
                return false;
            }
        }

        ResolvableType resolvableType = mbd.targetType;
        if (resolvableType == null) {
            //工廠方法建立的Bean
            resolvableType = mbd.factoryMethodReturnType;
        }
        if (resolvableType != null && resolvableType.resolve() == beanType) {
            return typeToMatch.isAssignableFrom(resolvableType);
        }
        return typeToMatch.isAssignableFrom(beanType);
    }

isTypeMatch這個方法就是進行各種Bean型別的判斷,如是已經例項化的FactoryBean可能會呼叫它的getObjectType方法獲取Bean的型別,或者從BeanDefinition中獲取Bean的型別,並且如果是未例項化的FactoryBean,為了進行Bean型別的判斷會導致FactoryBean的例項化。
下面我們來看看org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getTypeForFactoryBean這個方法的內容:

    protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
        //工程Bean和工廠方法名 根據工廠方法建立Bean
        String factoryBeanName = mbd.getFactoryBeanName();
        String factoryMethodName = mbd.getFactoryMethodName();

        if (factoryBeanName != null) {
            if (factoryMethodName != null) {
                // Try to obtain the FactoryBean's object type from its factory method declaration
                // without instantiating the containing bean at all.
                BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
                if (fbDef instanceof AbstractBeanDefinition) {
                    AbstractBeanDefinition afbDef = (AbstractBeanDefinition) fbDef;
                    if (afbDef.hasBeanClass()) {
                        Class<?> result = getTypeForFactoryBeanFromMethod(afbDef.getBeanClass(), factoryMethodName);
                        if (result != null) {
                            return result;
                        }
                    }
                }
            }
            // If not resolvable above and the referenced factory bean doesn't exist yet,
            // exit here - we don't want to force the creation of another bean just to
            // obtain a FactoryBean's object type...
            if (!isBeanEligibleForMetadataCaching(factoryBeanName)) {
                return null;
            }
        }

        // Let's obtain a shortcut instance for an early getObjectType() call...
        //getSingletonFactoryBeanForTypeCheck和getNonSingletonFactoryBeanForTypeCheck這兩個方法是建立FactoryBean的例項了
        FactoryBean<?> fb = (mbd.isSingleton() ?
                getSingletonFactoryBeanForTypeCheck(beanName, mbd) :
                getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));

        if (fb != null) {
            // Try to obtain the FactoryBean's object type from this early stage of the instance.
            //呼叫getObjectType方法
            Class<?> result = getTypeForFactoryBean(fb);
            if (result != null) {
                return result;
            }
            else {
                // No type found for shortcut FactoryBean instance:
                // fall back to full creation of the FactoryBean instance.
                return super.getTypeForFactoryBean(beanName, mbd);
            }
        }

        if (factoryBeanName == null && mbd.hasBeanClass()) {
            // No early bean instantiation possible: determine FactoryBean's type from
            // static factory method signature or from class inheritance hierarchy...
            if (factoryMethodName != null) {
                return getTypeForFactoryBeanFromMethod(mbd.getBeanClass(), factoryMethodName);
            }
            else {
                return GenericTypeResolver.resolveTypeArgument(mbd.getBeanClass(), FactoryBean.class);
            }
        }

        return null;
    }

上面這個方法的內容是:如果是以工廠方法來建立Bean的話,則從工廠方法中返回bean型別,如果是FactoryBean型別的Bean的話,會例項化FactoryBean型別的Bean。例項化是在getSingletonFactoryBeanForTypeCheck或getNonSingletonFactoryBeanForTypeCheck方法中完成的。

    private FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
        synchronized (getSingletonMutex()) {
            //先從快取中獲取
            BeanWrapper bw = this.factoryBeanInstanceCache.get(beanName);
            if (bw != null) {
                return (FactoryBean<?>) bw.getWrappedInstance();
            }
            //這個bean是不是正在建立中
            if (isSingletonCurrentlyInCreation(beanName) ||
                    (mbd.getFactoryBeanName() != null && isSingletonCurrentlyInCreation(mbd.getFactoryBeanName()))) {
                return null;
            }

            Object instance = null;
            try {
                // Mark this bean as currently in creation, even if just partially.
                //判斷是不是正在建立中的bean
                beforeSingletonCreation(beanName);
                // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
                //Bean例項化之前的一些處理 由Spring容器中InstantiationAwareBeanPostProcessor型別的例項來完成
                instance = resolveBeforeInstantiation(beanName, mbd);
                if (instance == null) {
                    //建立Bean的例項 看到這裡就很明瞭了
                    bw = createBeanInstance(beanName, mbd, null);
                    instance = bw.getWrappedInstance();
                }
            }
            finally {
                // Finished partial creation of this bean.
                afterSingletonCreation(beanName);
            }

            FactoryBean<?> fb = getFactoryBean(beanName, instance);
            if (bw != null) {
                this.factoryBeanInstanceCache.put(beanName, bw);
            }
            return fb;
        }
    }

按照我們上面的分析,在doGetBeanNamesForType這個方法中,

private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) 

allowEagerInit是一個非常重要的引數,這個引數可以控制要不要提前例項化一些Bean。為什麼這樣說呢?因為要不要呼叫isTypeMatch這個方法在很大程度上是由這個條件控制的

if (!mbd.isAbstract() && (allowEagerInit ||((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&!requiresEagerInitForType(mbd.getFactoryBeanName())))

通常我們的BeanDefinition都不是抽象類的,所以第一個條件為true,我們來看第二個條件,就是後面的那一大堆

(allowEagerInit ||((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&!requiresEagerInitForType(mbd.getFactoryBeanName()))

這個條件分為兩部分,是一個或的條件。如果allowEagerInit 為true的話,則整個條件為true,如果allowEagerInit 為false呢?mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()這三個條件基本上也是為true但是這個地方是一個與的條件,那麼還要判斷一下requiresEagerInitForType(mbd.getFactoryBeanName())這個方法是返回的true還是false。

    private boolean requiresEagerInitForType(String factoryBeanName) {
        //factoryBeanName不為null  isFactoryBean(factoryBeanName) 是否為FactoryBeanBean 
        //containsSingleton(factoryBeanName)) 如果這個Bean已經被例項化了 這個返回的值為true
        return (factoryBeanName != null && isFactoryBean(factoryBeanName) && !containsSingleton(factoryBeanName));
    }

因為我們在這篇文章中的分析是根據型別獲取Bean會導致FactoryBean型別的Bean被提前例項化,所以factoryBeanName不為null,isFactoryBean(factoryBeanName) 為true,!containsSingleton(factoryBeanName)同樣為true。綜合requiresEagerInitForType這個方法返回true,那麼

((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&!requiresEagerInitForType(mbd.getFactoryBeanName())

會返回false,那這個這一大段的邏輯也就返回false了。
所以我們在根據型別獲取Bean的時候要謹慎一點,如果整個Spring容器已經啟動完成之後,根據型別獲取Bean是沒有問題的,如果是在Spring容器的啟動過程中根據型別獲取Bean可能會導致一些意想不到的結果。