Spring IOC(七)类型推断

Spring IOC(七)类型推断

Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)

Spring 容器中可以根据 beanName 查找其类型,其推断方式和 bean 的创建方式密切相关,并且 Spring 中有一个原则是尽可能的不要通过创建 bean 来获取其类型,除了 FactoryBean 只有通过创建对象调用其 getObjectType 方法,但也只是部分的创建该 FactoryBean(所谓的部分创建是指只实例化对象,而不进行属性注入和初始化过程):

  1. 对于缓存中存在单例的 bean,则直接根据对象获取其类型。
  2. 对于 FactoryBean 创建的 bean,Spring 会部分实例化,调用 FactoryBean#getObjectType() 方法。
  3. 对于工厂方法,Spring 不会实例化这个对象,而是用该方法的返回值类型来进行推断。如果该方法定义了泛型且返回值的类型恰好和泛型有关,则要通过传递的参数来进一步推断,但在 @Spring 5.1.3 似乎有 BUG。
  4. 工厂方法也有可能返回一个 FactoryBean 类型,静态工厂和实例工厂方法处理方法有些不一样。都有两种方式推断类型:一是根据方法的返回值类型进行推断;二是实例化这个 FactoryBean 后调用 getObjectType 方法。对于实例工厂,如果这个工厂还没有实例话就只执行第一种推断方式。

一、Spring 类型推断测试

(1) 环境准备

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 配置

<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) 测试一把

@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 也不能正确处理。

二、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 获取对象类型。

下面我们就分别介绍一下这两个方法。

2.2 从 BeanDefinition 推断类型 - predictBeanType

我们先从简单的看起,AbstractBeanFactory#predictBeanType 实现了这个方法,其子类又重载了这个方法 AbstractAutowireCapableBeanFactory#predictBeanType。

(1) AbstractBeanFactory#predictBeanType

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 . 《》:<>


每天用心记录一点点。内容也许不重要,但习惯很重要!

posted on 2019-01-28 22:36  binarylei  阅读(823)  评论(0编辑  收藏  举报

导航