spring之getBeanByType探究

前言

spring容器獲取bean的方式主要有兩種,即byNamebyType

byName方式相對簡單,即Object getBean(String name),通過beanName獲取,因為容器中存儲的就是一個beanName->bean實體的映射,如果沒有創(chuàng)建,則通過beanName查找bean定義,通過bean定義去創(chuàng)建即可

而byType,即T getBean(Class<T> requiredType),則復(fù)雜一點,因為不管是bean定義容器還是bean容器存儲形式都是以beanName為key的map,所以它的獲取方式肯定要多一步type->name的轉(zhuǎn)換

源碼

其實雖然容器的key是beanName,但想一下getByType并不難實現(xiàn),只要循環(huán)bean定義看看哪些bean的class是所查找的type,獲取到對應(yīng)的beanName,調(diào)用getByName就解決了

其實源碼也正是如此,跟一下源碼(以下代碼省略了一些支線邏輯)

getBeanByType
public <T> T getBean(Class<T> requiredType) throws BeansException {
    return getBean(requiredType, (Object[]) null);
}
public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
             // 調(diào)用resolveBean
    Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
    return (T) resolved;
}

最終調(diào)用resolveBean方法

resolveBean
private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
    NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
    if (namedBean != null) {
        return namedBean.getBeanInstance();
    }
    // 省略
    return null;
}

調(diào)用resolveNamedBean

resolveNamedBean
private <T> NamedBeanHolder<T> resolveNamedBean(
            ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {
    // 調(diào)用getBeanNamesForType查找候選beanNames
    String[] candidateNames = getBeanNamesForType(requiredType);
    // 只有一個候選者,直接返回這個beanName
    if (candidateNames.length == 1) {
        String beanName = candidateNames[0];
        // 這里的getBean就是getBeanByName
        return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
    }
    else if (candidateNames.length > 1) {
        // 省略,大體意思是多個后選擇從中選一個
    }
    return null;
}

所以重點就來到了getBeanNamesForType,字面意思傳給它一個type能返回所有該type的beanName,獲取到這些beanName如果只有一個就達(dá)成目標(biāo)返回,多個就按自己的規(guī)矩選一個,最終通過getBeanByName實際獲取bean

getBeanNamesForType

getBeanNamesForType最終會走向doGetBeanNamesForType,這是spring源碼的一罐命名方式,不貼代碼了,直接看doGetBeanNamesForType

doGetBeanNamesForType
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
    // 準(zhǔn)備好匹配的集合
    List<String> result = new ArrayList<>();
    // 循環(huán)所有bean定義名集合
    for (String beanName : this.beanDefinitionNames) {
        // 省略一些別名判斷,bean定義完整判斷,異常處理...
        // 通過名字獲取beand定義
        RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
        // 判斷是不是FactoryBean         
        boolean isFactoryBean = isFactoryBean(beanName, mbd);
        BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
        // 是否匹配標(biāo)識
        boolean matchFound = false;
        // 是否允許FactoryBean初始化
        boolean allowFactoryBeanInit = (allowEagerInit || containsSingleton(beanName));
        boolean isNonLazyDecorated = (dbd != null && !mbd.isLazyInit());
        // 如果不是FactoryBean
        if (!isFactoryBean) {
            if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) {
                // 使用isTypeMatch判斷bean是否匹配type
                matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
            }
        }
        // 如果是FactoryBean
        else  {
            // 省略FactoryBean特殊處理
        }
        // 如果匹配,加入到匹配集合中
        if (matchFound) {
            result.add(beanName);
        }
    }
    // 省略查找手動注冊單例邏輯...
    // 返回匹配集合結(jié)果
    return StringUtils.toStringArray(result);
}

先不管FactoryBean的特殊處理,看看普通的bean,最終是通過isTypeMatch方法判斷是否匹配

protected boolean isTypeMatch(String name, ResolvableType typeToMatch, boolean allowFactoryBeanInit)
            throws NoSuchBeanDefinitionException {

    String beanName = transformedBeanName(name);
    boolean isFactoryDereference = BeanFactoryUtils.isFactoryDereference(name);

    // 省略判斷手動注冊bean是否匹配邏輯...

    // 根據(jù)beanName獲取bean定義
    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();

    // 要匹配的class
    Class<?> classToMatch = typeToMatch.resolve();

    // 省略FactoryBean相關(guān)邏輯
    
    // bean的type
    ResolvableType beanType = null;
    
    if (beanType == null) {
        // 獲取bean定義的targetType
        ResolvableType definedType = mbd.targetType;
        beanType = definedType;
    }

    // 判斷beanType是否可以轉(zhuǎn)換為要匹配的type
    if (beanType != null) {
        return typeToMatch.isAssignableFrom(beanType);
    }
}

實際上就是查找bean定義的targetType屬性,看是否是所需type/子類/實現(xiàn),如果是就匹配上了

整個過程梳理下來:
getByType就是循環(huán)bean定義,查找bean定義的targetType屬性等于該type的對應(yīng)beanName,再調(diào)用getByName,就完成了通過類型查找bean

getByType

FactoryBean的特殊性

上面的代碼為了清晰省略了很多分支邏輯,其中FactoryBean的判斷是比較重要的一環(huán),所以還不能忽略

FactoryBean的特殊性在于,他實際是兩個對象,一個工廠對象(FactoryType),一個生產(chǎn)的對象(ObjectType,也是一般情況我們實際需要的對象),不熟悉FactoryBean可以看這里Spring FactoryBean源碼解析

問題的癥結(jié)在于FactoryBean的bean定義的targetType屬性是FactoryType,而我們一般需要注入的是FactoryBean.getObject生產(chǎn)的ObjectType

比如現(xiàn)在有個BookFactoryBean生產(chǎn)的是Book實體bean,當(dāng)我們getByType(Book.class)時,bean定義中并不存在targetType==Book.class的bean定義,只有targetType==BookFactoryBean.class的bean定義,按照上面的邏輯是無法根據(jù)type獲取到Book的beanName的

FactoryBean

所以再回到doGetBeanNamesForType方法找回省略的FactoryBean邏輯,看spring如何解決這個問題

doGetBeanNamesForType
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
    // 準(zhǔn)備好匹配的集合
    List<String> result = new ArrayList<>();
    // 循環(huán)所有bean定義名集合
    for (String beanName : this.beanDefinitionNames) {
        // 省略一些別名判斷,bean定義完整判斷,異常處理...
        // 通過名字獲取beand定義
        RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
        // 判斷是不是FactoryBean         
        boolean isFactoryBean = isFactoryBean(beanName, mbd);
        BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
        // 是否匹配標(biāo)識
        boolean matchFound = false;
        // 是否允許FactoryBean初始化
        boolean allowFactoryBeanInit = (allowEagerInit || containsSingleton(beanName));
        boolean isNonLazyDecorated = (dbd != null && !mbd.isLazyInit());
        // 如果不是FactoryBean
        if (!isFactoryBean) {
            if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) {
                // 使用isTypeMatch判斷bean是否匹配type
                matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
            }
        }
        // 如果是FactoryBean
        else  {
            if (includeNonSingletons || isNonLazyDecorated ||
                    (allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) {
                // 使用isTypeMatch判斷FactoryBean生產(chǎn)的bean是否匹配type
                matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
            }
            // 如果查不到
            if (!matchFound) {
                // 判斷FactoryBean本身是否匹配type
                beanName = FACTORY_BEAN_PREFIX + beanName;
                matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
            }
        }
        // 如果匹配,加入到匹配集合中
        if (matchFound) {
            result.add(beanName);
        }
    }
    // 省略查找手動注冊bean邏輯...
    // 返回匹配集合結(jié)果
    return StringUtils.toStringArray(result);
}

可以看到,如果是FactoryBean,最終依然使用isTypeMatch進(jìn)行判斷,所以重點還是在這個方法

后面還有一句如果不匹配則beanName = FACTORY_BEAN_PREFIX + beanName,并再次isTypeMatch判斷,其中FACTORY_BEAN_PREFIX = "&",熟悉FactoryBean的同學(xué)應(yīng)該知道這是查找FactoryBean本身,因為某些特殊情況,可能我們想注入的就是這個FactoryBean本身而不是他生產(chǎn)的bean

重點依然是看isTypeMatch是怎么處理FactoryBean的匹配問題的

isTypeMatch
/**
** 根據(jù)beanName判斷與typeToMatch是否匹配
*/
protected boolean isTypeMatch(String name, ResolvableType typeToMatch, boolean allowFactoryBeanInit)
            throws NoSuchBeanDefinitionException {

    String beanName = transformedBeanName(name);
    // beanName是不是FactoryBean本身的name,即以&開頭
    boolean isFactoryDereference = BeanFactoryUtils.isFactoryDereference(name);

    // 省略判斷手動注冊bean是否匹配邏輯...

    // 根據(jù)beanName獲取bean定義
    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();

    // 要匹配的class
    Class<?> classToMatch = typeToMatch.resolve();

    // 將要匹配的class和FactoryBean.class組成一個數(shù)組,因為匹配的可能有兩種,一種是targetType就是class,一種是targetType是FactoryBean(保留可能性)
    if (classToMatch == null) {
        classToMatch = FactoryBean.class;
    }
    Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
            new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch});


    // 嘗試預(yù)測bean的Type
    Class<?> predictedType = null;

    // 省略修飾bean定義邏輯

    // predictBeanType(beanName, mbd, typesToMatch)就是獲取bean定義的targetType
    if (predictedType == null) {
        predictedType = predictBeanType(beanName, mbd, typesToMatch);
        if (predictedType == null) {
            return false;
        }
    }

    // 嘗試獲取bean的實際Type
    ResolvableType beanType = null;

    // 如果是FactoryBean, 要查看它創(chuàng)建了什么類型的bean,而不是工廠本身的Type
    if (FactoryBean.class.isAssignableFrom(predictedType)) {
        if (beanInstance == null && !isFactoryDereference) {
            // 獲取FactoryBean創(chuàng)建的type
            beanType = getTypeForFactoryBean(beanName, mbd, allowFactoryBeanInit);
            predictedType = beanType.resolve();
            if (predictedType == null) {
                return false;
            }
        }
    }
    else if (isFactoryDereference) {
        // 如果是FactoryBean本身,還是直接查bean定義的targetType
        predictedType = predictBeanType(beanName, mbd, FactoryBean.class);
        if (predictedType == null || !FactoryBean.class.isAssignableFrom(predictedType)) {
            return false;
        }
    }

    // 如果沒有明確的beanType,但mdb的targetType或factoryMethodReturnType等于predictedType,則可以使用它們作為明確的beanType
    if (beanType == null) {
        ResolvableType definedType = mbd.targetType;
        if (definedType == null) {
            definedType = mbd.factoryMethodReturnType;
        }
        if (definedType != null && definedType.resolve() == predictedType) {
            beanType = definedType;
        }
    }

    // 如果有明確beanType,直接判斷是否可轉(zhuǎn)換為typeToMatch
    if (beanType != null) {
        return typeToMatch.isAssignableFrom(beanType);
    }

    // 如果沒有有明確beanType,判斷predictedType是否可轉(zhuǎn)換為typeToMatch
    return typeToMatch.isAssignableFrom(predictedType);
}

邏輯看起來挺復(fù)雜,其實最重要的是如果是FactoryBean,調(diào)用getTypeForFactoryBean獲取其創(chuàng)建的ObjectType,那么看看getTypeForFactoryBean是如何獲取FactoryBean所創(chuàng)建的ObjectType

getTypeForFactoryBean
protected ResolvableType getTypeForFactoryBean(String beanName, RootBeanDefinition mbd, boolean allowInit) {
    // bean定義指明的ObjectType
    ResolvableType result = getTypeForFactoryBeanFromAttributes(mbd);
    // 取到就返回
    if (result != ResolvableType.NONE) {
        return result;
    }

    // 上面沒取到,則通過doGetBean(FACTORY_BEAN_PREFIX + beanName)獲取或創(chuàng)建FactoryBean工廠本身的對象
    if (allowInit && mbd.isSingleton()) {
        try {
            FactoryBean<?> factoryBean = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true);
            // 通過getTypeForFactoryBean方法獲取ObjectType
            Class<?> objectType = getTypeForFactoryBean(factoryBean);
            return (objectType != null) ? ResolvableType.forClass(objectType) : ResolvableType.NONE;
        }
        catch (BeanCreationException ex) {
            // 省略異常處理
        }
    }
    return ResolvableType.NONE;
}

上面的代碼分兩步,如果bean定義包含了返回的objectType,則直接返回,否則去bean容器查找FactoryBean的實例,通過getTypeForFactoryBean獲取FactoryBean的ObjectType,這里getTypeForFactoryBean實際上就是調(diào)用FactoryBean.getObjectType方法

protected Class<?> getTypeForFactoryBean(FactoryBean<?> factoryBean) {
    try {
        return factoryBean.getObjectType();
    }
    catch (Throwable ex) {
    }
}

getObjectType就是我們實現(xiàn)FactoryBean接口時需要實現(xiàn)的方法,例如如下

@Component
public class BookFactory implements FactoryBean<Book> {
    @Override
    public Book getObject() {
        return new Book();
    }

    @Override
    public Class<?> getObjectType() {
        return Book.class;
    }
}

總結(jié)

getBeanByType 就是循環(huán)bean定義查找targetType==type,如果是FactoryBean查找objectType==type的唯一beanName,再通過getBeanByName就獲取到了bean~

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

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

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