From 32b0053117462f18f2e08f8d8c56e5c802026e15 Mon Sep 17 00:00:00 2001 From: xuchengsheng Date: Thu, 26 Oct 2023 16:27:54 +0800 Subject: [PATCH] =?UTF-8?q?resolveDependency=E6=96=B9=E6=B3=95=E6=BA=90?= =?UTF-8?q?=E7=A0=81=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spring-core-resolveDependency/README.md | 659 +++++++++++++++++- .../spring/ResolveDependencyApplication.java | 1 + 2 files changed, 653 insertions(+), 7 deletions(-) diff --git a/spring-core/spring-core-resolveDependency/README.md b/spring-core/spring-core-resolveDependency/README.md index 05828f5..3ae0a58 100644 --- a/spring-core/spring-core-resolveDependency/README.md +++ b/spring-core/spring-core-resolveDependency/README.md @@ -1,6 +1,26 @@ +## resolveDependency + +- [resolveDependency](#resolvedependency) + - [一、基本信息](#一基本信息) + - [二、方法描述](#二方法描述) + - [三、方法源码](#三方法源码) + - [四、主要功能](#四主要功能) + - [五、最佳实践](#五最佳实践) + - [六、时序图](#六时序图) + - [解析环境变量](#解析环境变量) + - [解析Bean依赖](#解析bean依赖) + - [七、源码分析](#七源码分析) + - [解析环境变量](#解析环境变量-1) + - [解析Bean依赖](#解析bean依赖-1) + - [八、注意事项](#八注意事项) + - [九、总结](#九总结) + - [最佳实践总结](#最佳实践总结) + - [源码分析总结](#源码分析总结) + + ### 一、基本信息 -✒️ **作者** - Lex 📝 **博客** - [我的CSDN]() 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) +✒️ **作者** - Lex 📝 **博客** - [CSDN](https://blog.csdn.net/duzhuang2399) | [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [GitHub](https://github.com/xuchengsheng/spring-reading) ### 二、方法描述 @@ -79,7 +99,7 @@ public class ResolveDependencyApplication { */ public static void methodResolveDependency(ConfigurableListableBeanFactory beanFactory, Object injectTarget, String name) { try { - // 1. 获取MyServiceB类中名为setMyServiceA的方法的引用 + // 1. 获取MyServiceB类中名为setMethodMyServiceA的方法的引用 Method method = injectTarget.getClass().getMethod(name, MyServiceA.class); // 2. 创建一个描述此方法参数的DependencyDescriptor @@ -239,7 +259,7 @@ After MyServiceB = MyServiceB{myPropertyValue='Hello from Environment!', myServi ### 六、时序图 -#### 环境属性和变量 +#### 解析环境变量 通过使用`@Value`注解,我们可以请求一个特定的环境属性或变量。例如,我们可能会这样请求一个名为`my.property.value`的属性。 @@ -290,7 +310,7 @@ DefaultListableBeanFactory->>AbstractAutowireCapableBeanFactory:返回环境属 AbstractAutowireCapableBeanFactory->>ResolveDependencyApplication:返回环境属性 ~~~ -#### Bean依赖 +#### 解析Bean依赖 这是其主要的功能。当我们有一个bean,它需要另一个bean的实例时,`resolveDependency` 会为我们找到并提供所需的bean。例如,如果一个`MyServiceB`类需要一个`MyServiceA`类的实例,那么`resolveDependency` 可以为`MyServiceB`类提供一个`MyServiceA`的实例。 @@ -345,14 +365,639 @@ 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` 或其他特殊的依赖提供者。 + ### 九、总结 #### 最佳实践总结 -#### 源码分析总结 \ No newline at end of file +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实例。 \ No newline at end of file diff --git a/spring-core/spring-core-resolveDependency/src/main/java/com/xcs/spring/ResolveDependencyApplication.java b/spring-core/spring-core-resolveDependency/src/main/java/com/xcs/spring/ResolveDependencyApplication.java index a4568c4..534737b 100644 --- a/spring-core/spring-core-resolveDependency/src/main/java/com/xcs/spring/ResolveDependencyApplication.java +++ b/spring-core/spring-core-resolveDependency/src/main/java/com/xcs/spring/ResolveDependencyApplication.java @@ -3,6 +3,7 @@ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.service.MyServiceA; import com.xcs.spring.service.MyServiceB; +import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.context.annotation.AnnotationConfigApplicationContext;