spring-reading./spring-core/spring-core-resolveDependency/README.md

1003 lines
49 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

## 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<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`注解),然后进行必要的类型转换。
```java
@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的属性可能是一个字段或者方法参数
```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> 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)`方法中,主要考虑了许多转换场景,使其能够处理各种类型和结构的数据。
```java
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依赖
```java
@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则直接进行注入。
```java
@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`、数组、`Collection`、`Map`时如何自动注入对应的Bean。该方法首先判断依赖的类型然后针对不同类型进行解析。对于每种类型它都会查找所有匹配的Bean候选项并按需要转换和排序它们。
```java
@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。
```java
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的类型。
```java
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实例。
```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<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` 可能需要考虑来自非常规来源的依赖,如 `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实例。