zoukankan      html  css  js  c++  java
  • 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 . 《》:<>


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

  • 相关阅读:
    1462. 课程安排 IV
    最全的CSS浏览器兼容问题【FF与IE】
    this&super两个关键字的意义和用法
    Javascript this 的一些总结
    JS-封装类或对象的最佳方案
    背景色透明,里面内容(图片、文字)不透明
    css3 前端开发
    html5游戏之Box2d物理引擎集成
    Box2d引擎之元素
    西天取经第一步——制作自己的HTML5游戏
  • 原文地址:https://www.cnblogs.com/binarylei/p/10332100.html
Copyright © 2011-2022 走看看