Spring IoC 依赖查找之类型自省

Spring IoC 依赖查找之类型自省

Spring 核心编程思想目录:https://www.cnblogs.com/binarylei/p/12290153.html

推荐文章:

Spring 类型自省的原则:尽可能的不要通过创建 bean 来获取其类型,除了 FactoryBean 只有通过创建对象调用其 getObjectType 方法,但也只是部分创建该 FactoryBean(所谓的部分创建是指只实例化对象,而不进行属性注入和初始化过程)。

1. Spring Bean 五种实例方式

Spring 类型自省和 bean 的创建方式密切相关,而 Spring 要兼容如此多的创建方式,也导致了代码看起来就有点复杂。Spring Bean 常见的五种创建方式,每一各创建方式获取其类型的方式都不一样。

  • 1. 无参构造器
  • 2. 有参构造器:无论是有参还是无参构造器,BeanDefinition 都定义了 className 属性,可以直接获取其类型,不需要实例化 Bean.。
  • 3. FactoryBean:一般来说是根据 FactoryBean#getObjectType 获取 Bean 类型。所以必须实例化 FactoryBean 对象,但 Spring 为了减少提前初始化 Bean 的影响,只部分实例化 FactoryBean 对象,而不进行属性注入。但还有一种特殊的情况,FactoryBean 正在初始化时,就不能通过 getObjectType 获取对象类型,此时可以获取 FactoryBean 上的泛型类型,参考 循环依赖+类型推断引发的 Bug。也就是说泛型是 FactoryBean 的一种兜底方案。
  • 4. 静态工厂:通过方法的返回值类型,获取 Bean 类型,不需要实例化 Bean
  • 5. 实例工厂:通过方法的返回值类型,获取 Bean 类型,不需要实例化 Bean。正因为不会实例化该实例工厂,如果工厂方法的返回值类型有泛型,则无法获取 Bean 类型。

我们之所以强调这五种 Bean 配置方式,分别获取 Bean 类型时是否会实例化 Bean,是因为在 Spring 中提前初始化 Bean 会导致很多问题。

2. Spring 类型查找 API

Spring 根据类型查找提供了如下 API:

  • 获取单个 Bean 类型实例

    • getBean(Class) 以及重载方法
  • 获取集合 Bean 类型实例

    • getBeansOfType(Class) 以及重载方法
  • 获取集合 Bean 类型名称

    • getBeanNamesForType(Class)
    • Spring 4.2 getBeanNamesForType(ResolvableType)

2.1 获取单个 Bean 类型实例

getBean(Class) 最终调用 resolveNamedBean 方法,resolveNamedBean 是 Spring 内部根据类型查找 bean 的方法。

private <T> NamedBeanHolder<T> resolveNamedBean(ResolvableType requiredType, 
        Object[] args, boolean nonUniqueAsNull) throws BeansException {
    // 1. 根据类型查找,不会实例化 bean
    String[] candidateNames = getBeanNamesForType(requiredType);

    // 2. 多个类型,如何过滤?
    if (candidateNames.length > 1) {
        List<String> autowireCandidates = new ArrayList<>(candidateNames.length);
        for (String beanName : candidateNames) {
            // 如果容器中定义的beanDefinition.autowireCandidate=false(默认为true)则剔除
            // ①没有定义该beanDefinition或②beanDefinition.autowireCandidate=true时合法
            // 什么场景下会出现:没有定义该BeanDefinition,但根据类型可以查找到该beanName?
            if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
                autowireCandidates.add(beanName);
            }
        }
        if (!autowireCandidates.isEmpty()) {
            candidateNames = StringUtils.toStringArray(autowireCandidates);
        }
    }

    // 3. 单个candidateNames,则调用getBean(beanName)实例化该bean
    if (candidateNames.length == 1) {
        String beanName = candidateNames[0];
        return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
    // 4. 多个candidateNames,先尝试是否标注Primary属性,再尝试类上@Priority注解
    } else if (candidateNames.length > 1) {
        Map<String, Object> candidates = new LinkedHashMap<>(candidateNames.length);
        for (String beanName : candidateNames) {
            if (containsSingleton(beanName) && args == null) {
                Object beanInstance = getBean(beanName);
                candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance));
            } else {
                candidates.put(beanName, getType(beanName));
            }
        }
        // 查找 primary Bean,即 beanDefinition.primary=true
        String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass());
        // 比较 Bean 的优先级。@javax.annotation.Priority
        if (candidateName == null) {
            candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
        }
        // 4. 过滤后只有一个符合条件,getBean(candidateName)实例化
        if (candidateName != null) {
            Object beanInstance = candidates.get(candidateName);
            if (beanInstance == null || beanInstance instanceof Class) {
                beanInstance = getBean(candidateName, requiredType.toClass(), args);
            }
            return new NamedBeanHolder<>(candidateName, (T) beanInstance);
        }
        // 5. 多个bean,抛出NoUniqueBeanDefinitionException异常
        if (!nonUniqueAsNull) {
            throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
        }
    }

    return null;
}

说明: 代码很长,但逻辑却很简单。

  1. 首先,根据 Bean 类型查找 Spring IoC 容器中所有符合条件的 Bean 名称,可能有多个。要注意的是,getBeanNamesForType 只会读取 BeanDefinition 信息或部分实例化 FactoryBean 来获取 Bean 的类型,但还没有实例化 Bean。

  2. 如果容器中只注册了一个这种类型的 Bean,没什么可说的,直接实例化该 Bean 后返回即可。

  3. 问题是,Spring IoC 容器中可能注册有多个 Bean,此时该怎么办呢?Spring 做了好几重过滤:

    • 第一重过滤:没有定义该 BeanDefinition 或该 beanDefinition.autowireCandidate=true。默认情况下 autowireCandidate=true,也就是一般情况下都会通过。

      但令人疑惑的是,什么场景下会出现:没有定义该 BeanDefinition,但根据类型查找 getBeanNamesForType 可以查找到该 beanName 呢?多线程情况下?

    • 第二重过滤:查找 primary Bean,即 beanDefinition.primary=true。如果有多个,则抛出 NoUniqueBeanDefinitionException。

    • 第三重过滤:比较 Bean 的优先级。Spring 默认的比较器是 AnnotationAwareOrderComparator,比较 Bean 上 @javax.annotation.Priority 的优先级,值越小优先级越高。同样的,如果最高级别的多个,则抛出 NoUniqueBeanDefinitionException。

      话说,好像不支持 Spring 原生的 @Order 注解,难道我看错了?

    • 最后,还有多个,则抛出 NoUniqueBeanDefinitionException。

  4. 根据类型查找,最复杂的两步:

    • 根据类型查找所有符合条件的 beanNames - getBeanNamesForType。
    • 实例化 candidateName - getBean(candidateName)。Bean 的实例化不是本章重点。

2.2 获取集合 Bean 类型实例

和获取单个 Bean 类型实例的过程如出一辙,只不过如果有多个 candidateNames 时不用过滤,全部返回即可。

@Override
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException {
    return getBeansOfType(type, true, true);
}
@Override
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, 
       boolean includeNonSingletons, boolean allowEagerInit) throws BeansException {
    String[] beanNames = getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
    Map<String, T> result = new LinkedHashMap<>(beanNames.length);
    for (String beanName : beanNames) {
        Object beanInstance = getBean(beanName);
        if (!(beanInstance instanceof NullBean)) {
            result.put(beanName, (T) beanInstance);
        }
    }
    return result;
}

说明: getBeansOfType 同样调用 getBeanNamesForType 获取所有类型匹配的 beanNames,然后调用 getBean(beanName) 实例化所有的 Bean。

2.3 获取集合 Bean 类型名称

getBeanNamesForType 方法的功能,Spring 内部根据类型匹配所有的 beanNames。getBeanNamesForType 不会初始化 Bean,根据其 BeanDefinition 或 FactoryBean#getObjectType 获取其类型,具体获取 Bean 类型的方法参考本文第一部分。

getBeanNamesForType 有三个重载的方法:

  • Spring 4.2 getBeanNamesForType(ResolvableType):用于处理泛型
  • getBeanNamesForType(Class)
  • getBeanNamesForType( Class<?> type, boolean includeNonSingletons, boolean allowEagerInit):type 表示要查找的类型,includeNonSingletons 表示是否包含非单例 Bean,allowEagerInit 表示是否提前部分初始化 FactoryBean。
@Override
public String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
    // 1. 查询的结果不使用缓存,是因为此时查询的结果可能不正确吗?
    // 1.1 configurationFrozen表示是否冻结BeanDefinition,不允许修改,因此查询的结果可能有误
    //     一旦调用refresh方法,则configurationFrozen=true,也就是容器启动过程中会走if语句
    // 1.2 type=null,查找所有Bean?
    // 1.3 !allowEagerInit 表示不允许提前初始化FactoryBean,因此可能获取不到Bean的类型
    if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
        return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
    }
    // 2. 允许使用缓存,此时容器已经启动完成,bean已经加载,BeanDefinition不允许修改
    Map<Class<?>, String[]> cache =
        (includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
    String[] resolvedBeanNames = cache.get(type);
    if (resolvedBeanNames != null) {
        return resolvedBeanNames;
    }
    resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
    if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
        cache.put(type, resolvedBeanNames);
    }
    return resolvedBeanNames;
}

说明: 三种类型推断底层都调用 doGetBeanNamesForType 方法。

  1. 参数 type 表示要查找的类型,includeNonSingletons 表示是否包含非单例 Bean,allowEagerInit 表示是否提前部分初始化 FactoryBean(为什么说是部分初始化?是因为只实例化 FactoryBean,而不进行属性注入)。

  2. 是否将查询的结果缓存起来,个人认为原因是 getBeanNamesForType 方法根据类型查找匹配的 beanNames 结果并不十分准确。原因很简单,因为该方法不会通过提前实例化 Bean 的方式获取其类型,只会根据 BeanDefinition 或 FactoryBean#getObjectType 获取其类型。如果不实例化对象,有些场景可能并不能获取对象的类型。

    不使用缓存的场景如下:

    • configurationFrozen=false,表示 Spring 容器在初始化阶段,可以对 BeanDefinition 进行调整,当然缓存结果毫无意义。在 refresh 后会设置为 true,此时可以对结果进行缓存。
    • type=null ?
    • allowEagerInit=false,即不允许通过实例化 FactoryBean 来获取 Bean 类型,那就只能通过 FactoryBean 上的泛型来获取其类型了,也可能导致查询的结果不准确。

3. Spring 类型自省源码分析

  1. getBeanNamesForType:查找容器中所以类型匹配的 beanNames,包括托管 Bean。该方法不会实例 Bean,通过 BeanDefinition 或 FactoryBean 获取其类型。
  2. predictBeanType:从 BeanDefinition 获取原始类型,不处理 FacoryBean。
  3. getType:查询 beanName 的类型,包括托管 Bean。先从缓存的单例中获取对象类型,再通过 predictBeanType 获取 Bean 类型后,处理 FacoryBean。同时也会处理父容器的情况。
  4. isTypeMatch:处理流程基本同 getType 方法。
  5. getTypeForFactoryMethod:提取工厂方法的返回值类型,包括泛型处理,静态工厂和实例工厂等。
  6. getTypeForFactoryBean:从 FactoryBean 中提取对象类型。
  7. getTypeForFactoryBeanFromMethod:提取工厂方法返回值 FactoryBean 上的泛型类型。这个方法功能类似于 getTypeForFactoryMethod,只不过 getTypeForFactoryBeanFromMethod 创建的对象只可能为 FactoryBean。同样也会出现匹配多个工厂方法的情况,提取公共类型即可。

之前已经分析了,Spring 三种类型查找 API 底层都是先调用 getBeanNamesForType 匹配符合类型的 beanNames,下面我们就从 doGetBeanNamesForType 开始一探究竟。

3.1 getBeanNamesForType

Spring 类型匹配规则有以下几个:

  • 注册的 BeanDefinition:通过 registerBeanDefinition 方法将 BeanDefinition 注册到容器中,大多数情况
    • isTypeMatch(beanName, type):匹配 Bean,如果是 FactoryBean 会部分初始化来获取其类型。
    • isTypeMatch(&beanName, type):匹配 FactoryBean。
  • 托管的 Bean:通过 registerSingleton 方法直接注册到 manualSingletonNames 集合中,极少数
    • isTypeMatch(beanName, type)
    • isTypeMatch(&beanName, type)
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
    List<String> result = new ArrayList<>();

    // 1. Check all bean definitions.
    for (String beanName : this.beanDefinitionNames) {
        if (!isAlias(beanName)) {
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // 1.1 校验 BeanDefinition
            if (!mbd.isAbstract() && (allowEagerInit ||
                (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
                !requiresEagerInitForType(mbd.getFactoryBeanName()))) {
                boolean isFactoryBean = isFactoryBean(beanName, mbd);
                BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
                // 1.2 匹配beanName
                boolean matchFound =
                    (allowEagerInit || !isFactoryBean ||
                     (dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
                    (includeNonSingletons ||
                     (dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
                    isTypeMatch(beanName, type);
                // 1.3 没有匹配成功,继续匹配&beanName
                if (!matchFound && isFactoryBean) {
                    beanName = FACTORY_BEAN_PREFIX + beanName;
                    matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
                }
                if (matchFound) {
                    result.add(beanName);
                }
            }
        }
    }

    // 2. Check manually registered singletons too.
    for (String beanName : this.manualSingletonNames) {
        if (isFactoryBean(beanName)) {
            // 2.1 匹配beanName
            if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) {
                result.add(beanName);
                continue;
            }
            // 2.2 没有匹配成功,继续匹配&beanName
            beanName = FACTORY_BEAN_PREFIX + beanName;
        }
        if (isTypeMatch(beanName, type)) {
            result.add(beanName);
        }
    }
    
    return StringUtils.toStringArray(result);
}

说明: doGetBeanNamesForType 方法整体逻辑十分清晰,先查找 Spring 中注册的 BeanDefinition,再查找 Spring 托管的 Bean(即通过 registerSingleton 方法注册)。每种情况都先匹配 Bean,再匹配 FactoryBean。

doGetBeanNamesForType 方法难点有以下几个,下面会一一进行讲解:

  1. 如何校验 BeanDefinition?
  2. 如何匹配 beanName?
  3. 如何判断 beanName 是否是 FactoryBean?
  4. isTypeMatch 方法是如何里获取类型的?

(1)如何校验 BeanDefinition

首先,Spring 如何判断能否从 BeanDefinition 获取其类型?

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

下面我们对上述的表达式进行分析。通常情况下该表达式都会返回 true,一般只有 bean 的工厂为 FactoryBean 且未初始化时,返回 false。

  • 条件1:不能是抽象类,这点显而易见。

  • 条件2:allowEagerInit=true 表示可以提前初始化 FactoryBean。因为 FactoryBean 只有初始化才能通过 getObjectType 获取其类型。当然还有一种兜底的方案是 FactoryBean 上的泛型。

  • 条件3:判断 JVM 是否允许提前加载 mdb.beanClass,因为获取 Bean 类型时会将该类加载到 JVM 中。默认情况下永远为 true。

    • mdb.beanClass 已经解析完成,这毫无疑问可以获取 bean 类型。
    • mbd.lazyInit=false 非懒加载 bean 时解析 Bean 类型,因为此时需要在 JVM 上加载 mdb.className 为 mdb.beanClass。默认为 false,也就是可以加载 Bean 类型。
    • allowEagerClassLoading=true 表示强制加载 mdb.className,即使 mbd.lazyInit=true。Spring 容器 allowEagerClassLoading 默认为 true,而且目前为至,没有发现 Spring 的上下文将其修改为 false 的。也就是这一项条件判断永远为 true。
  • 条件4:requiresEagerInitForType 方法通常情况下返回 false,只有当工厂类为 FactoryBean 时且未初始化时才返回 true 时。如果为 true 则表示 FactoryBean 需要提前初始化,即必须允许提前初始化,也就是说 allowEagerInit=true。

    // ① mbd 存在 factoryBeanName 工厂类
    // ② factoryBeanName 为 FactoryBean 
    // ③ FactoryBean 未初始化。
    private boolean requiresEagerInitForType(@Nullable String factoryBeanName) {
        return (factoryBeanName != null && isFactoryBean(factoryBeanName) && !containsSingleton(factoryBeanName));
    }
    

(2)如何匹配 beanName

Spring 调用 isTypeMatch 判断 beanName 的类型,但在正式调用 isTypeMatch 还做了很多判断。

boolean matchFound =
    (allowEagerInit || !isFactoryBean ||
     (dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
    (includeNonSingletons ||
     (dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
    isTypeMatch(beanName, type);

首先要对 isTypeMatch 方法有一定的了解,isTypeMatch 获取 Bean 类型时,如果发现是 FactoryBean,会提前部分初始化 FactoryBean,所以判断前需要判断能不能调用该方法。

  • 条件1:判断能不能提前初始化 FactoryBean。
    • allowEagerInit=true 允许提前部分初始化,直接过。不管 FactoryBean 是否已经初始化,也不管是否懒加载,是否允许提前初始化,都可以初始化 FactoryBean。
    • isFactoryBean=false 不是 FactoryBean 也直接过。
    • 此时一定是 FactoryBean,则需要进行一步判断:要么是非懒加载,要么已经初始化完成。
  • 条件2:是否查找非单例 Bean,这个很简单就不多说了。
  • 条件3:isTypeMatch 是 Spring 中真正获取 Bean 类型的方法。

(3)如何判断 beanName 是否是 FactoryBean

protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {
    Class<?> beanType = predictBeanType(beanName, mbd, FactoryBean.class);
    return (beanType != null && FactoryBean.class.isAssignableFrom(beanType));
}

说明: predictBeanType 方法是 Spring 中从 BeanDefinition 中获取 Bean 类型的方法。下面会重点讲解一下这个方法。

3.2 predictBeanType

predictBeanType 方法功能是 Spring 中从 BeanDefinition 中提取 Bean 类型的底层 API。predictBeanType 有两个重写方法:

  • AbstractBeanFactory 类中:调用 resolveBeanClass 方法

    • resolveBeanClass:直接加载 mbd.beanClassName,并缓存为 mbd.beanClass。
  • AbstractAutowireCapableBeanFactory 类中:在 AbstractBeanFactory 类的基础进行了扩展

    • 处理工厂方法 factoryMethodName 属性:包括静态工厂和实例工厂方法,通过方法的返回值类型获取 Bean 类型。

    • resolveBeanClass:直接加载 mbd.beanClassName。

    • SmartInstantiationAwareBeanPostProcessor#predictBeanType:后置处理器,用于扩展。

@Override
protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
    Class<?> targetType = determineTargetType(beanName, mbd, typesToMatch);
    // ... SmartInstantiationAwareBeanPostProcessor扩展
    return targetType;
}
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;
}

说明: resolveBeanClass 很简单。getTypeForFactoryMethod 提取工厂方法的返回值类型,包括泛型处理。代码看上去非常复杂,但无论怎么复杂都是首先获取工厂类 factoryClass,再匹配工厂类中的工厂方法 factoryMethodName,获取工厂方法 Method,最后解析工厂方法的返回值类型。在后面会专门分析其源码。

3.3 getType

上文中已经分析了 predictBeanType 方法,predictBeanType 已经可以从 BeanDefinition 中提取 Bean 类型。但返回的该类型可能不是我们想要的,比如说返回的是 FactoryBean,我们需要的却是 Bean 的类型,还比如说从父容器中获取 Bean 类型,总之 getType 帮助我们解决了这些问题。

getType 在 predictBeanType 的基础上进行了扩展:

  1. 如果该 Bean 已经实例化,则直接通过实例对象获取对象类型。这样不仅可以对通过 BeanDefinition 创建的单例 Bean 进行类型查询,也可以对托管的 Bean 进行类型查询。对于 FactoryBean 需要特殊处理一下。
  2. 如果本地容器不包含 beanName 的定义,则尝试从父容器中获取对象类型。
  3. 调用 predictBeanType 获取对象类型。
  4. 如果返回的 FactoryBean 同样也需要特殊处理一下。
@Override
public Class<?> getType(String name) throws NoSuchBeanDefinitionException {
    String beanName = transformedBeanName(name);

    // 1. 直接根据实例对象提取 Bean 类型
    Object beanInstance = getSingleton(beanName, false);
    if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
        if (beanInstance instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
            return getTypeForFactoryBean((FactoryBean<?>) beanInstance);
        } else {
            return beanInstance.getClass();
        }
    }

    // 2. 子容器没有定义该beanName,从容器中提取
    BeanFactory parentBeanFactory = getParentBeanFactory();
    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
        return parentBeanFactory.getType(originalBeanName(name));
    }

    // 3. 现在开始从BeanDefinition中提取
    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

    // 4. 暂时未知
    BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
    if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
        RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
        Class<?> targetClass = predictBeanType(dbd.getBeanName(), tbd);
        if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
            return targetClass;
        }
    }

    // 5. predictBeanType方法从BeanDefinition中提取beanClass
    Class<?> beanClass = predictBeanType(beanName, mbd);

    // 6. 对结果进行处理,FacotoryBean
    if (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass)) {
    	// 6.1 实际查询的就是 Bean 类型,但实例类型是FactoryBean
        if (!BeanFactoryUtils.isFactoryDereference(name)) {
            return getTypeForFactoryBean(beanName, mbd);
    	// 6.2 实际查询的就是 FactoryBean 类型
        } else {
            return beanClass;
        }
    } else {
    	// 6.3 实际查询的就是 Bean 类型
        return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass : null);
    }
}

说明: 这一段代码在 predictBeanType 基础上,主要增强了三点功能:一是从实例中提取类型,二是从父类中提取类型,三是对结果类型 FactoryBean 进行处理。

思考:实例的自省类型和 BeanDefinition 的自省类型总是一致的吗?

答案是否定的,Spring 中大量使用的 CGLIB 或 JDK 动态代理,实例类很可能是代理类。看下面的这个小案例:

@EnableAsync
public class GetTypeTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(GetTypeTest.class);
        Assert.assertEquals(BeanA.class, context.getType("beanA"));
    }

    @Bean
    @Lazy
    public BeanA beanA() {
        return new BeanA();
    }

    private class BeanA {
        @Async
        public String hello() {
            return "hello, binarylei";
        }
    }
}

结果说明:如果在 beanA 上添加 @Lazy 注解,则正常;如果删除 beanA 上的注解,则抛出异常。也就是前后两次调用 context.getType("beanA") 查询是类型居然会不一致。为什么呢?

说明: 其实原因很简单,标注 @Lazy 注解是从 BeanDefinition 进行自省类型,而不标注 @Lazy 注解是从实例上进行自省类型,两者类型自省的方式不同,导致结果不同。这种情况不仅仅是 getType 方法独有,其实在所有类似的处理(实例 -> BeanDefinition) 的方法都会有这类问题,如下面将要介绍的 isTypeMatch 方法。

补充说明: @Async 注解标注的类会 AsyncAnnotationBeanPostProcessor 处理后进行代理,也就是容器中的实例其实是 CGLIB 代理后的类。

3.4 isTypeMatch

最后,我们分析一下 isTypeMatch 这个方法,逻辑基本上和 getType 个致。

  1. 先直接通过实例对象获取对象类型,这样不仅可以对通过 BeanDefinition 创建的单例 Bean 进行类型查询,也可以对托管的 Bean 进行类型查询。同样对 FactoryBean 需要特殊处理一下。
  2. 如果本地容器不包含 beanName 的定义,则尝试从父容器中获取对象类型。
  3. 调用 predictBeanType 获取对象类型。
  4. 如果返回的 FactoryBean 同样也需要特殊处理,还有泛型和非泛型的匹配。
@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);
    }

    // 下面就要从 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});

    // 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. predictBeanType 推断 beanName 的类型,主要的逻辑
    Class<?> beanType = predictBeanType(beanName, mbd, typesToMatch);
    if (beanType == null) {
        return false;
    }

    // 4. 结果处理:FactoryBean
    // 4.1 查询Bean类型,需要调用getTypeForFactoryBean方法从FactoryBean中获取Bean类型
    if (FactoryBean.class.isAssignableFrom(beanType)) {
        if (!BeanFactoryUtils.isFactoryDereference(name) && beanInstance == null) {
            // 此时需要从 FactoryBean 中推断出真实类型
            beanType = getTypeForFactoryBean(beanName, mbd);
            if (beanType == null) {
                return false;
            }
        }
    // 4.2 查询FactoryBean类型
    } else if (BeanFactoryUtils.isFactoryDereference(name)) {
        beanType = predictBeanType(beanName, mbd, FactoryBean.class);
        if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType)) {
            return false;
        }
    }

    // 4.3 普通类型处理,两种情况分别处理:有泛型和无泛型
    // mbd.resolvedTargetType 在方法determineTargetType中解析后缓存
    // mbd.targetType 什么时候解析的?好像基本都为空
    ResolvableType resolvableType = mbd.targetType;
    // mbd.factoryMethodReturnType 在方法getTypeForFactoryMethod解析后缓存的
    if (resolvableType == null) {
        resolvableType = mbd.factoryMethodReturnType;
    }
    // 4.3.2 泛型匹配,使用resolvableType匹配
    if (resolvableType != null && resolvableType.resolve() == beanType) {
        return typeToMatch.isAssignableFrom(resolvableType);
    }
    // 4.3.3 无泛型匹配,直接使用beanType匹配
    return typeToMatch.isAssignableFrom(beanType);
}

说明: isTypeMatch 和 getType 的逻辑基本上完全一致,可以将这两个方法对比起来看。

  1. Spring 先直接从缓存中获取这个 bean 实例,再根据对象推断其类型。如果 bean 还没有创建或者干脆就不是单例,则只能根据定义这个 bean 的 BeanDefinition 获取了。
  2. 先根据 BeanDefinition 获取其当前 bean 的类型,Spring 全部委托给了 predictBeanType(beanName, mbd, typesToMatch) 方法。
  3. 如果当前 bean 是 FactoryBean,则还需要进一步获取其真实类型。这个过程由 getTypeForFactoryBean 方法完成,这个方法有两个重载的方法,即可以直接通过实例对象获取,也可以通过定义的 BeanDefinition 获取对象类型。

最后关于 Spring 内部几个 getType* 功能总结如下:

  • getTypeForFactoryMethod:提取工厂方法的返回值类型,包括泛型处理,静态工厂和实例工厂等。在 predictBeanType 中已经进行过源码分析。
  • getTypeForFactoryBean:从 FactoryBean 中提取对象类型。
  • getTypeForFactoryBeanFromMethod:提取工厂方法返回值 FactoryBean 上的泛型类型。这个方法功能类似于 getTypeForFactoryMethod,只不过 getTypeForFactoryBeanFromMethod 创建的对象只可能为 FactoryBean。同样也会出现匹配多个工厂方法的情况,提取公共类型即可。

3.5 getTypeForFactoryMethod

protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
    // 1. 查找缓存
    ResolvableType cachedReturnType = mbd.factoryMethodReturnType;
    
    // 2. 查找工厂类 factoryClass
    Class<?> factoryClass;
    String factoryBeanName = mbd.getFactoryBeanName();
    if (factoryBeanName != null) {
        factoryClass = getType(factoryBeanName);
        isStatic = false;
    } else {
        factoryClass = resolveBeanClass(mbd, beanName, typesToMatch);
    }
    
    // 3. 匹配 Method,包括父类方法
    Class<?> commonType = null;
    Method uniqueCandidate = null;
    Method[] candidates = this.factoryMethodCandidateCache.computeIfAbsent(
        factoryClass, ReflectionUtils::getUniqueDeclaredMethods);
    for (Method candidate : candidates) {
        // 匹配成功 static、factoryMethodName、parameterCount 
        if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate) &&
					candidate.getParameterCount() >= minNrOfArgs) {
            // 根据方法返回值类型,注意泛型解析
            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. 返回Method工厂方法返回值类型
    cachedReturnType = (uniqueCandidate != null ?
				ResolvableType.forMethodReturnType(uniqueCandidate) : ResolvableType.forClass(commonType));
    mbd.factoryMethodReturnType = cachedReturnType;
    return cachedReturnType.resolve();
}

说明: 我们来理一下从工厂方法获取 Bean 类型的思路。

  1. 获取工厂类 factoryClass:分两种情况,静态工厂和实例工厂。

    • 静态工厂:没有设置 factoryBeanName 属性,将当前类的 beanClassName 作为 factoryClass。
    • 实例工厂:设置了 factoryBeanName 属性,通过 factoryBeanName 获取工厂类 factoryClass。
  2. 方法匹配 beanFactoryMethod:包括父类,方法匹配分三步,一是 static 匹配,二是方法名称匹配,三是参数个数匹配(不匹配具体的参数类型)。

  3. 为什么 Spring 只匹配参数个数,只要 "工厂方法实际参数个数" >= "beanDefinition 中参数个数" 即可?

    如果参数个数不够,可以从 Spring 容器中获取对象进行依赖注入。如此匹配,就可能会匹配多个候选工厂。

  4. 如果匹配后有多个候选的工厂方法怎么办?

    Spring 的解决方案是,如果只有一个候选工厂方法,直接返回该方法的返回值类型。如果有多个候选工厂方法,返回所有候选工厂方法的返回值类型的公共类型。如果没有公共类型,则返回 null。

    但大家想一下,类型查找可以取公共类型作为 Bean 的类型,实例化时到底取那一个候选工厂呢?个人猜测可能是取方法参数最多的那个工厂方法?

3.6 getTypeForFactoryBean

getTypeForFactoryBean 方法功能:从 FactoryBean 中提取对象类型,包括 getObjectType 和泛型提取。

  • FactoryBeanRegistrySupport:直接调用 factoryBean#getObjectType() 提取对象类型。
  • AbstractBeanFactory:先实例化 FactoryBean,再调用 FactoryBeanRegistrySupport 的重载方法获取对象类型。
  • AbstractAutowireCapableBeanFactory:在 AbstractBeanFactory 的基础上,主要是增加了对 FactoryBean 上泛型的处理。

前两个重载的 getTypeForFactoryBean 方法都比较简单,我们主要看一下 AbstractAutowireCapableBeanFactory 的这个重载的方法。

@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();

    // 2. 实例工厂创建FactoryBean<User> `public FactoryBean<User> createUser() { ... }`
    //    此时我们不想调用实例工厂来创建这个FactoryBean,因为这需要实例化factoryBeanName对象
    //    如果factoryBeanName对象已经实例了,当然我们也可以部分实例化FactoryBean
    //    如果没有实例化,大家都后退一步,何不直接解析FactoryBean上的泛型呢?当然也可能没有定义泛型,则返回null
    if (factoryBeanName != null) {
        if (factoryMethodName != null) {
            BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
            if (fbDef instanceof AbstractBeanDefinition) {
                AbstractBeanDefinition afbDef = (AbstractBeanDefinition) fbDef;
                if (afbDef.hasBeanClass()) {
                    // 2.1 直接解析factoryMethodName方法返回值上 FactoryBean 的泛型
                    Class<?> result = getTypeForFactoryBeanFromMethod(afbDef.getBeanClass(), factoryMethodName);
                    if (result != null) {
                        return result;
                    }
                }
            }
        }
        
        // 2.2 当然,如果factoryBeanName已经实例化,那当然就可以直接调用工厂方法创建这个FactoryBean
        //     如果 factoryBeanName 没有创建则直接返回null,因为我们不想嵌套创建bean
        if (!isBeanEligibleForMetadataCaching(factoryBeanName)) {
            return null;
        }
    }

    // 3. 现在我们要先部分实例化这个 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);
        }
    }

    // 4. 出现FactoryBean循环创建,此时FactoryBean正在创建,也不能获取其实例对象
    if (factoryBeanName == null && mbd.hasBeanClass()) {
        // 4.1 解析方法的返回值的泛型类型,FactoryBean<User>
        if (factoryMethodName != null) {
            return getTypeForFactoryBeanFromMethod(mbd.getBeanClass(), factoryMethodName);
        // 4.2 该类就是一个 FactoryBean,则直接解析其泛型就好 FactoryBean<User>
        } else {
            return GenericTypeResolver.resolveTypeArgument(mbd.getBeanClass(), FactoryBean.class);
        }
    }

    return null;
}

说明: getTypeForFactoryBean 是从 FactoryBean 中提取对象类型,但 FactoryBean 的来源比较多,基本上常见的一种创建 Bean 的方式都可以创建 FactoryBean。我们一起整理一下:

  1. mbd.instanceSupplier:这是 Spring 5.0+ 后提供的一种 Bean 的创建方式,我们先忽略...

  2. 实例工厂创建:Spring 首先解析其工厂方法返回值的 FactoryBean 上的泛型,如果有则直接返回。

    如果无法解析到对象类型,那问题就来了,为了获取 FactoryBean,Spring 会先创建 factoryBeanName 吗?答案是否定的,Spring 不会为了获取对象类型而循环创建另外一个对象实例,因为这会提前初始化对象,造成更严重的 Bug。当然,如果这个 factoryBeanName 在 Spring 容器中已经实例化了,那就另说了。

    public FactoryBean<User> createUser() { ... }
    
  3. 现在,我们可以部分实例化 FactoryBean,调用 FactoryBean#getObjectType 获取对象类型。如果还是无法获取对象类型,Spring 会完全实例化 FactoryBean,这部分代码是在 AbstractBeanFactory 中完成的。

  4. 如果不能获取提前初始化的 FactoryBean,此时只能通过解析 FactoryBean 的泛型。也分种情况:一是工厂方法创建的 FactoryBean,二是正常注册的 FactoryBean。如果是工厂方法创建的 FactoryBean,同样也会出现匹配多个方法的可能,也是提取公共类型。

3.7 getTypeForFactoryBeanFromMethod

提取工厂方法返回值 FactoryBean 上的泛型类型。这个方法功能类似于 getTypeForFactoryMethod,只不过 getTypeForFactoryBeanFromMethod 创建的对象只可能为 FactoryBean。同样也会出现匹配多个工厂方法的情况,提取公共类型即可。


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

posted on 2020-02-13 09:17  binarylei  阅读(1863)  评论(1编辑  收藏  举报

导航