Spring IOC(七)類型推斷

Spring IOC(七)類型推斷

Spring 系列目錄(http://www.javashuo.com/article/p-kqecupyl-bm.html)html

Spring 容器中能夠根據 beanName 查找其類型,其推斷方式和 bean 的建立方式密切相關,而且 Spring 中有一個原則是儘量的不要經過建立 bean 來獲取其類型,除了 FactoryBean 只有經過建立對象調用其 getObjectType 方法,但也只是部分的建立該 FactoryBean(所謂的部分建立是指只實例化對象,而不進行屬性注入和初始化過程):java

  1. 對於緩存中存在單例的 bean,則直接根據對象獲取其類型。
  2. 對於 FactoryBean 建立的 bean,Spring 會部分實例化,調用 FactoryBean#getObjectType() 方法。
  3. 對於工廠方法,Spring 不會實例化這個對象,而是用該方法的返回值類型來進行推斷。若是該方法定義了泛型且返回值的類型剛好和泛型有關,則要經過傳遞的參數來進一步推斷,但在 @Spring 5.1.3 彷佛有 BUG。
  4. 工廠方法也有可能返回一個 FactoryBean 類型,靜態工廠和實例工廠方法處理方法有些不同。都有兩種方式推斷類型:一是根據方法的返回值類型進行推斷;二是實例化這個 FactoryBean 後調用 getObjectType 方法。對於實例工廠,若是這個工廠尚未實例話就只執行第一種推斷方式。

1、Spring 類型推斷測試

(1) 環境準備git

public class User {
}

// 若是 FactoryBean 沒有指定 User 類型則要部分實例化對象
public class UserFactoryBean implements FactoryBean<User> {
    @Override
    public User getObject() throws Exception {
        return new User();
    }

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

public class UserFactory {
    // 1. 實例工廠
    public User getObject1() {
        return new User();
    }

    // 2. 靜態工廠
    public static User getObject2() {
        return new User();
    }

    // 3.1 帶參的工廠方法
    public User getObject3(String username, String password) {
        return new User();
    }

    // 3.2 帶泛型的工廠方法,但返回值類型和泛型無關
    public <T, K, V> User getObject4(T t, String username) {
        return new User();
    }

    // 3.3 帶泛型的工廠方法,但返回值類型由參數決定
    public <T, K, V> T getObject5(T t) {
        return t;
    }

    // 4.1 靜態工廠方法返回類型爲 FactoryBean
    public static FactoryBean<User> getObject6() {
        return new UserFactoryBean();
    }

    // 4.2 實例工廠方法返回類型爲 FactoryBean
    public FactoryBean<User> getObject7() {
        return new UserFactoryBean();
    }
}

(2) xcml 配置github

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--1. 直接定義 className-->
    <bean id="bean1" class="com.github.binarylei.spring.beans.factory.test.BeanFactoryTypeInferenceTest.User"/>

    <!--2. FactoryBean-->
    <bean id="bean2" class="com.github.binarylei.spring.beans.factory.test.BeanFactoryTypeInferenceTest.UserFactoryBean"/>

    <!--3. 實例工廠-->
    <bean id="userFactory" class="com.github.binarylei.spring.beans.factory.test.BeanFactoryTypeInferenceTest.UserFactory"/>
    <bean id="bean3" factory-bean="userFactory" factory-method="getObject1"/>

    <!--4. 靜態工廠-->
    <bean id="bean4" class="com.github.binarylei.spring.beans.factory.test.BeanFactoryTypeInferenceTest.UserFactory" factory-method="getObject2"/>

    <!--5. 帶參的工廠方法,和構造器注入相似-->
    <!--5.1 工廠方法沒有定義泛型-->
    <bean id="bean5.1" factory-bean="userFactory" factory-method="getObject3">
        <constructor-arg index="0" value="0"/>
        <constructor-arg index="1" value="1"/>
    </bean>

    <!--5.2 工廠方法定義泛型,但返回值類型與泛型無關-->
    <bean id="bean5.2" factory-bean="userFactory" factory-method="getObject4">
        <constructor-arg index="0" value="0"/>
        <constructor-arg index="1" value="1"/>
    </bean>

    <!--5.3 工廠方法定義泛型,返回值類型與參數類型決定-->
    <bean id="bean5.3" factory-bean="userFactory" factory-method="getObject5">
        <constructor-arg index="0" value="0"/>
    </bean>

    <!--6 靜態工廠方法的返回值爲一個 FactoryBean 類型-->
    <bean id="bean6.1" class="com.github.binarylei.spring.beans.factory.test.BeanFactoryTypeInferenceTest.UserFactory" factory-method="getObject6"/>

    <!--7 實例工廠方法的返回值爲一個 FactoryBean 類型-->
    <bean id="bean6.2" factory-bean="userFactory" factory-method="getObject7"/>
</beans>

(3) 測試一把spring

@Test
public void test() {
    DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(lbf);
    reader.loadBeanDefinitions(new ClassPathResource("spring-context-factory.xml"));

     // 1. 直接定義 className
    Assert.assertTrue(lbf.isTypeMatch("bean1", User.class));
    // 2. FactoryBean
    Assert.assertTrue(lbf.isTypeMatch("bean2", User.class));
    // 3. 實例工廠
    Assert.assertTrue(lbf.isTypeMatch("bean3", User.class));
    // 4. 靜態工廠
    Assert.assertTrue(lbf.isTypeMatch("bean4", User.class));
    // 5.1 工廠方法沒有定義泛型
    Assert.assertTrue(lbf.isTypeMatch("bean5.1", User.class));
    // 5.2 工廠方法定義泛型,但返回值類型與泛型無關
    Assert.assertTrue(lbf.isTypeMatch("bean5.2", User.class));
    // 5.3 工廠方法定義泛型,返回值類型與參數類型決定,沒法獲取 @Spring 5.1.3
    Assert.assertFalse(lbf.isTypeMatch("bean5.3", User.class));
    // 6.1 靜態工廠方法返回類型爲 FactoryBean
    Assert.assertTrue(lbf.isTypeMatch("bean6.1", User.class));

    // 6.2 實例工廠方法返回類型爲 FactoryBean,若是實例化這個工廠後能夠獲取其類型
    Assert.assertFalse(lbf.isTypeMatch("bean6.2", User.class));
    lbf.getBean("userFactory");
    Assert.assertTrue(lbf.isTypeMatch("bean6.2", User.class));
}

能夠看到工廠方法帶有泛型且返回值類型和泛型有關後 Spring 不能正確處理了,另外實例工廠的工廠方法返回 FactoryBean 也不能正確處理。 express

2、Spring 類型推斷源碼分析

2.1 類型匹配的入口 - isTypeMatch

@Override
public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
    return isTypeMatch(name, ResolvableType.forRawClass(typeToMatch));
}

@Override
public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException {
    String beanName = transformedBeanName(name);

    // 1. 根據實例化後的對象獲取 bean 的類型,注意 FactoryBean 的處理便可
    Object beanInstance = getSingleton(beanName, false);
    if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
        // 1.1 若是是 FactoryBean 類型,此時須要判斷是否要查找的就是這個工廠對象,判斷 beanName 是不是以 & 開頭
        //     若是是其建立的類型,則須要調用 getTypeForFactoryBean 從這個 FactoryBean 實例中獲取真實類型
        if (beanInstance instanceof FactoryBean) {
            if (!BeanFactoryUtils.isFactoryDereference(name)) {
                Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
                return (type != null && typeToMatch.isAssignableFrom(type));
            } else {
                return typeToMatch.isInstance(beanInstance);
            }
        // 1.2 若是是普通 bean 則可直接判斷類型,固然 Spring 還考慮的泛型的狀況
        } else if (!BeanFactoryUtils.isFactoryDereference(name)) {
            if (typeToMatch.isInstance(beanInstance)) {
                return true;
            } else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
                // AOP 有關 ????
                RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                Class<?> targetType = mbd.getTargetType();
                if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance) &&
                        typeToMatch.isAssignableFrom(targetType)) {
                    Class<?> classToMatch = typeToMatch.resolve();
                    return (classToMatch == null || classToMatch.isInstance(beanInstance));
                }
            }
        }
        return false;
    } else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
        // null instance registered
        return false;
    }

    // 2. 父工廠,沒什麼好說的
    BeanFactory parentBeanFactory = getParentBeanFactory();
    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
        return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch);
    }

    // 3. 下面就要從 bean 的定義中獲取該 bean 的類型了
    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

    Class<?> classToMatch = typeToMatch.resolve();
    if (classToMatch == null) {
        classToMatch = FactoryBean.class;
    }
    Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
            new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch});

    // 3.1 AOP 代理時會將原始的 BeanDefinition 存放到 decoratedDefinition 屬性中,能夠行忽略這部分
    BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
    if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
        RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
        Class<?> targetClass = predictBeanType(dbd.getBeanName(), tbd, typesToMatch);
        if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
            return typeToMatch.isAssignableFrom(targetClass);
        }
    }

    // 3.2 predictBeanType 推斷 beanName 的類型,主要的邏輯
    Class<?> beanType = predictBeanType(beanName, mbd, typesToMatch);
    if (beanType == null) {
        return false;
    }

    // 3.3 處理 FactoryBean 類型
    if (FactoryBean.class.isAssignableFrom(beanType)) {
        if (!BeanFactoryUtils.isFactoryDereference(name) && beanInstance == null) {
            // 此時須要從 FactoryBean 中推斷出真實類型
            beanType = getTypeForFactoryBean(beanName, mbd);
            if (beanType == null) {
                return false;
            }
        }
    // 3.4 beanType 不是 FactoryBean 類型,可是又要獲取 FactoryBean 的類型???
    } 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.
        beanType = predictBeanType(beanName, mbd, FactoryBean.class);
        if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType)) {
            return false;
        }
    }

    // 3.5 優先從緩存中判斷,能夠比較泛型
    ResolvableType resolvableType = mbd.targetType;
    if (resolvableType == null) {
        resolvableType = mbd.factoryMethodReturnType;
    }
    if (resolvableType != null && resolvableType.resolve() == beanType) {
        return typeToMatch.isAssignableFrom(resolvableType);
    }
    return typeToMatch.isAssignableFrom(beanType);
}

上面的代碼一大堆,邏輯也比較複雜,咱們如今只須要明白三點:緩存

  1. Spring 先直接從緩存中獲取這個 bean 實例,再根據對象推斷其類型。若是 bean 尚未建立或者乾脆就不是單例,則只能根據定義這個 bean 的 BeanDefinition 獲取了。
  2. 先根據 BeanDefinition 獲取其當前 bean 的類型,Spring 所有委託給了 predictBeanType(beanName, mbd, typesToMatch) 方法。
  3. 若是當前 bean 是 FactoryBean,則還須要進一步獲取其真實類型。這個過程由 getTypeForFactoryBean 方法完成,這個方法有兩個重載的方法,便可以直接經過實例對象獲取,也能夠經過定義的 BeanDefinition 獲取對象類型。

下面咱們就分別介紹一下這兩個方法。app

2.2 從 BeanDefinition 推斷類型 - predictBeanType

咱們先從簡單的看起,AbstractBeanFactory#predictBeanType 實現了這個方法,其子類又重載了這個方法 AbstractAutowireCapableBeanFactory#predictBeanType。less

(1) AbstractBeanFactory#predictBeanTypeide

protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
    // 1. 直接從緩存中獲取
    Class<?> targetType = mbd.getTargetType();
    if (targetType != null) {
        return targetType;
    }
    // 2. 工廠方法一概返回 null,子類會重載後解析對應的 getFactoryMethodName
    if (mbd.getFactoryMethodName() != null) {
        return null;
    }
    // 3. 解析 BeanDefinition 的 className,若是已經加載則直接從緩存中獲取
    return resolveBeanClass(mbd, beanName, typesToMatch);
}

能夠看到最終是從 BeanDefinition 中解析配置的 className 屬性,將其加載到 JVM 中。它有三個參數,毫無疑問的是 mbd 是必須的,beanName 記錄異常時使用,那 typesToMatch 是幹嗎的呢?帶着這個疑問咱們看一下源碼。

protected Class<?> resolveBeanClass(final RootBeanDefinition mbd, String beanName, 
        final Class<?>... typesToMatch) throws CannotLoadBeanClassException {
    if (mbd.hasBeanClass()) {
        return mbd.getBeanClass();
    }
    return doResolveBeanClass(mbd, typesToMatch);       
}
private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)
        throws ClassNotFoundException {

    ClassLoader beanClassLoader = getBeanClassLoader();
    ClassLoader classLoaderToUse = beanClassLoader;
    // 1. Spring 自定義的類型器 DecoratingClassLoader 修改了 JDK 的類加載規則,本身先加載一把,沒有再特派給父加載器
    //    這就產生了一個問題,每一個臨時的類加載器可能加載同一個類可能出現多個
    //    因此能夠將其加入到 excludeClass 仍採用雙親委派
    if (!ObjectUtils.isEmpty(typesToMatch)) {
        ClassLoader tempClassLoader = getTempClassLoader();
        if (tempClassLoader != null) {
            classLoaderToUse = tempClassLoader;
            if (tempClassLoader instanceof DecoratingClassLoader) {
                DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader;
                for (Class<?> typeToMatch : typesToMatch) {
                    dcl.excludeClass(typeToMatch.getName());
                }
            }
        }
    }
    // 2. 若是 className 含有點位符,解析後發生了變化,則不用緩存,之後每次都解析一次
    String className = mbd.getBeanClassName();
    if (className != null) {
        Object evaluated = evaluateBeanDefinitionString(className, mbd);
        if (!className.equals(evaluated)) {
            if (evaluated instanceof Class) {
                return (Class<?>) evaluated;
            } else if (evaluated instanceof String) {
                return ClassUtils.forName((String) evaluated, classLoaderToUse);
            } else {
                throw new IllegalStateException("Invalid class name expression result: " + evaluated);
            }
        }
        if (classLoaderToUse != beanClassLoader) {
            return ClassUtils.forName(className, classLoaderToUse);
        }
    }
    // 3. 解析 className 後緩存起來
    return mbd.resolveBeanClass(beanClassLoader);
}

至此,根據 BeanDefinition 解析 bean 的類型就完成了,最終仍是回到了咱們在 xml 文件中配置的 class 上來了,只是 Spring 的解析考慮了不少狀況,一會兒就複雜起來了。 想必如今對 typesToMatch 也有必定的瞭解了,就是爲了保證要匹配的類型是同一個類加載器加載的,這裏也就是 JDK 的系統類加載器 - AppClassLoader,這樣調用 typeToMatch.isAssignableFrom(type) 方法纔有意義。

(2) AbstractAutowireCapableBeanFactory#predictBeanType

AbstractAutowireCapableBeanFactory 又對 predictBeanType 進行了重載,增長了對工廠方法 factoryMethodName 的解析。

@Override
protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
    // 1. 增長了對工廠 factoryMethod 的解析
    Class<?> targetType = determineTargetType(beanName, mbd, typesToMatch);

    // 2. 後置處理器 SmartInstantiationAwareBeanPostProcessors 
    if (targetType != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                Class<?> predicted = ibp.predictBeanType(targetType, beanName);
                if (predicted != null && (typesToMatch.length != 1 || FactoryBean.class != typesToMatch[0] ||
                        FactoryBean.class.isAssignableFrom(predicted))) {
                    return predicted;
                }
            }
        }
    }
    return targetType;
}

// 怎麼樣,邏輯仍是以前的同樣,只是增長對 factoryMethodName 的處理
protected Class<?> determineTargetType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
    Class<?> targetType = mbd.getTargetType();
    if (targetType == null) {
        targetType = (mbd.getFactoryMethodName() != null ?
                getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
                resolveBeanClass(mbd, beanName, typesToMatch));
        if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) {
            mbd.resolvedTargetType = targetType;
        }
    }
    return targetType;
}

在這裏,咱們重點關注 getTypeForFactoryMethod 方法,Spring 是如何從一個工廠方法中獲取其類型的。

protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
    // 1. 緩存中直接取
    ResolvableType cachedReturnType = mbd.factoryMethodReturnType;
    if (cachedReturnType != null) {
        return cachedReturnType.resolve();
    }

    Class<?> factoryClass;
    boolean isStatic = true;

    // 2. 在 Spring 中有靜態工廠和實例工廠之分,若是是實例工廠名稱不能是當前的 beanName
    String factoryBeanName = mbd.getFactoryBeanName();
    if (factoryBeanName != null) {
        if (factoryBeanName.equals(beanName)) {
            throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
                    "factory-bean reference points back to the same bean definition");
        }
        // 2.1 獲取實例工廠的類型
        factoryClass = getType(factoryBeanName);
        isStatic = false;
    } else {
        // 2.2 靜態工廠只能是靜態方法,解析本身的 BeanDefinition 就能夠了
        factoryClass = resolveBeanClass(mbd, beanName, typesToMatch);
    }

    if (factoryClass == null) {
        return null;
    }
    // 2.3 若是是 CGLIB 代理,獲取其真實類型
    factoryClass = ClassUtils.getUserClass(factoryClass);

    // 3.1 若是有多個方法同名,則取其返回傎的公有類型,除非爲 null
    Class<?> commonType = null;
    // 3.2 緩存這個工廠方法
    Method uniqueCandidate = null;
    // 3.3 若是方法含有泛型,則須要根據傳遞的參數進一步判斷返回值類型
    int minNrOfArgs =
            (mbd.hasConstructorArgumentValues() ? mbd.getConstructorArgumentValues().getArgumentCount() : 0);
    Method[] candidates = this.factoryMethodCandidateCache.computeIfAbsent(
            factoryClass, ReflectionUtils::getUniqueDeclaredMethods);

    // 4. 遍歷全部的名稱爲工廠方法的
    for (Method candidate : candidates) {
        if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate) &&
                candidate.getParameterCount() >= minNrOfArgs) {
            // 4.1 方法中定義了泛型,則須要將配置的構造參數進行比較了,但彷佛有問題
            // 4.1.1 getTypeParameters 獲取定義的泛型個數
            if (candidate.getTypeParameters().length > 0) {
                // 4.1.2 getParameterTypes 獲取該方法的參數列表
                Class<?>[] paramTypes = candidate.getParameterTypes();
                String[] paramNames = null;
                ParameterNameDiscoverer pnd = getParameterNameDiscoverer();
                if (pnd != null) {
                    paramNames = pnd.getParameterNames(candidate);
                }
                ConstructorArgumentValues cav = mbd.getConstructorArgumentValues();
                Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
                Object[] args = new Object[paramTypes.length];
                // 4.1.3 根據類型和參數名匹配真實的參數
                for (int i = 0; i < args.length; i++) {
                    ConstructorArgumentValues.ValueHolder valueHolder = cav.getArgumentValue(
                            i, paramTypes[i], (paramNames != null ? paramNames[i] : null), usedValueHolders);
                    if (valueHolder == null) {
                        valueHolder = cav.getGenericArgumentValue(null, null, usedValueHolders);
                    }
                    if (valueHolder != null) {
                        args[i] = valueHolder.getValue();
                        usedValueHolders.add(valueHolder);
                    }
                }
                // 4.1.4 解析返回值對應的泛型類型
                Class<?> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod(
                        candidate, args, getBeanClassLoader());
                uniqueCandidate = (commonType == null && returnType == candidate.getReturnType() ?
                        candidate : null);
                commonType = ClassUtils.determineCommonAncestor(returnType, commonType);
                if (commonType == null) {
                    return null;
                }
            // 4.2 直接獲取方法的返回值類型,若是有多個同名方法取其公共的祖先便可,沒有就直接返回 null
            } else {
                // 第一次匹配上會緩存這個方法,但再次匹配上了,很差意思有重名的方法,直接清空
                uniqueCandidate = (commonType == null ? candidate : null);
                commonType = ClassUtils.determineCommonAncestor(candidate.getReturnType(), commonType);
                if (commonType == null) {
                    return null;
                }
            }
        }
    }

    // 5.1 若是同名的方法只有一個,那麼將這個工廠方法直接緩存起來
    mbd.factoryMethodToIntrospect = uniqueCandidate;
    if (commonType == null) {
        return null;
    }
    // 5.2 若是隻有惟一的工廠方法,那麼儘量緩存完型的類型,包括泛型。因此再解析一次用於緩存
    cachedReturnType = (uniqueCandidate != null ?
            ResolvableType.forMethodReturnType(uniqueCandidate) : ResolvableType.forClass(commonType));
    mbd.factoryMethodReturnType = cachedReturnType;
    return cachedReturnType.resolve();
}

根據工廠方法推斷 bean 的類型看起來很複雜,但邏輯仍是很清晰的:

  1. 直接從緩存中獲取這個工廠方法的返回值類型。
  2. 解析工廠方法的類型,這裏分爲靜態工廠和實例工廠,靜態工廠即爲本身自己,實例工廠爲配置的 factory-bean
  3. 遍歷這個工廠類的每一個和 factory-method 同名的工廠方法,包括其父類。Spring 增長了對泛型返回值方法的支持,但我在測試的時候失敗了,不知道是否是使用不對。總之,就是遍歷這些方法的返回值類型,若是有多個就取其公共的類型。
  4. 若是隻有惟一的一個工廠方法,緩存起來,同時緩存解析後的類型,儘量保存完整的類型,包括泛型。

對於泛型返回值類型再多說一句,Class<?> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod( candidate, args, getBeanClassLoader()); 在測試時確實沒有問題,後來發現是 Spring 在解析 xml 文件時對構造參數作了一個封裝,也就是 valueHolder.getValue() 不是配置的類型,而是 TypedStringValue 類型,致使解析出現問題。

2.3 根據 FactoryBean 獲取真實類型 - getTypeForFactoryBean

getTypeForFactoryBean 相對來講比較簡單,咱們先說這個方法。AbstractBeanFactory#getTypeForFactoryBean 實現了這個方法,其子類又重載了這個方法 AbstractAutowireCapableBeanFactory#getTypeForFactoryBean

(1) AbstractBeanFactory#getTypeForFactoryBean

protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
    if (!mbd.isSingleton()) {
        return null;
    }
    FactoryBean<?> factoryBean = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true);
    return getTypeForFactoryBean(factoryBean);
}

protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) {
    return factoryBean.getObjectType(); 
}

怎麼樣是否是很簡單,先建立這個 FactoryBean 的實例,而後調用其 getObjectType 方法獲取真實類型。但問題又來了,咱們僅僅須要獲取這個 bean 的類型,卻要實例化與之相關的一系列對象,是否是代價太昂貴了,怎麼規避這個問題呢?在其子類 AbstractAutowireCapableBeanFactory 中對這個方法作了進一步的加強,一塊兒看一下。

(2) AbstractAutowireCapableBeanFactory#getTypeForFactoryBean

@Override
protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
    // 1. 處理 mbd.getInstanceSupplier(),另一種建立 bean 的方式,@Sping 5.0 提供,咱們先忽略...
    // if (mbd.getInstanceSupplier() != null) ...

    // 2. 這個 FactoryBean 也多是其餘的工廠的工廠方法建立的
    String factoryBeanName = mbd.getFactoryBeanName();
    String factoryMethodName = mbd.getFactoryMethodName();

    // 3. 實例工廠建立 FactoryBean<User>
    if (factoryBeanName != null) {
        // 3.1 若是實例工廠的 className 已經解析過了,就直接從其工廠方法的返回值類型進行推斷
        if (factoryMethodName != null) {
            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;
                    }
                }
            }
        }
        // 3.2 判斷這個實例工廠有沒有實例化,若是沒有則直接 Game Over
        //     由於下面咱們要調用 FactoryBean#getObjectType(),不能就是要部分實例化這個 FactoryBean 對象
        //     若是連建立這個 FactoryBean 的工廠都未實例化,那麼更談不上建立 FactoryBean 了
        //     Spring 不會爲了獲取一個 bean 的類型去循環建立 bean
        if (!isBeanEligibleForMetadataCaching(factoryBeanName)) {
            return null;
        }
    }

    // 4. 如今咱們要先部分實例化這個 FactoryBean,調用其 getObjectType() 來獲取對象類型
    FactoryBean<?> fb = (mbd.isSingleton() ?
            getSingletonFactoryBeanForTypeCheck(beanName, mbd) :
            getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));
    if (fb != null) {
        // 4.1 調用 FactoryBean#getObjectType() 獲取類型
        Class<?> result = getTypeForFactoryBean(fb);
        if (result != null) {
            return result;
        // 4.2 若是部分實例化仍是獲取不到,沒辦法了只有將這個 FactoryBean 所有實例化出來了
        } else {
            return super.getTypeForFactoryBean(beanName, mbd);
        }
    }

    // 5. 靜態工廠建立 FactoryBean,這種狀況下 fb=null,咱們能夠解析該方法的返回值類型
    if (factoryBeanName == null && mbd.hasBeanClass()) {
        // 5.1 解析方法的返回值的泛型類型,FactoryBean<User>
        if (factoryMethodName != null) {
            return getTypeForFactoryBeanFromMethod(mbd.getBeanClass(), factoryMethodName);
        // 5.2 該類就是一個 FactoryBean,則直接解析其泛型就好 FactoryBean<User>
        } else {
            return GenericTypeResolver.resolveTypeArgument(mbd.getBeanClass(), FactoryBean.class);
        }
    }
    return null;
}

這個方法思路很清晰,就是在儘量不去實例化對象的狀況下,拿到這個 FactoryBean 的真實建立的類型。重點關注下面三個方法:

  1. getTypeForFactoryBeanFromMethod 工廠方法建立 FactoryBean<...>,直接解析其中的泛型
  2. getSingletonFactoryBeanForTypeCheckgetNonSingletonFactoryBeanForTypeCheck 都先嚐試部分實例化這個 FactoryBean,若是任然沒法獲取,則須要完整的實例化這個對象。
private Class<?> getTypeForFactoryBeanFromMethod(Class<?> beanClass, final String factoryMethodName) {
    class Holder {
        Class<?> value = null;
    }
    final Holder objectType = new Holder();
    Class<?> fbClass = ClassUtils.getUserClass(beanClass);

    // 解析工廠方法的返回值 FactoryBean<User> 類型,若是有多個同名的方法則取公共的類型
    ReflectionUtils.doWithMethods(fbClass, method -> {
        if (method.getName().equals(factoryMethodName) &&
                FactoryBean.class.isAssignableFrom(method.getReturnType())) {
            // 解析 FactoryBean<User> 中的泛型類型
            Class<?> currentType = GenericTypeResolver.resolveReturnTypeArgument(method, FactoryBean.class);
            if (currentType != null) {
                objectType.value = ClassUtils.determineCommonAncestor(currentType, objectType.value);
            }
        }
    });
    return (objectType.value != null && Object.class != objectType.value ? objectType.value : null);
}

getTypeForFactoryBeanFromMethod 其實是在解析方法返回值 FactoryBean 中定義的範型。以下場景所示。

public class UserFactory {
    public static FactoryBean<User> getObject6() {
        return new UserFactoryBean();
    }
}

至於 getSingletonFactoryBeanForTypeCheck 和 getNonSingletonFactoryBeanForTypeCheck 兩個方法也只是實例化了這個 bean。調用了 createBeanInstance 方法。

private FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
        BeanWrapper bw = createBeanInstance(beanName, mbd, null);
        instance = bw.getWrappedInstance();      
        return fb;
    }
}

參考:

1 . 《》:<>


天天用心記錄一點點。內容也許不重要,但習慣很重要!

相關文章
相關標籤/搜索