@Autowired注解源碼分析

@Autowired

一、基本信息

?? 作者 - Lex ?? 博客 - 我的CSDN ?? 文章目錄 - 所有文章 ?? 源碼地址 - @Autowired源碼

二、注解描述

@Autowired 注解,用于實現(xiàn)依賴注入(Dependency Injection, DI)。當我們在 Spring 中定義了一個 Bean 并想要使用另一個 Bean 時,可以使用 @Autowired 注解來自動注入所需的 Bean,而我們無需手動查找和配置它。

三、接口源碼

@Autowired注解是 Spring 框架自 2.5 版本開始引入的一個核心注解,該注解用于告知 Spring 框架的依賴注入工具自動注入所需的依賴。

/**
 * 通過Spring的依賴注入機制標記構造函數、字段、setter方法或配置方法。
 * 這是JSR-330 Inject 注解的一種替代,增加了必需與可選的語義。
 *
 * 自動注入的構造函數:
 * 任何給定的bean類只有一個構造函數可以聲明此注解,且其required屬性設置為 true,
 * 表示當作為Spring bean使用時要自動注入的構造函數。如果required屬性設置為true,
 * 則只能有一個構造函數被標記為Autowired。如果多個非必需的構造函數聲明了這個注解,
 * 它們都會被視為自動注入的候選者。Spring會選擇可以滿足最多依賴的構造函數進行注入。如果沒有一個候選者滿足條件,
 * 則會使用默認的構造函數。如果一個類聲明了多個構造函數,但沒有一個使用Autowired,
 * 則會使用默認的構造函數。如果一個類從一開始就只聲明了一個構造函數,它總是會被使用,即使沒有被標記。被標記的構造函數不需要是public的。
 *
 * 自動注入的字段:
 * 字段會在bean構造完成后注入,任何配置方法調用之前。配置字段不需要是public的。
 *
 * 自動注入的方法:
 * 配置方法可以有任意名稱和任意數量的參數;其中每一個參數都會通過Spring容器中的一個匹配的bean進行自動注入。
 * Bean屬性的setter方法在實質上只是這種通用配置方法的一個特例。這些配置方法不需要是public的。
 *
 * 自動注入的參數:
 * 盡管從Spring 5.0開始Autowired可以在方法或構造函數的單獨參數上聲明,
 * 但框架的大部分都會忽略這樣的聲明。唯一支持自動注入參數的Spring核心部分是
 * spring-test模塊中的JUnit Jupiter支持。
 *
 * 多參數與'required'語義:
 * 對于多參數的構造函數或方法,required屬性適用于所有參數。
 *
 * 自動裝配數組、集合和映射:
 * 對于數組、Collection或Map類型的依賴,容器會自動注入所有匹配聲明的值類型的bean。
 * 對于這種目的,map的鍵必須聲明為類型String,它會解析為相應的bean名稱。
 *
 * 在BeanPostProcessor或BeanFactoryPostProcessor中不支持:
 * 請注意,實際的注入是通過BeanPostProcessor執(zhí)行的,這意味著我們不能使用Autowired在
 * BeanPostProcessor或BeanFactoryPostProcessor類型中進行注入。
 *
 * @author Juergen Hoeller
 * @author Mark Fisher
 * @author Sam Brannen
 * @since 2.5
 * @see AutowiredAnnotationBeanPostProcessor
 * @see Qualifier
 * @see Value
 */
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {

    /**
     * 聲明注解的依賴是否是必需的。
     * 默認值為true。
     */
    boolean required() default true;
}

四、主要功能

  1. 自動注入依賴
    • 不需要明確指定 bean 之間的關系,Spring 會自動找到并注入所需的依賴。
  2. 字段注入
    • 可以直接標記在類的字段上,使得該字段在 Bean 初始化時被自動注入。
  3. 構造函數注入
    • 當標記在構造函數上時,該構造函數會被用于創(chuàng)建 bean 實例并注入所需的依賴。
  4. 方法注入
    • 當標記在 setter 方法或其他方法上時,這些方法會在 Bean 初始化時被調用以注入依賴。
  5. 指定必需性
    • 通過 required 屬性,可以指定某個依賴是否是必需的。如果標記為必需但沒有找到相應的依賴,Spring 會拋出異常。

五、最佳實踐

首先來看看啟動類入口,上下文環(huán)境使用AnnotationConfigApplicationContext(此類是使用Java注解來配置Spring容器的方式),構造參數我們給定了一個MyConfiguration組件類。然后從Spring上下文中獲取一個MyController類型的bean并調用了showService方法,

public class AutowiredApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
        MyController controller = context.getBean(MyController.class);
        controller.showService();
    }
}

MyConfiguration類中,使用了@ComponentScan("com.xcs.spring")注解告訴 Spring 在指定的包(在這里是 "com.xcs.spring")及其子包中搜索帶有 @Component、@Service、@Repository@Controller 等注解的類,并將它們自動注冊為 beans。這樣,spring就不必為每個組件明確寫一個 bean 定義。Spring 會自動識別并注冊它們。

@Configuration
@ComponentScan("com.xcs.spring")
public class MyConfiguration {

}

Spring 容器在初始化 MyController 時自動注入一個 MyService 類型的 bean 到 myService 字段。

@Controller
public class MyController {

    @Autowired
    private MyService myService;

    public void showService(){
        System.out.println("myService = " + myService);
    }
}

MyService 是一個簡單的服務類,但我們沒有定義任何方法或功能。

@Service
public class MyService {
    
}

運行結果發(fā)現(xiàn),我們使用 @Autowired 注解的功能,在我們的 Spring 上下文中工作正常,并且它成功地自動注入了所需的依賴關系。

myService = com.xcs.spring.service.MyService@4a883b15

六、時序圖

sequenceDiagram
Title: @Autowired注解時序圖
AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName)<br>應用Bean定義的后置處理器
AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor:postProcessMergedBeanDefinition(beanDefinition,beanType,beanName)<br>處理已合并的Bean定義
AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:findAutowiringMetadata(beanName,clazz,pvs)<br>查找自動注入的元數據
AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:buildAutowiringMetadata(clazz)<br>構建自動注入的元數據
AutowiredAnnotationBeanPostProcessor->>ReflectionUtils:doWithLocalFields(clazz,fc)<br>處理類的本地字段
ReflectionUtils->>AutowiredAnnotationBeanPostProcessor:解析有@Autowired注解的字段
AutowiredAnnotationBeanPostProcessor->>ReflectionUtils:doWithLocalMethods(clazz,fc)<br>處理類的本地方法
ReflectionUtils->>AutowiredAnnotationBeanPostProcessor:解析有@Autowired注解的方法
AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:injectionMetadataCache.put(cacheKey, metadata)<br>將元數據存入緩存
AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:populateBean(beanName,mbd,bw)<br>填充Bean的屬性值
AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor:postProcessProperties(pvs,bean,beanName)<br>后處理Bean的屬性
AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:findAutowiringMetadata(beanName,clazz,pvs)<br>再次查找自動注入的元數據
Note right of AutowiredAnnotationBeanPostProcessor:<br>從緩存中獲取注入的元數據
AutowiredAnnotationBeanPostProcessor->>InjectionMetadata:inject(bean, beanName, pvs)<br>執(zhí)行實際的屬性注入
InjectionMetadata->>AutowiredFieldElement:inject(target, beanName, pvs)<br>注入特定的字段元素
AutowiredFieldElement->>AutowiredFieldElement:resolveFieldValue(field,bean,beanName)<br>解析字段的值
AutowiredFieldElement->>DefaultListableBeanFactory:resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)<br>解析字段的依賴
DefaultListableBeanFactory->>DefaultListableBeanFactory:doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter)<br>解析指定的依賴關系
DefaultListableBeanFactory->>DefaultListableBeanFactory:findAutowireCandidates(beanName, type, descriptor)<br>查找符合自動裝配條件的候選 Bean
DefaultListableBeanFactory->>DefaultListableBeanFactory:addCandidateEntry(result, candidate, descriptor, requiredType)<br>向結果集中添加候選 Bean
DefaultListableBeanFactory->>AbstractBeanFactory:getType(name)<br>獲取指定 Bean 的類型
AbstractBeanFactory->>DefaultListableBeanFactory:返回被依賴Bean的類<br>返回依賴 Bean 的實際類
DefaultListableBeanFactory->>DependencyDescriptor:resolveCandidate(beanName, requiredType, beanFactory)<br>解析候選的依賴 Bean
DependencyDescriptor->>AbstractBeanFactory:getBean(name)<br>獲取指定的 Bean 實例
AbstractBeanFactory->>DependencyDescriptor:<br>返回具體的依賴 Bean 實例
DependencyDescriptor->>DefaultListableBeanFactory:<br>返回依賴的 Bean 實例給工廠
DefaultListableBeanFactory->>AutowiredFieldElement:<br>返回依賴的 Bean 給字段注入器
AutowiredFieldElement->>Field:field.set(bean, value)<br>實際設置 Bean 的字段值

七、源碼分析

前置條件

在Spring中,AutowiredAnnotationBeanPostProcessor是處理@Autowired等注解的關鍵類,它實現(xiàn)了下述兩個接口。因此,為了深入理解@Autowired的工作方式,研究這個類是非常有用的。簡而言之,為了完全理解@Autowired的工作機制,了解下述接口確實是必要的。這兩個接口提供了對bean生命周期中關鍵階段的干預,從而允許進行屬性注入和其他相關的操作。

  1. MergedBeanDefinitionPostProcessor接口
    • 此接口提供的postProcessMergedBeanDefinition方法允許后處理器修改合并后的bean定義。合并后的bean定義是一個已經考慮了所有父bean定義屬性的bean定義。對于@Autowired注解的處理,這一步通常涉及到收集需要被解析的@Autowired注解信息并準備對其進行后續(xù)處理。
    • ?? MergedBeanDefinitionPostProcessor接口傳送門
  2. InstantiationAwareBeanPostProcessor接口
    • 此接口提供了幾個回調方法,允許后處理器在bean實例化之前和實例化之后介入bean的創(chuàng)建過程。特別是,postProcessProperties方法允許后處理器對bean的屬性進行操作。對于@Autowired注解,這通常需要在屬性設置或依賴注入階段對 bean 進行處理,并將解析得到的值注入到bean中。
    • ?? InstantiationAwareBeanPostProcessor接口傳送門

收集階段

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition方法中,主要確保給定的bean定義與其預期的自動裝配元數據一致。

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    // 對于給定的bean名稱和類型,它首先嘗試查找相關的InjectionMetadata,這可能包含了該bean的字段和方法的注入信息
    InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
    
    // 使用找到的InjectionMetadata來驗證bean定義中的配置成員是否與預期的注入元數據匹配。
    metadata.checkConfigMembers(beanDefinition);
}

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata方法中,確保了始終為給定的bean名稱和類獲取最新和相關的InjectionMetadata,并利用緩存機制優(yōu)化性能。

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
    // 如果beanName為空,則使用類名作為緩存鍵。
    String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
    // 首先嘗試從并發(fā)緩存中獲取InjectionMetadata。
    InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
    // 檢查獲取到的元數據是否需要刷新。
    if (InjectionMetadata.needsRefresh(metadata, clazz)) {
        // 使用雙重檢查鎖定確保線程安全。
        synchronized (this.injectionMetadataCache) {
            metadata = this.injectionMetadataCache.get(cacheKey);
            if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                // 如果有舊的元數據,清除它。
                if (metadata != null) {
                    metadata.clear(pvs);
                }
                // 為給定的類構建新的InjectionMetadata。
                metadata = buildAutowiringMetadata(clazz);
                // 將新構建的元數據更新到緩存中。
                this.injectionMetadataCache.put(cacheKey, metadata);
            }
        }
    }
    // 返回找到的或新構建的元數據。
    return metadata;
}

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata方法中,查找類及其所有父類中的字段和方法,以找出所有帶有自動裝配注解的字段和方法,并為它們創(chuàng)建一個統(tǒng)一的InjectionMetadata對象。

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
    // 檢查類是否含有自動裝配注解,若無則直接返回空的InjectionMetadata。
    if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
        return InjectionMetadata.EMPTY;
    }

    // 初始化存放注入元素的列表。
    List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
    Class<?> targetClass = clazz;

    do {
        // 當前類中要注入的元素列表。
        final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

        // 處理類中的所有字段。
        ReflectionUtils.doWithLocalFields(targetClass, field -> {
            // 查找字段上的自動裝配注解。
            MergedAnnotation<?> ann = findAutowiredAnnotation(field);
            if (ann != null) {
                
                // ... [代碼部分省略以簡化]
                
                boolean required = determineRequiredStatus(ann);
                // 創(chuàng)建一個新的AutowiredFieldElement并加入到列表。
                currElements.add(new AutowiredFieldElement(field, required));
            }
        });

        // 處理類中的所有方法。
        ReflectionUtils.doWithLocalMethods(targetClass, method -> {
            Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
            if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                return;
            }
            // 查找方法上的自動裝配注解。
            MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
            if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                
                // ... [代碼部分省略以簡化]
                
                boolean required = determineRequiredStatus(ann);
                PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                // 創(chuàng)建一個新的AutowiredMethodElement并加入到列表。
                currElements.add(new AutowiredMethodElement(method, required, pd));
            }
        });

        // 將當前類的注入元素加入到總的注入元素列表的開頭。
        elements.addAll(0, currElements);
        // 處理父類。
        targetClass = targetClass.getSuperclass();
    }
    // 循環(huán)直至Object類。
    while (targetClass != null && targetClass != Object.class);

    // 返回為元素列表創(chuàng)建的新的InjectionMetadata。
    return InjectionMetadata.forElements(elements, clazz);
}

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#autowiredAnnotationTypes字段中,主要的用途是告訴AutowiredAnnotationBeanPostProcessor哪些注解它應該處理。當Spring容器解析bean定義并創(chuàng)建bean實例時,如果這個bean的字段、方法或構造函數上的注解被包含在這個autowiredAnnotationTypes集合中,那么AutowiredAnnotationBeanPostProcessor就會對它進行處理。

public AutowiredAnnotationBeanPostProcessor() {
   this.autowiredAnnotationTypes.add(Autowired.class);
   // ... [代碼部分省略以簡化]
}

注入階段

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties方法中,用于處理bean屬性的后處理,特別是通過@Autowired等注解進行的屬性注入。

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    // 獲取與bean名稱和類相關的InjectionMetadata。
    // 這包括該bean需要進行注入的所有字段和方法。
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    
    try {
        // 使用獲取到的InjectionMetadata,實際進行屬性的注入。
        metadata.inject(bean, beanName, pvs);
    }
    // 如果在注入過程中出現(xiàn)BeanCreationException,直接拋出。
    catch (BeanCreationException ex) {
        throw ex;
    }
    // 捕獲其他異常,并以BeanCreationException的形式拋出,提供詳細的錯誤信息。
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    // 返回原始的PropertyValues,因為這個方法主要關注依賴注入而不是修改屬性。
    return pvs;
}

org.springframework.beans.factory.annotation.InjectionMetadata#inject方法中,主要目的是將所有需要注入的元素(例如帶有@Autowired等注解的字段或方法)注入到目標bean中。

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    // 獲取已經檢查的元素。通常,在初始化階段,所有的元素都會被檢查一次。
    Collection<InjectedElement> checkedElements = this.checkedElements;

    // 如果已經有檢查過的元素,則使用它們,否則使用所有注入的元素。
    Collection<InjectedElement> elementsToIterate =
        (checkedElements != null ? checkedElements : this.injectedElements);

    // 如果有需要注入的元素...
    if (!elementsToIterate.isEmpty()) {
        // 遍歷每個元素并注入到目標bean中。
        for (InjectedElement element : elementsToIterate) {
            // 對每個元素(字段或方法)執(zhí)行注入操作。
            element.inject(target, beanName, pvs);
        }
    }
}

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject方法中,首先檢查字段的值是否已經被緩存。如果已緩存,則從緩存中獲取,否則重新解析。然后,它確保字段是可訪問的(特別是對于私有字段),并將解析的值設置到目標bean的相應字段中。

@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    // 步驟1. 獲取代表帶有@Autowired注解的字段的Field對象。
    Field field = (Field) this.member;

    Object value;
    // 步驟2. 如果字段的值已經被緩存(即先前已解析過),則嘗試從緩存中獲取。
    if (this.cached) {
        try {
            // 從緩存中獲取已解析的字段值。
            value = resolvedCachedArgument(beanName, this.cachedFieldValue);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // 如果緩存中的bean已被意外刪除 -> 重新解析。
            value = resolveFieldValue(field, bean, beanName);
        }
    }
    else {
        // 步驟3. 如果字段值未被緩存,直接解析。
        value = resolveFieldValue(field, bean, beanName);
    }

    // 步驟4. 如果解析到的值不為null...
    if (value != null) {
        // 步驟4.1. 使字段可訪問,這是必要的,特別是當字段是private時。
        ReflectionUtils.makeAccessible(field);
        // 步驟4.2. 實際將解析的值注入到目標bean的字段中。
        field.set(bean, value);
    }
}

首先來到org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject方法中的步驟3。在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue方法中,通過beanFactory.resolveDependency方法從Spring的bean工廠中解析字段的值。

@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
    // ... [代碼部分省略以簡化]
    Object value;
    try {
        // 通過`beanFactory.resolveDependency`方法從Spring的bean工廠中解析字段的值
        value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
    }
    catch (BeansException ex) {
        throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
    }
    // ... [代碼部分省略以簡化]
    return value;
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency方法中,首先嘗試獲取一個延遲解析代理。如果無法獲得,它會進一步嘗試解析依賴。doResolveDependency 是實際進行解析工作的方法。

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,然后選擇一個最佳匹配的 bean。如果存在多個匹配的 bean 或沒有找到匹配的 bean,它會進行相應的處理。

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
            @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

    // ... [代碼部分省略以簡化]

    try {
        // 如果存在快捷解決依賴的方法,使用它
        Object shortcut = descriptor.resolveShortcut(this);
        if (shortcut != null) {
            return shortcut;
        }

        // 獲取依賴的類型
        Class<?> type = descriptor.getDependencyType();
        
        // ... [代碼部分省略以簡化]

        // 步驟1. 根據依賴描述符查找匹配的bean
        Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
        
        // 如果沒有找到匹配的bean
        if (matchingBeans.isEmpty()) {
            if (isRequired(descriptor)) {
                // 如果依賴是必需的,拋出異常
                raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
            }
            return null;
        }

        String autowiredBeanName;
        Object instanceCandidate;

        // 當找到多個匹配的bean
        if (matchingBeans.size() > 1) {
            // 確定最佳的自動裝配候選者
            autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
            if (autowiredBeanName == null) {
                if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
                    // 如果不能確定唯一的bean,嘗試解析不唯一的依賴
                    return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
                }
                else {
                    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);
        }

        // 步驟2. 如果候選者是一個類,實例化它
        if (instanceCandidate instanceof Class) {
            instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
        }
        
        Object result = instanceCandidate;
        
        // ... [代碼部分省略以簡化]
        
        return result;
    }
    // ... [代碼部分省略以簡化]
}

我們來到在org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法中的步驟1。在org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates方法中,首先基于給定的類型獲取所有可能的bean名。接著,對于每一個可能的候選bean,它檢查該bean是否是一個合適的自動注入候選,如果是,它將這個bean添加到結果集中。最后,方法返回找到的所有合適的候選bean。

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

    // 根據所需的類型,包括所有父工廠中的bean,獲取所有可能的bean名
    String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
        this, requiredType, true, descriptor.isEager());

    // ... [代碼部分省略以簡化]

    // 遍歷所有候選bean名
    for (String candidate : candidateNames) {
        // 如果候選bean不是正在查找的bean本身并且它是一個合適的自動注入候選
        if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
            // 添加這個候選bean到結果中
            addCandidateEntry(result, candidate, descriptor, requiredType);
        }
    }

    // ... [代碼部分省略以簡化]

    // 返回找到的所有候選bean
    return result; 
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#addCandidateEntry方法中,主要獲取候選bean的類型,并將其添加到候選bean的集合中。

private void addCandidateEntry(Map<String, Object> candidates, String candidateName,
            DependencyDescriptor descriptor, Class<?> requiredType) {
    // ... [代碼部分省略以簡化]
    candidates.put(candidateName, getType(candidateName));
}

org.springframework.beans.factory.support.AbstractBeanFactory#getType(name)方法中,通過bean的名字來獲取對應bean的類型。

public Class<?> getType(String name) throws NoSuchBeanDefinitionException {
    return getType(name, true);
}

我們來到在org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法中的步驟2。在org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate方法中,最后發(fā)現(xiàn)@Autowired 的整個流程最終還是從Spring容器中獲取一個bean實例并注入到相應的字段或構造函數參數中。

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

    return beanFactory.getBean(beanName);
}

最后我們來到org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject方法中的步驟4.2。在 AutowiredFieldElement#inject 方法內部,通過resolveFieldValue(field, bean, beanName)方法,來確定了正確的bean值并滿足某個字段的 @Autowired 注解,將使用反射來實際設置這個值。具體地說,它會使用 Field 類的 set 方法來為目標對象的這個字段設置相應的值。這就是 @Autowired 在字段上使用時如何使得Spring能夠自動為這個字段注入值的背后原理。

// 步驟4. 如果解析到的值不為null...
if (value != null) {
    // 步驟4.1. 使字段可訪問,這是必要的,特別是當字段是private時。
    ReflectionUtils.makeAccessible(field);
    // 步驟4.2. 實際將解析的值注入到目標bean的字段中。
    field.set(bean, value);
}

八、注意事項

  1. 默認情況下,依賴是必需的
    • 如果Spring找不到匹配的bean來注入,它會拋出一個異常。我們可以通過將 required 屬性設置為 false 來使其變?yōu)榉潜匦瑁?code>@Autowired(required=false)。
  2. 類型匹配
    • 默認情況下,Spring使用類型匹配來解析依賴。如果有多個匹配的bean,它會拋出一個異常。
  3. 使用 @Qualifier
    • 如果有多個相同類型的bean,我們可以使用 @Qualifier 注解來指定bean的名稱,以解決歧義。
  4. 循環(huán)依賴
    • @Autowired 可能導致循環(huán)依賴的問題。例如,A依賴于B,而B依賴于A。Spring有一定的機制來處理單例作用域的bean的循環(huán)依賴,但對于原型作用域的bean,循環(huán)依賴會導致異常。
  5. 注入位置
    • 我們可以在字段、構造函數、或setter方法上使用 @Autowired。但是,推薦的做法是在構造函數上使用它,這樣可以確保所有的依賴在對象創(chuàng)建時都已經注入。
  6. 影響范圍
    • 使用 @Autowired 時,請注意不要在大范圍的bean(例如單例)中注入小范圍的bean(例如原型),除非我們清楚地知道自己在做什么。
  7. 考慮使用構造函數注入
    • 使用構造函數注入可以確保bean在構造時已完全初始化,從而使bean處于不變的狀態(tài)。這也有助于在單元測試中模擬依賴關系。
  8. 私有字段注入
    • 盡管可以將 @Autowired 應用于私有字段,但這意味著Spring通過反射繞過了正常的Java訪問控制來注入字段,這可能不是最佳實踐。
  9. 不支持靜態(tài)字段
    • @Autowired 不能用于靜態(tài)字段。這是因為靜態(tài)字段屬于類而不是實例,而Spring是通過實例進行依賴注入的。
  10. 不支持靜態(tài)方法
    • @Autowired 也不能用于靜態(tài)setter方法或其他靜態(tài)方法

九、總結

最佳實踐總結

  1. 上下文初始化
    • 當我們創(chuàng)建 AnnotationConfigApplicationContext 并提供 MyConfiguration 類作為參數時,Spring 開始初始化上下文。這意味著它會加載所有的bean定義并準備創(chuàng)建實例。
  2. 組件掃描
    • MyConfiguration 類中,我們使用了 @ComponentScan 注解指定了掃描的包路徑。這使得Spring掃描指定包和其子包中的所有類,并查找標記為 @Component、@Service、@Repository@Controller 等注解的類。找到后,Spring 會自動將這些類注冊為bean。
  3. 依賴解析
    • MyController 類中,我們在 myService 字段上使用了 @Autowired 注解。這告訴Spring,當創(chuàng)建 MyController bean時,需要找到一個 MyService 類型的bean,并自動注入到該字段中。
  4. 實例化并注入
    • 當我們從上下文中請求 MyController 類型的bean時,Spring會先創(chuàng)建 MyController 的一個實例。但在此之前,它會查看所有帶有 @Autowired 注解的字段,然后為這些字段找到匹配的bean并注入。
    • 在我們的例子中,Spring找到了 MyService 類型的bean并將其注入到了 myService 字段中。
  5. 執(zhí)行業(yè)務邏輯
    • showService 方法被調用時,它簡單地打印了 myService 字段。由于這個字段已經被成功地自動注入,所以我們看到了預期的輸出,證明 @Autowired 功能正常。
  6. 結果
    • 最終輸出顯示了 myService 已經被成功地注入到 MyController 中,并顯示了其實例的內存地址。

源碼分析總結

  1. 核心后處理器

    • AutowiredAnnotationBeanPostProcessor是處理@Autowired等注解的主要后處理器。它實現(xiàn)了兩個關鍵的接口,MergedBeanDefinitionPostProcessorInstantiationAwareBeanPostProcessor,這兩個接口允許在bean的生命周期中的關鍵階段進行干預,為屬性注入提供了機制。
  2. 收集階段

    • 檢索Autowired的元數據

      • Spring首先使用postProcessMergedBeanDefinition方法確保給定的bean定義與其預期的自動裝配元數據一致。

      • 在該方法中, Spring會嘗試查找與給定bean名稱和類型相關的InjectionMetadata。這可能包括了該bean的字段和方法的注入信息。

    • 尋找匹配的Autowiring元數據

      • findAutowiringMetadata中,Spring確保始終為給定的bean名稱和類獲取最新和相關的InjectionMetadata。Spring也利用了緩存機制,以提高性能。
    • 構建Autowiring元數據

      • buildAutowiringMetadata方法中,Spring會查找類及其所有父類中的字段和方法,以找出所有帶有自動裝配注解的字段和方法。

      • 然后,為這些字段和方法創(chuàng)建一個統(tǒng)一的InjectionMetadata對象。

    • 檢查注解類型

      • AutowiredAnnotationBeanPostProcessor的構造方法中,主要的目的是告訴這個后處理器它應該處理哪些注解。例如, @Autowired就是這些注解之一。
  3. 注入階段

    • 處理bean屬性的后處理

      • postProcessProperties中,Spring用于處理bean屬性的后處理,特別是通過@Autowired進行的屬性注入。

      • 這涉及到實際將解析得到的值注入到bean中。

    • 注入元數據的實際注入操作

      • InjectionMetadata#inject方法中,這里會對bean進行屬性的實際注入。

      • Spring會遍歷每一個需要注入的元素,并執(zhí)行實際的注入操作。

    • 字段的實際注入

      • AutowiredFieldElement#inject中,Spring首先會檢查字段的值是否已經被緩存。如果已緩存,則從緩存中獲取,否則重新解析。

      • 然后,它確保字段是可訪問的,并將解析的值設置到目標bean的相應字段中。

    • 解析依賴

      • doResolveDependency方法中,Spring開始嘗試解析一個特定的依賴。

      • 首先,基于給定的類型,Spring會查找所有匹配的bean。

      • 如果找到多個匹配的bean,它會嘗試確定哪一個是最佳的自動裝配候選。

    • 獲取bean的類型

      • addCandidateEntry方法中,Spring主要獲取候選bean的類型,并將其添加到候選bean的集合中。

      • 使用getType方法,Spring可以通過bean的名字來獲取對應bean的類型。

    • 從Spring容器中獲取bean實例

      • resolveCandidate中,即從Spring容器中獲取一個bean實例并注入到相應的字段或構造函數參數中。
    • 反射注入

      • 通過field.set(bean, value)來完成實際字段注入的步驟,將解析出的bean實例(value)注入到目標bean的對應字段上。這是整個@Autowired流程的最終步驟
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容