zoukankan      html  css  js  c++  java
  • Spring IoC 依赖注入(三)resolveDependency

    Spring IoC 依赖注入(三)resolveDependency

    resolveDependency 是 Spring 进行依赖查找的核心 API。弄明白了 resolveDependency,基本上依赖注入的问题也就搞明白了一半。resolveDependency 本质是根据类型查找依赖,调用 beanFactory#beanNamesForType 方法根据类型查找依赖名称。

    1. 根据名称查找依赖:getBean(beanName)。
    2. 根据类型查找依赖:beanFactory#resolveDependency,本文会深入分析依赖查找的源码。

    认知一下,与依赖查找的相关 API:

    1. resolveDependency:支持 Optional、延迟注入、懒加载注入、正常注入。

    2. doResolveDependency:在依赖查找之前,想办法快速查找,如缓存 beanName、@Value 等直接获取注入的值,避免通过类型查找,最后才对集合依赖和单一依赖分别进行了处理。实际上,无论是集合依赖还是单一依赖查找都是调用 findAutowireCandidates 方法。

    3. findAutowireCandidates:真正在 Spring IoC 容器中进行依赖查找,依赖查找的来源有三:①内部对象 ②托管Bean ③BeanDefinition。最后如果无法查找到依赖对象,会进行一些补偿机制,想方设法获取注入的对象,如泛型补偿,自引用补偿。

    4. isAutowireCandidate:判断候选对象是否可用,有三重过滤规则:①bd.autowireCandidate=true -> ②泛型匹配 -> ③@Qualifier。委托给 ContextAnnotationAutowireCandidateResolver。

      isAutowireCandidate 方法过滤候选对象

    首先,我们也先认知一下这几个类:

    1. ParameterNameDiscoverer:用于提取方法参数名称。
    2. DependencyDescriptor:封装了依赖注入点的详细信息,可以是字段 Field,也可以是方法参数 MethodParameter。
    3. AutowireCandidateResolver:判断 DependencyDescriptor 是否是可注入对象。Spring IoC 容器默认实现为 ContextAnnotationAutowireCandidateResolver。

    1. resolveDependency

    resolveDependency 依赖查找解决了以下场景:

    1. Optional:JDK8 提供了 API。主要是将依赖设置非强制依赖,即 descriptor.required=false。
    2. 延迟依赖注入支持:ObjectFactory、ObjectProvider、javax.inject.Provider 没有本质的区别。
    3. 另一种延迟注入的支持 - @Lazy 属性。
    4. 根据类型查找依赖 - doResolveDependency。
    @Override
    public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    
        // ParameterNameDiscovery用于解析方法参数名称
        descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
        // 1. Optional<T>
        if (Optional.class == descriptor.getDependencyType()) {
            return createOptionalDependency(descriptor, requestingBeanName);
        // 2. ObjectFactory<T>、ObjectProvider<T>
        } else if (ObjectFactory.class == descriptor.getDependencyType() ||
                 ObjectProvider.class == descriptor.getDependencyType()) {
            return new DependencyObjectProvider(descriptor, requestingBeanName);
        // 3. javax.inject.Provider<T>
        } else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
            return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
        } else {
            // 4. @Lazy
            Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
                descriptor, requestingBeanName);
            // 5. 正常情况
            if (result == null) {
                result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
            }
            return result;
        }
    }
    

    说明: 前四种场景(Optional,延迟注入 ObjectProvider + @Lazy),我们先放一下,重点分析一下最基本的使用场景,Spring 是如何进行依赖查找的 - doResolveDependency。其实无论是什么场景,最底层都是调用 doResolveDependency。

    2. doResolveDependency

    doResolveDependency 封装了依赖查找的各种情况:

    1. 快速查找: @Autowired 注解处理场景。AutowiredAnnotationBeanPostProcessor 处理 @Autowired 注解时,如果注入的对象只有一个,会将该 bean 对应的名称缓存起来,下次直接通过名称查找会快很多。
    2. 注入指定值:@Value 注解处理场景。QualifierAnnotationAutowireCandidateResolver 处理 @Value 注解时,会读取 @Value 对应的值进行注入。如果是 String 要经过三个过程:①占位符处理 -> ②EL 表达式解析 -> ③类型转换,这也是一般的处理过程,BeanDefinitionValueResolver 处理 String 对象也是这个过程。
    3. 集合依赖查询:直接全部委托给 resolveMultipleBeans 方法。
    4. 单个依赖查询:先调用 findAutowireCandidates 查找所有可用的依赖,如果有多个依赖,则根据规则匹配: @Primary -> @Priority -> ③方法名称或字段名称。
    public Object doResolveDependency(DependencyDescriptor descriptor, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
    
        InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
        try {
            // 1. 快速查找,根据名称查找。AutowiredAnnotationBeanPostProcessor用到
            Object shortcut = descriptor.resolveShortcut(this);
            if (shortcut != null) {
                return shortcut;
            }
    
            // 2. 注入指定值,QualifierAnnotationAutowireCandidateResolver解析@Value会用到
            Class<?> type = descriptor.getDependencyType();
            Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
            if (value != null) {
                if (value instanceof String) {
                    // 2.1 占位符解析
                    String strVal = resolveEmbeddedValue((String) value);
                    BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                                         getMergedBeanDefinition(beanName) : null);
                    // 2.2 Spring EL 表达式
                    value = evaluateBeanDefinitionString(strVal, bd);
                }
                TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
                try {
                    // 2.3 类型转换
                    return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
                } catch (UnsupportedOperationException ex) {
                    return (descriptor.getField() != null ?
                            converter.convertIfNecessary(value, type, descriptor.getField()) :
                            converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
                }
            }
    
            // 3. 集合依赖,如 Array、List、Set、Map。内部查找依赖也是使用findAutowireCandidates
            Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
            if (multipleBeans != null) {
                return multipleBeans;
            }
    
            // 4. 单个依赖查询
            Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
            // 4.1 没有查找到依赖,判断descriptor.require
            if (matchingBeans.isEmpty()) {
                if (isRequired(descriptor)) {
                    raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
                }
                return null;
            }
    
            String autowiredBeanName;
            Object instanceCandidate;
    
            // 4.2 有多个,如何过滤
            if (matchingBeans.size() > 1) {
                // 4.2.1 @Primary -> @Priority -> 方法名称或字段名称匹配 
                autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
                // 4.2.2 根据是否必须,抛出异常。注意这里如果是集合处理,则返回null
                if (autowiredBeanName == null) {
                    if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
                        return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
                    } else {
                        return null;
                    }
                }
                instanceCandidate = matchingBeans.get(autowiredBeanName);
            } else {
                // We have exactly one match.
                Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
                autowiredBeanName = entry.getKey();
                instanceCandidate = entry.getValue();
            }
    
            // 4.3 到了这,说明有且仅有命中一个
            if (autowiredBeanNames != null) {
                autowiredBeanNames.add(autowiredBeanName);
            }
            // 4.4 实际上调用 getBean(autowiredBeanName, type)。但什么情况下会出现这种场景?
            if (instanceCandidate instanceof Class) {
                instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
            }
            Object result = instanceCandidate;
            if (result instanceof NullBean) {
                if (isRequired(descriptor)) {
                    raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
                }
                result = null;
            }
            if (!ClassUtils.isAssignableValue(type, result)) {
                throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
            }
            return result;
        } finally {
            ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
        }
    }
    

    说明: doResolveDependency 方法的四个功能,快速查找和集合处理都委托给了其它方法,注入指定值虽然看起来复杂,但占位符处理、EL 表达式解析、类型转换这三个功能点都有具体的类处理,也不是本文的重点。

    我们重点看一下单个依赖的查询,弄明白了单个依赖的查询,其它集合依赖也差不多。

    1. 查找容器中所有可用依赖:findAutowireCandidates 方法根据类型查找依赖。
    2. 如何有多个依赖怎么处理?其实 Spring 有一套通用的流程,先按 @Primary 查找,再按 @Priority,最后按方法名称或字段名称查找,直到只有一个 bean 为止。相关的匹配规则见 determineAutowireCandidate 方法。
    3. 此时只有一个依赖,从容器获取真实的 bean。descriptor.resolveCandidate 方法根据名称 autowiredBeanName 实例化对象。

    思考:findAutowireCandidates 返回的为什么是对象类型,而不是实例对象?

    Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
    

    matchingBeans 中的 Object 对象可能是对象类型,而不全部是实例对象。因为 findAutowireCandidates 方法是根据类型 type 查找名称 beanNames,如果容器中该 beanName 还没有实例化,findAutowireCandidates 不会画蛇添足直接实例化该 bean,当然如果已经实例化了会直接返回这个 bean。

    3. findAutowireCandidates

    根据上面的分析,resolveDependency 方法对 Optional、延迟注入、懒加载注入等分别进行了处理。之后 doResolveDependency 在正式查找之前看能不能快速查找,如缓存 beanName、@Value 等快速指定需要注入的值,避免通过类型查找,最后才对集合依赖和单一依赖分别进行了处理。实际上,无论是集合依赖还是单一依赖查找,本质上都是调用 findAutowireCandidates 进行类型依赖查找。

    从 findAutowireCandidates 方法,我们可以看到 Spring IoC 依赖注入的来源:

    1. 先查找 Spring IoC 内部依赖 resolvableDependencies。在 AbstractApplicationContext#prepareBeanFactory 方法中默认设置了如下内部依赖:BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext。
    2. 在父子容器进行类型查找:查找类型匹配的 beanNames,beanFactory#beanNamesForType 方法根据类型查找是,先匹配单例实例类型(包括 Spring 托管 Bean),再匹配 BeanDefinition 的类型。从这一步,我们可以看到 Spring 依赖注入的另外两个来源:一是 Spring 托管的外部 Bean,二是 Spring BeanDefinition。
    protected Map<String, Object> findAutowireCandidates(
        @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
        
        Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
        // 1. Spring IoC 内部依赖 resolvableDependencies
        for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
            Class<?> autowiringType = classObjectEntry.getKey();
            if (autowiringType.isAssignableFrom(requiredType)) {
                Object autowiringValue = classObjectEntry.getValue();
                autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
                if (requiredType.isInstance(autowiringValue)) {
                    result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                    break;
                }
            }
        }
        
        // 2. 类型查找:本质上递归调用beanFactory#beanNamesForType。先匹配实例类型,再匹配bd。
        String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
            this, requiredType, true, descriptor.isEager());
        for (String candidate : candidateNames) {
            // 2.1 isSelfReference说明beanName和candidate本质是同一个对象
            //     isAutowireCandidate进一步匹配bd.autowireCandidate、泛型、@@Qualifier等进行过滤
            if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
                // 2.2 添加到候选对象中
                addCandidateEntry(result, candidate, descriptor, requiredType);
            }
        }
        
        // 3. 补偿机制:如果依赖查找无法匹配,怎么办?包含泛型补偿和自身引用补偿两种。
        if (result.isEmpty()) {
            boolean multiple = indicatesMultipleBeans(requiredType);
            // 3.1 fallbackDescriptor: 泛型补偿,实际上是允许注入对象类型的泛型存在无法解析的情况
            DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
            // 3.2 补偿1:不允许自称依赖,但如果是集合依赖,需要过滤非@Qualifier对象。什么场景?
            for (String candidate : candidateNames) {
                if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
                    (!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
                    addCandidateEntry(result, candidate, descriptor, requiredType);
                }
            }
            // 3.3 补偿2:允许自称依赖,但如果是集合依赖,注入的集合依赖中需要过滤自己
            if (result.isEmpty() && !multiple) {
                for (String candidate : candidateNames) {
                    if (isSelfReference(beanName, candidate) &&
                        (!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
                        isAutowireCandidate(candidate, fallbackDescriptor)) {
                        addCandidateEntry(result, candidate, descriptor, requiredType);
                    }
                }
            }
        }
        return result;
    }
    

    说明: findAutowireCandidates 大致可以分为三步:先查找内部依赖,再根据类型查找,最后没有可注入的依赖则进行补偿。

    1. 查找内部依赖:Spring IoC 容器本身相关依赖,这部分内容是用户而言是透明的,也不用感知。resolvableDependencies 集合中注册如 BeanFactory、ApplicationContext 、ResourceLoader、ApplicationEventPublisher 等。
    2. 根据类型查找:包括 ①外部托管 Bean ②注册 BeanDefinition。类型查找调用 beanFactory#beanNamesForType 方法,详见 Spring IoC 依赖查找之类型自省。我们来看一下如何过滤的。
      • 自身引用:isSelfReference 方法判断 beanName 和 candidate 是否是同一个对象,包括两种情况:一是名称完全相同,二是 candidate 对应的工厂对象创建了 beanName。
      • 是否可以注入:底层实际调用 resolver.isAutowireCandidate 方法进行过滤,包含三重规则:①bd.autowireCandidate=true -> ②泛型匹配 -> ③@Qualifier。下面会详细介绍这个方法。
    3. 补偿机制:如果依赖查找无法匹配,怎么办?Spring 提供了两种补偿机制:一是泛型补偿,允许注入对象对象的泛型无法解析,二是自身引用补偿,对这两种机制使用如下:
      • 先使用泛型补偿,不允许自身引用:即 fallbackDescriptor。此时如果是集合依赖,对象必须是 @Qualifier 类型。
      • 允许泛型补偿和自身引用补偿:但如果是集合依赖,必须过滤自己本身,即 beanName.equals(candidate) 必须剔除。

    现在 findAutowireCandidates 处理过程,基本上很清晰了,还有两个小问题需要再澄清一下:

    1. isAutowireCandidate 方法是如何过滤修改对象?
    2. addCandidateEntry 最终最终返回的都是实例对象吗?

    先看一下 addCandidateEntry 方法,如果对象还未实例化,Spring 不会画蛇添足将 candidateName 通过 getName 提前实例化。之所以要强调这点,是因为 Spring 的 Bean 生命周期,其实从 Bean 还未实例化就已经开始,Spring 会尽可能的不要初始化该 Bean,除非显式调用 getBean 或不得不实例化时,这点在阅读源码是会感受非常强烈,我们在使用 Spring API 时也要非常注意这点。

    private void addCandidateEntry(Map<String, Object> candidates, String candidateName,
                                   DependencyDescriptor descriptor, Class<?> requiredType) {
    
        // 1. 集合依赖,直接调用 getName(candidateName) 实例化
        if (descriptor instanceof MultiElementDescriptor) {
            Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
            if (!(beanInstance instanceof NullBean)) {
                candidates.put(candidateName, beanInstance);
            }
        // 2. 已经实例化,直接返回实例对象
        } else if (containsSingleton(candidateName) || (descriptor instanceof StreamDependencyDescriptor && ((StreamDependencyDescriptor) descriptor).isOrdered())) {
            Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
            candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
        // 3. 只获取candidateName的类型,真正需要注入时才实例化对象
        } else {
            candidates.put(candidateName, getType(candidateName));
        }
    }
    

    说明: descriptor.resolveCandidate 基本上都是直接调用 getName(beanName) 实例化 bean。在大部分场景中,addCandidateEntry 方法只会以返回该 candidateName 对应的类型,而不会提前实例该对象。

    4. isAutowireCandidate

    isAutowireCandidate 判断候选对象是否可用。实际是都是委托给 AutowireCandidateResolver#isAutowireCandidate 接口判断,Spring 中默认的实现是 ContextAnnotationAutowireCandidateResolver。

    isAutowireCandidate 方法过滤候选对象有三重规则:①bd.autowireCandidate=true -> ②泛型匹配 -> ③@Qualifier。更多源码分析见 Spring 注解原理 AutowireCandidateResolver:@Qualifier @Value

    protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd,
            DependencyDescriptor descriptor, AutowireCandidateResolver resolver) {
        String beanDefinitionName = BeanFactoryUtils.transformedBeanName(beanName);
        // 1. 传统方式:解析 bd.beanClass,注意 Spring注解驱动时根本不会配置beanClassName
        resolveBeanClass(mbd, beanDefinitionName);
        // 2. 注解驱动:解析工厂方法 bd.factoryMethodToIntrospect
        if (mbd.isFactoryMethodUnique && mbd.factoryMethodToIntrospect == null) {
            new ConstructorResolver(this).resolveFactoryMethodIfPossible(mbd);
        }
        // 3. 直接委托给AutowireCandidateResolver
        return resolver.isAutowireCandidate(
            new BeanDefinitionHolder(mbd, beanName, getAliases(beanDefinitionName)), descriptor);
    }
    

    说明: 主要注意一下传统方式和注解驱动获取 Bean 类型的不同:

    • 传统方式:配置 beanClassName,直接解析成 beanClass,从而获取对象类型。
    • 注解驱动:如 @Bean 方式,需要解析方法返回值类型,获取对象类型。

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

  • 相关阅读:
    BF算法和KMP算法
    Python课程笔记 (五)
    0268. Missing Number (E)
    0009. Palindrome Number (E)
    0008. String to Integer (atoi) (M)
    0213. House Robber II (M)
    0198. House Robber (E)
    0187. Repeated DNA Sequences (M)
    0007. Reverse Integer (E)
    0006. ZigZag Conversion (M)
  • 原文地址:https://www.cnblogs.com/binarylei/p/12337145.html
Copyright © 2011-2022 走看看