Skip to content

Latest commit

 

History

History
1003 lines (797 loc) · 48.8 KB

File metadata and controls

1003 lines (797 loc) · 48.8 KB

resolveDependency

一、基本信息

✒️ 作者 - Lex 📝 博客 - CSDN | 掘金 📚 源码地址 - GitHub

二、方法描述

resolveDependency 是 Spring 框架中 ConfigurableListableBeanFactory 接口中定义的一个方法。这个方法的核心功能是解析并提供 bean 的依赖项。它主要在处理 @Autowired@Inject@Value这样的自动装配场景时起作用。

三、方法源码

Spring IOC 容器中的一个核心方法,用于解析 bean 之间的依赖关系。当容器需要知道应该为特定的 bean、字段、构造函数或方法注入哪个其他 bean 时,就会调用这个方法。

/**
 * 在此工厂中定义的 beans 之间,解析指定的依赖项。
 * 
 * 该方法尝试匹配和返回一个适当的 bean 实例来满足给定的依赖描述符。
 * 依赖描述符可以描述字段、方法或构造函数中的依赖。
 * 
 * @param descriptor 描述依赖的对象,提供有关依赖类型、限定符等的详细信息。
 * @param requestingBeanName 声明或请求依赖的 bean 的名称。这通常用于解决 bean 之间的循环依赖。
 * @return 对应的 bean 实例以满足该依赖,如果没有合适的匹配,则返回 null。
 * 
 * @throws NoSuchBeanDefinitionException 当没有找到匹配的 bean 时抛出。
 * @throws NoUniqueBeanDefinitionException 当存在多个可能的匹配并且没有明确的选择时抛出。
 * @throws BeansException 如果由于其他原因解析失败则抛出。
 * 
 * @since 2.5
 * @see #resolveDependency(DependencyDescriptor, String, Set, TypeConverter) 用于更复杂的依赖解析场景,允许传递排除的 beans 和类型转换器。
 */
@Nullable
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException;

四、主要功能

  1. 依赖描述符
    • 该方法接收一个 DependencyDescriptor 参数,它描述了所需的依赖关系。这个描述符可以表示一个字段、方法参数或构造函数参数的依赖。
  2. 自动装配限定符
    • 如果存在多个匹配的 bean,并且所需的依赖有 @Autowired@Qualifier 注解,那么 resolveDependency 会考虑这些注解来确定正确的 bean。
  3. 解决循环依赖
    • 该方法也会考虑循环依赖的问题。例如,如果 Bean A 依赖于 Bean B,而 Bean B 又依赖于 Bean A,那么这种情况会被处理。
  4. 返回匹配的 bean
    • 此方法尝试返回一个与描述符匹配的 bean 实例。如果找不到合适的 bean,它可能返回 null 或抛出一个异常,具体取决于描述符的设置。
  5. 异常处理
    • 如果没有找到合适的 bean 或找到了多个合适的 bean 且没有明确的选择,该方法会抛出相应的异常。
  6. 其他 Considerations
    • 可以考虑其他因素,如 @Primary 注解。
    • 对于基本类型或字符串类型的属性,可以解析 @Value 注解,从属性文件或环境变量中获取值。

五、最佳实践

首先来看看启动类入口,上下文环境使用AnnotationConfigApplicationContext(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个MyConfiguration组件类。代码调用了两个方法(methodResolveDependencyfieldResolveDependency)来分别解析方法和字段的依赖。具体来说,它们会使用反射来获取目标方法或字段。创建一个描述这个方法或字段的 DependencyDescriptor。使用 resolveDependency 方法来从 Spring 容器中解析真正的依赖。使用反射来将解析得到的依赖注入到目标对象中。在解析方法和字段的依赖之后,代码通过格式化的字符串打印了相关信息。例如,它显示了方法或字段的完全限定名称和解析得到的依赖对象的值。

public class ResolveDependencyApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
        // 获得Bean工厂
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 被注入对象
        MyServiceB injectTarget = new MyServiceB();

        System.out.println("Before MyServiceB = " + injectTarget + "\n");

        methodResolveDependency(beanFactory, injectTarget, "setMethodMyServiceA");
        fieldResolveDependency(beanFactory, injectTarget, "fieldMyServiceA");
        fieldResolveDependency(beanFactory, injectTarget, "myPropertyValue");

        System.out.println("After MyServiceB = " + injectTarget + "\n");
    }

    /**
     * 解析方法依赖
     *
     * @param beanFactory
     * @param injectTarget
     */
    public static void methodResolveDependency(ConfigurableListableBeanFactory beanFactory, Object injectTarget, String name) {
        try {
            // 1. 获取MyServiceB类中名为setMethodMyServiceA的方法的引用
            Method method = injectTarget.getClass().getMethod(name, MyServiceA.class);

            // 2. 创建一个描述此方法参数的DependencyDescriptor
            DependencyDescriptor desc = new DependencyDescriptor(new MethodParameter(method, 0), true);

            // 3. 使用BeanFactory来解析这个方法参数的依赖
            Object value = beanFactory.resolveDependency(desc, null);

            System.out.println("解析方法依赖结果:");
            System.out.println("---------------------------------------------");
            System.out.println(String.format("Name:   %s.%s",method.getDeclaringClass().getName(),method.getName()));
            System.out.println(String.format("Value:  %s", value));
            System.out.println("---------------------------------------------\n");

            // 4. 使方法可访问(特别是如果它是private的)
            ReflectionUtils.makeAccessible(method);

            // 5. 使用反射调用这个方法,将解析得到的依赖注入到目标对象中
            method.invoke(injectTarget, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 解析字段依赖
     *
     * @param beanFactory
     * @param injectTarget
     */
    public static void fieldResolveDependency(ConfigurableListableBeanFactory beanFactory, Object injectTarget, String name) {
        try {
            // 1. 获取MyServiceB类中名为fieldMyServiceA的字段的引用
            Field field = injectTarget.getClass().getDeclaredField(name);

            // 2. 创建一个描述此字段的DependencyDescriptor
            DependencyDescriptor desc = new DependencyDescriptor(field, true);

            // 3. 使用BeanFactory来解析这个字段的依赖
            Object value = beanFactory.resolveDependency(desc, null);

            System.out.println("解析字段依赖结果:");
            System.out.println("---------------------------------------------");
            System.out.println(String.format("Name:   %s.%s", field.getDeclaringClass().getName(), field.getName()));
            System.out.println(String.format("Value:  %s", value));
            System.out.println("---------------------------------------------\n");

            // 4. 使字段可访问(特别是如果它是private的)
            ReflectionUtils.makeAccessible(field);

            // 5. 使用反射设置这个字段的值,将解析得到的依赖注入到目标对象中
            field.set(injectTarget, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

MyConfiguration类中,使用了@ComponentScan("com.xcs.spring")注解告诉 Spring 在指定的包(在这里是 "com.xcs.spring")及其子包中搜索带有 @Component@Service@Repository@Controller 等注解的类,并将它们自动注册为 beans。这样,spring就不必为每个组件明确写一个 bean 定义。Spring 会自动识别并注册它们。另外使用@PropertySource注解从类路径下的application.properties文件中加载属性。

@Configuration
@ComponentScan("com.xcs.spring")
@PropertySource("classpath:application.properties")
public class MyConfiguration {
    
}

application.properties文件在src/main/resources目录中,并添加以下内容。

my.property.value = Hello from Environment!

MyService 是一个简单的服务类,但我们没有定义任何方法或功能。

@Service
public class MyServiceA {
    
}

首先 MyServiceB 没有被 Spring 托管,那么它在代码中的表现就与一个普通的 Java 类没有什么不同。虽然 MyServiceB 本身不是 Spring 托管的,但 ResolveDependencyApplication 类中,我给大家展示了如何使用 Spring 的底层 API 手动解析和注入依赖。

  • 字段 myPropertyValue
    • 虽然它有一个 @Value 注解,但由于 MyServiceB 不是一个 Spring 管理的 bean,所以这个注解不会自动被解析。
  • 字段 myServiceAfieldMyServiceA
    • 由于没有自动的 Spring 依赖注入,这两个字段默认为 null
  • 方法 setMyServiceA
    • 这是一个普通的 setter 方法。但在 MyServiceB 不被 Spring 托管的情况下,它只是一个普通的 setter。
  • 方法 toString
    • 该方法为该类提供了一个自定义的字符串表示。这与 MyServiceB 是否被 Spring 托管无关。
public class MyServiceB {

    /**
     * 方法注入
     */
    private MyServiceA methodMyServiceA;

    /**
     * 字段注入
     */
    private MyServiceA fieldMyServiceA;

    /**
     * 字段注入 (环境变量)
     */
    @Value("${my.property.value}")
    private String myPropertyValue;

    public void setMethodMyServiceA(MyServiceA methodMyServiceA){
        this.methodMyServiceA = methodMyServiceA;
    }

    @Override
    public String toString() {
        return "MyServiceB{" +
                "myPropertyValue='" + myPropertyValue + '\'' +
                ", methodMyServiceA=" + methodMyServiceA +
                ", fieldMyServiceA=" + fieldMyServiceA +
                '}';
    }
}

运行结果发现,使用了 Spring 的底层 resolveDependency 方法来为 MyServiceB 类的字段和方法手动注入依赖。虽然在常规的 Spring 开发中我们通常不这样做(因为 Spring 提供了更高级的自动化工具进行依赖注入),但这主要目的是给大家展示 Spring 如何在底层工作原理,并提供了一种手动控制依赖注入的方法。

Before MyServiceB = MyServiceB{myPropertyValue='null', myServiceA=null, fieldMyServiceA=null}

解析方法依赖结果:
---------------------------------------------
Name:   com.xcs.spring.service.MyServiceB.setMyServiceA
Value:  com.xcs.spring.service.MyServiceA@202b0582
---------------------------------------------

解析字段依赖结果:
---------------------------------------------
Name:   com.xcs.spring.service.MyServiceB.fieldMyServiceA
Value:  com.xcs.spring.service.MyServiceA@202b0582
---------------------------------------------

解析字段依赖结果:
---------------------------------------------
Name:   com.xcs.spring.service.MyServiceB.myPropertyValue
Value:  Hello from Environment!
---------------------------------------------

After MyServiceB = MyServiceB{myPropertyValue='Hello from Environment!', myServiceA=com.xcs.spring.service.MyServiceA@202b0582, fieldMyServiceA=com.xcs.spring.service.MyServiceA@202b0582}

六、时序图

解析环境变量

通过使用@Value注解,我们可以请求一个特定的环境属性或变量。例如,我们可能会这样请求一个名为my.property.value的属性。

sequenceDiagram
Title: resolveDependency方法解析环境属性和变量时序图

ResolveDependencyApplication-->>AbstractAutowireCapableBeanFactory:resolveDependency(descriptor,requestingBeanName)
note over AbstractAutowireCapableBeanFactory: 请求解析依赖

AbstractAutowireCapableBeanFactory->>DefaultListableBeanFactory:resolveDependency(descriptor, requestingBeanName, null, null)
note over DefaultListableBeanFactory: 转到具体的Bean工厂进行解析

DefaultListableBeanFactory->>DefaultListableBeanFactory:doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter)
note over DefaultListableBeanFactory: 进行实际的依赖解析

DefaultListableBeanFactory->>DefaultListableBeanFactory:getAutowireCandidateResolver()
note over DefaultListableBeanFactory: 获取当前的依赖解析策略

DefaultListableBeanFactory->>QualifierAnnotationAutowireCandidateResolver:getSuggestedValue(descriptor)
note over QualifierAnnotationAutowireCandidateResolver: 基于给定的描述符查找建议的值

QualifierAnnotationAutowireCandidateResolver->>QualifierAnnotationAutowireCandidateResolver:findValue(annotationsToSearch)
note over QualifierAnnotationAutowireCandidateResolver: 在指定的注解中寻找值

QualifierAnnotationAutowireCandidateResolver->>QualifierAnnotationAutowireCandidateResolver:extractValue(attr)
note over QualifierAnnotationAutowireCandidateResolver: 从注解属性中提取值

QualifierAnnotationAutowireCandidateResolver->>DefaultListableBeanFactory:返回@Value注解的表达式
note over DefaultListableBeanFactory: 解析注解中的表达式或值

DefaultListableBeanFactory->>AbstractBeanFactory:resolveEmbeddedValue(value)
note over AbstractBeanFactory: 解析嵌入的值(如占位符或SpEL表达式)

DefaultListableBeanFactory->>AbstractBeanFactory:evaluateBeanDefinitionString(strVal, bd)
note over AbstractBeanFactory: 对bean定义字符串进行评估(可能是一个表达式)

DefaultListableBeanFactory->>AbstractBeanFactory:getTypeConverter()
note over AbstractBeanFactory: 获取用于类型转换的转换器

DefaultListableBeanFactory->>TypeConverterSupport:convertIfNecessary(value, type, typeDescriptor())
note over TypeConverterSupport: 根据需要将值转换为指定的类型

TypeConverterSupport->>DefaultListableBeanFactory:返回类型转换的结果
note over DefaultListableBeanFactory: 用转换后的值继续解析流程

DefaultListableBeanFactory->>AbstractAutowireCapableBeanFactory:返回环境属性
AbstractAutowireCapableBeanFactory->>ResolveDependencyApplication:返回环境属性

解析Bean依赖

这是其主要的功能。当我们有一个bean,它需要另一个bean的实例时,resolveDependency 会为我们找到并提供所需的bean。例如,如果一个MyServiceB类需要一个MyServiceA类的实例,那么resolveDependency 可以为MyServiceB类提供一个MyServiceA的实例。

sequenceDiagram
Title: resolveDependency方法解析Bean依赖时序图

ResolveDependencyApplication-->>AbstractAutowireCapableBeanFactory:resolveDependency(descriptor,requestingBeanName)
note right of AbstractAutowireCapableBeanFactory: 开始解析依赖请求

AbstractAutowireCapableBeanFactory->>DefaultListableBeanFactory:resolveDependency(descriptor, requestingBeanName, null, null)
note right of DefaultListableBeanFactory: 委托给具体的Bean工厂进行依赖解析

DefaultListableBeanFactory->>DefaultListableBeanFactory:doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter)
note right of DefaultListableBeanFactory: 执行具体的依赖解析逻辑

DefaultListableBeanFactory->>DefaultListableBeanFactory:resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter)
note right of DefaultListableBeanFactory: 解析满足条件的多个Beans

DefaultListableBeanFactory->>DefaultListableBeanFactory:findAutowireCandidates(beanName, type, descriptor)
note right of DefaultListableBeanFactory: 查找适合自动装配的候选Beans

DefaultListableBeanFactory->>DefaultListableBeanFactory:isSelfReference(beanName, candidate)
note right of DefaultListableBeanFactory: 检查bean是否引用了自身

DefaultListableBeanFactory->>DefaultListableBeanFactory:isAutowireCandidate(candidate, descriptor)
note right of DefaultListableBeanFactory: 检查bean是否是一个合适的自动装配候选

DefaultListableBeanFactory->>DefaultListableBeanFactory:addCandidateEntry(result, candidate, descriptor, requiredType)
note right of DefaultListableBeanFactory: 将找到的候选添加到结果集中

DefaultListableBeanFactory->>DependencyDescriptor:resolveCandidate(candidateName, requiredType, beanFactory)
note right of DependencyDescriptor: 尝试解析并返回候选Bean

DependencyDescriptor->>AbstractBeanFactory:getBean(String name)
note right of AbstractBeanFactory: 请求获取指定名称的bean

AbstractBeanFactory->>DependencyDescriptor:返回候选的Bean对象
note right of DependencyDescriptor: 返回找到的Bean实例

DependencyDescriptor->>DefaultListableBeanFactory:返回候选的Bean对象
note right of DefaultListableBeanFactory: 将Bean实例返回给Bean工厂

DefaultListableBeanFactory->>DefaultListableBeanFactory:determineAutowireCandidate(matchingBeans, descriptor)
note right of DefaultListableBeanFactory: 从匹配的Beans中确定自动装配的候选

DefaultListableBeanFactory->>AbstractAutowireCapableBeanFactory:返回Bean
note right of AbstractAutowireCapableBeanFactory: 将确定的Bean返回给上一级的Bean工厂

AbstractAutowireCapableBeanFactory->>ResolveDependencyApplication:返回bean
note right of ResolveDependencyApplication: 返回bean给依赖解析的请求者

七、源码分析

解析环境变量

org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency方法中,此方法将尝试使用懒加载代理或直接解析依赖项,具体使用哪种方式取决于上下文及其他配置(本次研究环境属性)。

@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
                                @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
	// ... [代码部分省略以简化]
    // 尝试为依赖获取懒加载代理
    Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
        descriptor, requestingBeanName);

    // 如果懒加载代理不可用,则执行实际的依赖解析
    if (result == null) {
        result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
    }
    return result;
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法中,此代码段尝试解析一个依赖,先查找建议的值(@Value注解),然后进行必要的类型转换。

@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
                                  @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    try {
        // ... [代码部分省略以简化]
        
        // 获取依赖描述符中定义的依赖类型
        Class<?> type = descriptor.getDependencyType();

        // 步骤1: 从自动装配候选解析器中获取建议的依赖值
        Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);

        if (value != null) {
            // 如果建议的值是一个字符串
            if (value instanceof String) {
                // 步骤2: 解析嵌入在字符串中的值(例如,从环境变量中查找)
                String strVal = resolveEmbeddedValue((String) value);

                // 获取bean定义,如果beanName存在且已经在容器中,则获取与beanName相关的bean定义
                BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
                
                // 步骤3: 对字符串值进行求值,可能会处理表达式之类的内容
                value = evaluateBeanDefinitionString(strVal, bd);
            }

            // 获取类型转换器,如果传入了外部转换器则使用它,否则使用默认的转换器
            TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());

            try {
                // 步骤4: 尝试进行必要的类型转换
                return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
            }
            catch (UnsupportedOperationException ex) {
                // 处理不支持的操作异常(具体实现已省略)
            }
        }

        // ... [代码部分省略以简化]

    }
    finally {
        // ... [代码部分省略以简化]
    }
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法步骤1。

org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#getSuggestedValue方法中,主要目的是从一个DependencyDescriptor中提取建议的值。DependencyDescriptor可以描述一个bean的属性(可能是一个字段或者方法参数)。

@Override
@Nullable
public Object getSuggestedValue(DependencyDescriptor descriptor) {
    // 从字段的注解中尝试找到建议的值
    Object value = findValue(descriptor.getAnnotations());
    
    // 如果在字段上没有找到,则从方法的注解中尝试找到
    if (value == null) {
        MethodParameter methodParam = descriptor.getMethodParameter();
        if (methodParam != null) {
            value = findValue(methodParam.getMethodAnnotations());
        }
    }
    
    // 返回建议的值,如果没有找到则返回null
    return value;
}

org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#findValue方法中,主要目的是从提供的注解数组中寻找一个特定的注解(@Value)并提取其值。

@Nullable
protected Object findValue(Annotation[] annotationsToSearch) {
    // 如果注解数组非空,则进入检查逻辑
    if (annotationsToSearch.length > 0) {   
        // 从注解数组中获取合并后的特定注解属性(这里的特定注解可能是@Value)
        AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
            AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);
        // 如果找到了相应的注解属性,则提取它的值
        if (attr != null) {
            return extractValue(attr);
        }
    }
    // 没有找到相应的注解属性或值,返回null
    return null;
}

org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#extractValue方法中,从@Value注解属性中,提取一个value属性值。

protected Object extractValue(AnnotationAttributes attr) {
    // 从注解属性中尝试获取'VALUE'(一般为"value")的属性值
    Object value = attr.get(AnnotationUtils.VALUE);
    
    // 如果该属性值为空,那么抛出一个异常
    if (value == null) {
        throw new IllegalStateException("Value annotation must have a value attribute");
    }
    
    // 返回提取的值
    return value;
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法步骤2。

org.springframework.beans.factory.support.AbstractBeanFactory#resolveEmbeddedValue方法中,主要接受一个字符串值,并利用StringValueResolver逐一对其进行解析StringValueResolver逐一对其进行解析,然后返回最终解析后的字符串值。

@Override
@Nullable
public String resolveEmbeddedValue(@Nullable String value) {
    // 如果传入的值为空,直接返回null
    if (value == null) {
        return null;
    }
    // 初始化结果为传入的值
    String result = value; 
    
    // 遍历当前对象中所有的字符串值解析器
    for (StringValueResolver resolver : this.embeddedValueResolvers) {
        // 使用解析器解析当前的结果值
        result = resolver.resolveStringValue(result);
        
        // 如果解析后的值为空,直接返回null
        if (result == null) {
            return null;
        }
    }
    
    // 返回最终解析后的字符串值
    return result;
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法步骤3。

org.springframework.beans.factory.support.AbstractBeanFactory#evaluateBeanDefinitionString方法中,主要负责对一个Bean定义中的字符串值(可能是一个表达式${my.property.value})进行求值。如果配置了beanExpressionResolver,则会使用它进行求值;如果没有,则直接返回原始值。

@Nullable
protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) {
    // 如果没有设置beanExpressionResolver(即解析表达式的解析器),直接返回原始值
    if (this.beanExpressionResolver == null) {
        return value;
    }

    Scope scope = null; // 定义作用域变量
    if (beanDefinition != null) {
        // 从Bean定义中获取作用域名称
        String scopeName = beanDefinition.getScope(); 
        if (scopeName != null) {
            // 获取该作用域的具体实例
            scope = getRegisteredScope(scopeName);
        }
    }
    // 使用beanExpressionResolver对字符串值进行求值,并返回结果
    return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法步骤4。

org.springframework.beans.TypeConverterSupport#convertIfNecessary(value,requiredType,typeDescriptor)方法中,首先确保有一个可用的typeConverterDelegate来进行实际的转换工作,然后尝试将给定值转换为指定的类型。

@Nullable
@Override
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
                                @Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {

    // 断言,确保typeConverterDelegate(类型转换代理)不为空
    Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
    try {
        // 通过typeConverterDelegate进行类型转换
        return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType, typeDescriptor);
    }
    // ... [代码部分省略以简化]
}

org.springframework.beans.TypeConverterDelegate#convertIfNecessary(propertyName, oldValue, newValue,requiredType, typeDescriptor)方法中,主要考虑了许多转换场景,使其能够处理各种类型和结构的数据。

public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
			@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {

    // 查找对于此类型的自定义编辑器
    PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);

    ConversionFailedException conversionAttemptEx = null;

    // 如果没有自定义编辑器但指定了自定义ConversionService,则尝试转换
    ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
    if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
        // ... [代码部分省略以简化]
    }

    Object convertedValue = newValue;

    // 如果值不是所需的类型,进行转换
    if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
        // ... [代码部分省略以简化]
    }

    boolean standardConversion = false;

    // 尝试应用标准类型转换规则(如果适用)
    if (requiredType != null) {
		
        // 对不同类型的专门转换逻辑
        if (convertedValue != null) {
            if (Object.class == requiredType) {
                return (T) convertedValue;
            }
            
            // 这里根据不同的requiredType类型进行了不同的转换处理,例如处理数组、集合、映射、枚举等不同类型的转换。
            else if (requiredType.isArray()) {
                // ... [代码部分省略以简化]
            }
            else if (convertedValue instanceof Collection) {
                // ... [代码部分省略以简化]
            }
            else if (convertedValue instanceof Map) {
                // ... [代码部分省略以简化]
            }
            if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
                // ... [代码部分省略以简化]
            }
            if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
                // ... [代码部分省略以简化]
            }
            else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
                // ... [代码部分省略以简化]
            }
            else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
                // ... [代码部分省略以简化]
            }
        }
        else {
            // convertedValue == null
            if (requiredType == Optional.class) {
                convertedValue = Optional.empty();
            }
        }

        if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
            // ... [代码部分省略以简化]
        }
    }

    if (conversionAttemptEx != null) {
        // ... [代码部分省略以简化]
    }

    return (T) convertedValue;
}

解析Bean依赖

org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency方法中,此方法将尝试使用懒加载代理或直接解析依赖项,具体使用哪种方式取决于上下文及其他配置(本次研究Bean依赖)。

@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
                                @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
	// ... [代码部分省略以简化]
    // 尝试为依赖获取懒加载代理
    Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
        descriptor, requestingBeanName);

    // 如果懒加载代理不可用,则执行实际的依赖解析
    if (result == null) {
        result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
    }
    return result;
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法中,主要是解析并注入依赖项。首先,它尝试解析多个Bean候选项(例如,当需要注入的是List或Map类型)。如果没有找到匹配的Bean,它可能会抛出异常。如果有多个匹配的Bean,它会尝试确定最合适的一个进行注入。如果只有一个匹配的Bean,则直接进行注入。

@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
                                  @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    // ... [代码部分省略以简化]
    try {
        // ... [代码部分省略以简化]

        // 步骤1: 尝试解析多个Bean候选项。例如,当需要注入List或Map类型的时候
        Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
        if (multipleBeans != null) {
            return multipleBeans;
        }

        // 步骤2: 寻找符合类型匹配的Bean候选项
        Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
        if (matchingBeans.isEmpty()) {
            if (isRequired(descriptor)) {
                raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
            }
            return null;
        }

        String autowiredBeanName;
        Object instanceCandidate;

        // 步骤3: 如果有多个匹配的Bean,则尝试确定哪一个是最合适的
        if (matchingBeans.size() > 1) {
            autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
            if (autowiredBeanName == null) {
                if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
                    return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
                }
                else {
                    // 对于非必需的Collection/Map,忽略非唯一的情况
                    return null;
                }
            }
            instanceCandidate = matchingBeans.get(autowiredBeanName);
        }
        else {
            // 只有一个匹配的Bean
            Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
            autowiredBeanName = entry.getKey();
            instanceCandidate = entry.getValue();
        }

        // 记录已经自动装配的Bean名称
        if (autowiredBeanNames != null) {
            autowiredBeanNames.add(autowiredBeanName);
        }

        // 如果候选对象是一个类,则尝试解析为实际的Bean实例
        if (instanceCandidate instanceof Class) {
            instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
        }
        Object result = instanceCandidate;
        
        // ... [代码部分省略以简化]
        
        return result;
    }
    finally {
        // ... [代码部分省略以简化]
    }
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法步骤1。

org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveMultipleBeans方法中,解决了当依赖是多值类型(如Stream、数组、CollectionMap)时如何自动注入对应的Bean。该方法首先判断依赖的类型,然后针对不同类型进行解析。对于每种类型,它都会查找所有匹配的Bean候选项,并按需要转换和排序它们。

@Nullable
private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
        @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {

    Class<?> type = descriptor.getDependencyType();

    // 判断依赖是否为Java 8 Stream类型
    if (descriptor instanceof StreamDependencyDescriptor) {
       // ... [代码部分省略以简化]
    } 
    // 判断依赖是否为数组类型
    else if (type.isArray()) {
        // ... [代码部分省略以简化]
        return result;
    }
    // 判断依赖是否为集合接口类型
    else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
        // ... [代码部分省略以简化]
        return result;
    }
    // 判断依赖是否为Map类型
    else if (Map.class == type) {
        // ... [代码部分省略以简化]
        return matchingBeans;
    }
    else {
        return null;
    }
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法步骤2。

org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates方法中,主要目的是确定哪些Bean适合自动注入给定的依赖。它首先从当前的BeanFactory及其祖先中获取所有可能的候选Bean,然后根据各种条件(如类型匹配、qualifiers等)过滤这些候选项。如果在首次查找中没有找到任何匹配的bean,它还会尝试回退匹配来找到适当的bean。

protected Map<String, Object> findAutowireCandidates(
        @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

    // 1. 从BeanFactory及其祖先中获取所有可能的候选bean名称,这些bean的类型与所需的类型匹配。
    String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
        this, requiredType, true, descriptor.isEager());

    // 2. 初始化一个空的结果映射,用于保存候选bean。
    Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);

    // 3. 遍历预定义的可解析的依赖项。
    for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
        Class<?> autowiringType = classObjectEntry.getKey();

        // 4. 检查当前的依赖项是否与所需的类型兼容。
        if (autowiringType.isAssignableFrom(requiredType)) {
            Object autowiringValue = classObjectEntry.getValue();
            // 5. 对当前的值进行自动装配的解析。
            autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
            // 6. 如果解析后的值与所需的类型相匹配,将其添加到结果映射中。
            if (requiredType.isInstance(autowiringValue)) {
                result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                break;
            }
        }
    }

    // 7. 遍历所有可能的候选bean名称。
    for (String candidate : candidateNames) {
        // 8. 如果当前bean名称不是对自己的引用,并且它是一个有效的自动装配候选项,则将其添加到结果映射中。
        if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
            addCandidateEntry(result, candidate, descriptor, requiredType);
        }
    }

    // 9. 如果没有找到任何匹配的候选bean。
    if (result.isEmpty()) {
        boolean multiple = indicatesMultipleBeans(requiredType);

        // 10. 为回退匹配创建一个新的描述符。
        DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();

        // 11. 重新遍历所有候选bean名称。
        for (String candidate : candidateNames) {
            // 12. 如果当前bean名称不是对自己的引用,并且它是一个有效的回退匹配的候选项,则将其添加到结果映射中。
            if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
                (!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
                addCandidateEntry(result, candidate, descriptor, requiredType);
            }
        }

        // 13. 如果还没有找到任何匹配的候选bean,考虑自引用的bean作为候选bean。
        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);
                }
            }
        }
    }
    // 14. 返回找到的候选bean的映射。
    return result;
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#addCandidateEntry方法中,根据不同的DependencyDescriptor类型和其他条件决定如何将候选Bean及其实例或类型添加到提供的映射中。对于需要多个元素的依赖项(如集合或数组),它会添加所有的Bean实例;对于单例或需要排序的流描述符,它会添加Bean实例;而对于其他情况,只会添加Bean的类型。

private void addCandidateEntry(Map<String, Object> candidates, String candidateName,
			DependencyDescriptor descriptor, Class<?> requiredType) {

    // 如果DependencyDescriptor是一个MultiElementDescriptor(意味着描述的依赖可能有多个元素,例如集合或数组)
	if (descriptor instanceof MultiElementDescriptor) {
        // 解析候选Bean的实例
		Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
		// 如果解析的实例不是NullBean(即一个占位符对象,代表null值),则将其添加到候选列表中
		if (!(beanInstance instanceof NullBean)) {
			candidates.put(candidateName, beanInstance);
		}
	}
	// 如果当前Bean是单例,或者DependencyDescriptor是一个StreamDependencyDescriptor并且有序
	else if (containsSingleton(candidateName) || (descriptor instanceof StreamDependencyDescriptor &&
			((StreamDependencyDescriptor) descriptor).isOrdered())) {
        // 解析候选Bean的实例
		Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
		// 如果解析的实例是NullBean,则将null添加到映射中,否则添加实际的Bean实例
		candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
	}
	// 其他情况下,只将Bean的类型而不是实例添加到映射中
	else {
		candidates.put(candidateName, getType(candidateName));
	}
}

org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate方法中,最后发现,在Spring的依赖注入过程中,当一个bean依赖于另一个bean时,底层的实现最终会通过getBean方法从容器中获取所依赖的bean实例。

public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
    	throws BeansException {

    // 根据提供的bean名称从beanFactory中获取并返回bean的实例
    return beanFactory.getBean(beanName);
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法步骤3。

org.springframework.beans.factory.support.DefaultListableBeanFactory#determineAutowireCandidate方法中,主要是存在多个候选bean时确定应该自动注入哪一个。它首先检查是否有被标记为"primary"的bean,然后检查是否有优先级最高的bean。如果这两种方法都无法确定,它会回退到查找与依赖描述符匹配的bean名字或是否该bean实例存在于可解析的依赖中。如果仍然没有找到匹配的bean,它会返回null。

@Nullable
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
    // 获取依赖的类型
    Class<?> requiredType = descriptor.getDependencyType();
    
    // 确定是否有被标记为"primary"的候选bean
    String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
    if (primaryCandidate != null) {
        // 如果有主要的候选bean,则返回这个bean的名字
        return primaryCandidate;
    }
    
    // 检查是否有最高优先级的候选bean
    String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
    if (priorityCandidate != null) {
        // 如果有优先级最高的候选bean,则返回这个bean的名字
        return priorityCandidate;
    }
    
    // 如果上述两种方法都无法确定一个bean,那么进行回退处理
    for (Map.Entry<String, Object> entry : candidates.entrySet()) {
        String candidateName = entry.getKey();
        Object beanInstance = entry.getValue();
        
        // 如果候选bean实例存在于可解析的依赖中,或者候选bean的名字与描述符中的依赖名字匹配,则选择该候选bean
        if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
                matchesBeanName(candidateName, descriptor.getDependencyName())) {
            return candidateName;
        }
    }
    
    // 如果没有匹配的bean,返回null
    return null;
}

八、注意事项

  1. 循环依赖
    • 确保不引入循环依赖,否则会导致 Bean 创建失败。Spring 有一些内置的检测机制,但在自定义逻辑时仍需格外注意。
  2. 候选 Bean 的选择
    • 在有多个候选 Bean 可用于注入时,该如何选择是一个重要问题。Spring 提供了 @Primary, @Priority 等注解来辅助决策,但你可能需要考虑其他因素。
  3. 自定义类型转换
    • 如果需要,应确保提供适当的类型转换逻辑,以将 Bean 转换为所需的类型。
  4. 性能考虑
    • 避免不必要的重复查找或计算。在可能的情况下,考虑缓存结果,特别是对于高频率的依赖查找。
  5. 生命周期和作用域
    • 确保考虑到目标 Bean 的生命周期和作用域。例如,一个 prototype 作用域的 Bean 不应该被注入到一个 singleton 作用域的 Bean 中,除非你明确知道这样做的后果。
  6. 考虑非常规的依赖源
    • resolveDependency 可能需要考虑来自非常规来源的依赖,如 FactoryBeansBeanFactory 或其他特殊的依赖提供者。

九、总结

最佳实践总结

  1. 明确环境和配置

    • 在示例中,使用了 AnnotationConfigApplicationContext 作为 Spring 的应用上下文,它基于 Java 注解来配置和初始化 Spring 容器。指定了 MyConfiguration 类作为配置源,它用于引导整个应用程序的上下文环境。
  2. 简化依赖解析

    • 通过将依赖解析过程封装到具体的方法中 (methodResolveDependencyfieldResolveDependency),代码的逻辑变得清晰且可维护。
  3. 利用Spring的底层API

    • 虽然大部分时间我们依赖于 Spring 的自动化特性进行依赖注入,但在这个实践中,我们深入地使用了 Spring 的底层 resolveDependency 方法来手动解析和注入依赖。这不仅展示了 Spring 的内部工作原理,还提供了当自动注入不适用时的替代解决方案。
  4. 手动依赖注入的用例

    • 即使 MyServiceB 不是由 Spring 托管的 Bean,我们仍然能够使用 Spring 的机制为它解析和注入所需的依赖。这种能力在某些特定场景下可能非常有用,例如当我们需要将 Spring 与非 Spring 管理的代码集成时。
  5. 使用反射增强灵活性

    • 通过反射API,我们能够动态地访问和操作目标对象的方法和字段,无论它们是否是公开的。这为我们提供了极大的灵活性,特别是当我们需要操作第三方库中的类时。
  6. 确保配置的完整性

    • 确保所需的所有配置资源,如 application.properties 文件,都在正确的位置并且被正确加载。在这个示例中,它被用来为 MyServiceB 提供一个属性值。
  7. 验证和测试

    • 最后,通过打印方法,确保了所有依赖都已正确解析和注入。在实际应用中,我们应该有相应的单元测试来验证这些行为。

源码分析总结

解析环境变量

  1. 解析 @Value 注解
    • doResolveDependency 方法首先检查是否存在一个建议的值,通常来自 @Value 注解。如果找到这样的值,它会首先进行字符串替换(例如替换属性占位符),然后可能对该值进行求值(如果它是一个表达式),最后尝试将该值转换为目标类型。
  2. 类型转换
    • 如果找到了一个建议的值,或者已经解析了一个依赖项但其类型与所需的类型不匹配,系统会尝试进行类型转换。Spring有一个强大的类型转换系统,能够处理各种各样的转换场景。
  3. 深入的类型转换
    • 如果需要进行更复杂的类型转换(例如从集合到数组、从字符串到数字等),Spring提供了许多内置的转换规则来处理这些场景。

解析Bean依赖

  1. 依赖解析的起点
    • resolveDependency是Spring的主要方法,用于解析bean的依赖关系。
  2. 懒加载代理判断
    • 首先,尝试使用懒加载代理满足依赖关系。如果不能使用懒加载代理,它会进一步尝试解析依赖。
  3. 解析具体的依赖
    • doResolveDependency中,根据依赖的类型,Spring可能尝试解析多个Bean候选项,如List或Map。它会搜索匹配的Bean,并根据条件(如类型、qualifiers等)选择合适的Bean进行注入。
  4. 多值依赖的处理
    • 如果依赖是多值类型(如集合或映射),Spring将在resolveMultipleBeans中进行处理。
  5. 确定注入的Bean
    • 如果有多个可能的Bean候选项,determineAutowireCandidate将帮助确定哪一个Bean是最佳的注入选择,这可能基于“primary”标记或Bean的优先级。
  6. 获取实际的Bean实例
    • 在所有这些决策之后,Spring最终会通过getBean方法获取并返回所依赖的Bean实例。