## resolveDependency - [resolveDependency](#resolvedependency) - [一、基本信息](#一基本信息) - [二、方法描述](#二方法描述) - [三、方法源码](#三方法源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [解析环境变量](#解析环境变量) - [解析Bean依赖](#解析bean依赖) - [七、源码分析](#七源码分析) - [解析环境变量](#解析环境变量-1) - [解析Bean依赖](#解析bean依赖-1) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [CSDN](https://blog.csdn.net/duzhuang2399) | [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [GitHub](https://github.com/xuchengsheng/spring-reading) ### 二、方法描述 `resolveDependency` 是 Spring 框架中 `ConfigurableListableBeanFactory` 接口中定义的一个方法。这个方法的核心功能是解析并提供 bean 的依赖项。它主要在处理 `@Autowired` , `@Inject` ,`@Value`这样的自动装配场景时起作用。 ### 三、方法源码 Spring IOC 容器中的一个核心方法,用于解析 bean 之间的依赖关系。当容器需要知道应该为特定的 bean、字段、构造函数或方法注入哪个其他 bean 时,就会调用这个方法。 ```java /** * 在此工厂中定义的 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`组件类。代码调用了两个方法(`methodResolveDependency` 和 `fieldResolveDependency`)来分别解析方法和字段的依赖。具体来说,它们会使用反射来获取目标方法或字段。创建一个描述这个方法或字段的 `DependencyDescriptor`。使用 `resolveDependency` 方法来从 Spring 容器中解析真正的依赖。使用反射来将解析得到的依赖注入到目标对象中。在解析方法和字段的依赖之后,代码通过格式化的字符串打印了相关信息。例如,它显示了方法或字段的完全限定名称和解析得到的依赖对象的值。 ```java 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`文件中加载属性。 ```java @Configuration @ComponentScan("com.xcs.spring") @PropertySource("classpath:application.properties") public class MyConfiguration { } ``` `application.properties`文件在`src/main/resources`目录中,并添加以下内容。 ```properties my.property.value = Hello from Environment! ``` `MyService` 是一个简单的服务类,但我们没有定义任何方法或功能。 ```java @Service public class MyServiceA { } ``` 首先 `MyServiceB` 没有被 Spring 托管,那么它在代码中的表现就与一个普通的 Java 类没有什么不同。虽然 `MyServiceB` 本身不是 Spring 托管的,但 `ResolveDependencyApplication` 类中,我给大家展示了如何使用 Spring 的底层 API 手动解析和注入依赖。 - **字段 `myPropertyValue`** + 虽然它有一个 `@Value` 注解,但由于 `MyServiceB` 不是一个 Spring 管理的 bean,所以这个注解不会自动被解析。 - **字段 `myServiceA` 和 `fieldMyServiceA`** + 由于没有自动的 Spring 依赖注入,这两个字段默认为 `null`。 - **方法 `setMyServiceA`** + 这是一个普通的 setter 方法。但在 `MyServiceB` 不被 Spring 托管的情况下,它只是一个普通的 setter。 - **方法 `toString`** + 该方法为该类提供了一个自定义的字符串表示。这与 `MyServiceB` 是否被 Spring 托管无关。 ```java 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 如何在底层工作原理,并提供了一种手动控制依赖注入的方法。 ```java 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`的属性。 ~~~mermaid 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`的实例。 ~~~mermaid 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`方法中,此方法将尝试使用懒加载代理或直接解析依赖项,具体使用哪种方式取决于上下文及其他配置(本次研究环境属性)。 ```java @Override @Nullable public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set 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`注解),然后进行必要的类型转换。 ```java @Nullable public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set 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的属性(可能是一个字段或者方法参数)。 ```java @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`)并提取其值。 ```java @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`属性值。 ```java 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`逐一对其进行解析,然后返回最终解析后的字符串值。 ```java @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`,则会使用它进行求值;如果没有,则直接返回原始值。 ```java @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`来进行实际的转换工作,然后尝试将给定值转换为指定的类型。 ```java @Nullable @Override public T convertIfNecessary(@Nullable Object value, @Nullable Class 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)`方法中,主要考虑了许多转换场景,使其能够处理各种类型和结构的数据。 ```java public T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class 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依赖)。 ```java @Override @Nullable public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set 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,则直接进行注入。 ```java @Nullable public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set 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 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 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`、数组、`Collection`、`Map`)时如何自动注入对应的Bean。该方法首先判断依赖的类型,然后针对不同类型进行解析。对于每种类型,它都会查找所有匹配的Bean候选项,并按需要转换和排序它们。 ```java @Nullable private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set 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。 ```java protected Map findAutowireCandidates( @Nullable String beanName, Class requiredType, DependencyDescriptor descriptor) { // 1. 从BeanFactory及其祖先中获取所有可能的候选bean名称,这些bean的类型与所需的类型匹配。 String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); // 2. 初始化一个空的结果映射,用于保存候选bean。 Map result = CollectionUtils.newLinkedHashMap(candidateNames.length); // 3. 遍历预定义的可解析的依赖项。 for (Map.Entry, 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的类型。 ```java private void addCandidateEntry(Map 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实例。 ```java 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。 ```java @Nullable protected String determineAutowireCandidate(Map 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 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` 可能需要考虑来自非常规来源的依赖,如 `FactoryBeans`、`BeanFactory` 或其他特殊的依赖提供者。 ### 九、总结 #### 最佳实践总结 1. **明确环境和配置** + 在示例中,使用了 `AnnotationConfigApplicationContext` 作为 Spring 的应用上下文,它基于 Java 注解来配置和初始化 Spring 容器。指定了 `MyConfiguration` 类作为配置源,它用于引导整个应用程序的上下文环境。 2. **简化依赖解析** + 通过将依赖解析过程封装到具体的方法中 (`methodResolveDependency` 和 `fieldResolveDependency`),代码的逻辑变得清晰且可维护。 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实例。