@Autowired注解源碼分析

@Autowired

一、基本信息

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

二围小、注解描述

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

三框舔、接口源碼

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

/**
 * 通過Spring的依賴注入機制標(biāo)記構(gòu)造函數(shù)刘绣、字段樱溉、setter方法或配置方法。
 * 這是JSR-330 Inject 注解的一種替代额港,增加了必需與可選的語義。
 *
 * 自動注入的構(gòu)造函數(shù):
 * 任何給定的bean類只有一個構(gòu)造函數(shù)可以聲明此注解歧焦,且其required屬性設(shè)置為 true移斩,
 * 表示當(dāng)作為Spring bean使用時要自動注入的構(gòu)造函數(shù)肚医。如果required屬性設(shè)置為true,
 * 則只能有一個構(gòu)造函數(shù)被標(biāo)記為Autowired向瓷。如果多個非必需的構(gòu)造函數(shù)聲明了這個注解肠套,
 * 它們都會被視為自動注入的候選者。Spring會選擇可以滿足最多依賴的構(gòu)造函數(shù)進行注入猖任。如果沒有一個候選者滿足條件你稚,
 * 則會使用默認(rèn)的構(gòu)造函數(shù)。如果一個類聲明了多個構(gòu)造函數(shù)朱躺,但沒有一個使用Autowired刁赖,
 * 則會使用默認(rèn)的構(gòu)造函數(shù)。如果一個類從一開始就只聲明了一個構(gòu)造函數(shù)长搀,它總是會被使用宇弛,即使沒有被標(biāo)記。被標(biāo)記的構(gòu)造函數(shù)不需要是public的源请。
 *
 * 自動注入的字段:
 * 字段會在bean構(gòu)造完成后注入枪芒,任何配置方法調(diào)用之前。配置字段不需要是public的谁尸。
 *
 * 自動注入的方法:
 * 配置方法可以有任意名稱和任意數(shù)量的參數(shù)舅踪;其中每一個參數(shù)都會通過Spring容器中的一個匹配的bean進行自動注入。
 * Bean屬性的setter方法在實質(zhì)上只是這種通用配置方法的一個特例良蛮。這些配置方法不需要是public的抽碌。
 *
 * 自動注入的參數(shù):
 * 盡管從Spring 5.0開始Autowired可以在方法或構(gòu)造函數(shù)的單獨參數(shù)上聲明,
 * 但框架的大部分都會忽略這樣的聲明背镇。唯一支持自動注入?yún)?shù)的Spring核心部分是
 * spring-test模塊中的JUnit Jupiter支持咬展。
 *
 * 多參數(shù)與'required'語義:
 * 對于多參數(shù)的構(gòu)造函數(shù)或方法,required屬性適用于所有參數(shù)瞒斩。
 *
 * 自動裝配數(shù)組破婆、集合和映射:
 * 對于數(shù)組、Collection或Map類型的依賴胸囱,容器會自動注入所有匹配聲明的值類型的bean祷舀。
 * 對于這種目的,map的鍵必須聲明為類型String烹笔,它會解析為相應(yīng)的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 {

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

四冤吨、主要功能

  1. 自動注入依賴
    • 不需要明確指定 bean 之間的關(guān)系蒿柳,Spring 會自動找到并注入所需的依賴。
  2. 字段注入
    • 可以直接標(biāo)記在類的字段上漩蟆,使得該字段在 Bean 初始化時被自動注入垒探。
  3. 構(gòu)造函數(shù)注入
    • 當(dāng)標(biāo)記在構(gòu)造函數(shù)上時,該構(gòu)造函數(shù)會被用于創(chuàng)建 bean 實例并注入所需的依賴怠李。
  4. 方法注入
    • 當(dāng)標(biāo)記在 setter 方法或其他方法上時圾叼,這些方法會在 Bean 初始化時被調(diào)用以注入依賴。
  5. 指定必需性
    • 通過 required 屬性捺癞,可以指定某個依賴是否是必需的夷蚊。如果標(biāo)記為必需但沒有找到相應(yīng)的依賴,Spring 會拋出異常翘簇。

五撬码、最佳實踐

首先來看看啟動類入口,上下文環(huán)境使用AnnotationConfigApplicationContext(此類是使用Java注解來配置Spring容器的方式)版保,構(gòu)造參數(shù)我們給定了一個MyConfiguration組件類呜笑。然后從Spring上下文中獲取一個MyController類型的bean并調(diào)用了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 是一個簡單的服務(wù)類仲智,但我們沒有定義任何方法或功能买乃。

@Service
public class MyService {
    
}

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

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

六前联、時序圖

sequenceDiagram
Title: @Autowired注解時序圖
AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName)<br>應(yīng)用Bean定義的后置處理器
AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor:postProcessMergedBeanDefinition(beanDefinition,beanType,beanName)<br>處理已合并的Bean定義
AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:findAutowiringMetadata(beanName,clazz,pvs)<br>查找自動注入的元數(shù)據(jù)
AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:buildAutowiringMetadata(clazz)<br>構(gòu)建自動注入的元數(shù)據(jù)
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>將元數(shù)據(jù)存入緩存
AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:populateBean(beanName,mbd,bw)<br>填充Bean的屬性值
AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor:postProcessProperties(pvs,bean,beanName)<br>后處理Bean的屬性
AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:findAutowiringMetadata(beanName,clazz,pvs)<br>再次查找自動注入的元數(shù)據(jù)
Note right of AutowiredAnnotationBeanPostProcessor:<br>從緩存中獲取注入的元數(shù)據(jù)
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>解析指定的依賴關(guān)系
DefaultListableBeanFactory->>DefaultListableBeanFactory:findAutowireCandidates(beanName, type, descriptor)<br>查找符合自動裝配條件的候選 Bean
DefaultListableBeanFactory->>DefaultListableBeanFactory:addCandidateEntry(result, candidate, descriptor, requiredType)<br>向結(jié)果集中添加候選 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>實際設(shè)置 Bean 的字段值

七功戚、源碼分析

前置條件

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

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

收集階段

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition方法中鄙煤,主要確保給定的bean定義與其預(yù)期的自動裝配元數(shù)據(jù)一致晾匠。

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

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata方法中,確保了始終為給定的bean名稱和類獲取最新和相關(guān)的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);
    // 檢查獲取到的元數(shù)據(jù)是否需要刷新。
    if (InjectionMetadata.needsRefresh(metadata, clazz)) {
        // 使用雙重檢查鎖定確保線程安全旷太。
        synchronized (this.injectionMetadataCache) {
            metadata = this.injectionMetadataCache.get(cacheKey);
            if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                // 如果有舊的元數(shù)據(jù)展懈,清除它。
                if (metadata != null) {
                    metadata.clear(pvs);
                }
                // 為給定的類構(gòu)建新的InjectionMetadata供璧。
                metadata = buildAutowiringMetadata(clazz);
                // 將新構(gòu)建的元數(shù)據(jù)更新到緩存中存崖。
                this.injectionMetadataCache.put(cacheKey, metadata);
            }
        }
    }
    // 返回找到的或新構(gòu)建的元數(shù)據(jù)。
    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 {
        // 當(dāng)前類中要注入的元素列表葛虐。
        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));
            }
        });

        // 將當(dāng)前類的注入元素加入到總的注入元素列表的開頭万栅。
        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哪些注解它應(yīng)該處理代赁。當(dāng)Spring容器解析bean定義并創(chuàng)建bean實例時扰她,如果這個bean的字段、方法或構(gòu)造函數(shù)上的注解被包含在這個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名稱和類相關(guān)的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,因為這個方法主要關(guān)注依賴注入而不是修改屬性个初。
    return pvs;
}

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

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

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

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

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject方法中金踪,首先檢查字段的值是否已經(jīng)被緩存捺氢。如果已緩存买雾,則從緩存中獲取馒胆,否則重新解析。然后凝果,它確保字段是可訪問的(特別是對于私有字段),并將解析的值設(shè)置到目標(biāo)bean的相應(yīng)字段中睦尽。

@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. 如果字段的值已經(jīng)被緩存(即先前已解析過),則嘗試從緩存中獲取当凡。
    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. 使字段可訪問,這是必要的朴则,特別是當(dāng)字段是private時权纤。
        ReflectionUtils.makeAccessible(field);
        // 步驟4.2. 實際將解析的值注入到目標(biāo)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忘闻,它會進行相應(yīng)的處理挂绰。

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. 根據(jù)依賴描述符查找匹配的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;

        // 當(dāng)找到多個匹配的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添加到結(jié)果集中命爬。最后,方法返回找到的所有合適的候選bean饲宛。

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

    // 根據(jù)所需的類型皆愉,包括所有父工廠中的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到結(jié)果中
            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的名字來獲取對應(yīng)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實例并注入到相應(yīng)的字段或構(gòu)造函數(shù)參數(shù)中冤寿。

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 方法內(nèi)部青伤,通過resolveFieldValue(field, bean, beanName)方法疚沐,來確定了正確的bean值并滿足某個字段的 @Autowired 注解,將使用反射來實際設(shè)置這個值潮模。具體地說亮蛔,它會使用 Field 類的 set 方法來為目標(biāo)對象的這個字段設(shè)置相應(yīng)的值。這就是 @Autowired 在字段上使用時如何使得Spring能夠自動為這個字段注入值的背后原理擎厢。

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

八、注意事項

  1. 默認(rèn)情況下厘惦,依賴是必需的
    • 如果Spring找不到匹配的bean來注入偷仿,它會拋出一個異常哩簿。我們可以通過將 required 屬性設(shè)置為 false 來使其變?yōu)榉潜匦瑁?code>@Autowired(required=false)。
  2. 類型匹配
    • 默認(rèn)情況下酝静,Spring使用類型匹配來解析依賴节榜。如果有多個匹配的bean,它會拋出一個異常别智。
  3. 使用 @Qualifier
    • 如果有多個相同類型的bean宗苍,我們可以使用 @Qualifier 注解來指定bean的名稱,以解決歧義薄榛。
  4. 循環(huán)依賴
    • @Autowired 可能導(dǎo)致循環(huán)依賴的問題讳窟。例如,A依賴于B敞恋,而B依賴于A丽啡。Spring有一定的機制來處理單例作用域的bean的循環(huán)依賴,但對于原型作用域的bean硬猫,循環(huán)依賴會導(dǎo)致異常补箍。
  5. 注入位置
    • 我們可以在字段、構(gòu)造函數(shù)浦徊、或setter方法上使用 @Autowired。但是天梧,推薦的做法是在構(gòu)造函數(shù)上使用它盔性,這樣可以確保所有的依賴在對象創(chuàng)建時都已經(jīng)注入。
  6. 影響范圍
    • 使用 @Autowired 時呢岗,請注意不要在大范圍的bean(例如單例)中注入小范圍的bean(例如原型)冕香,除非我們清楚地知道自己在做什么。
  7. 考慮使用構(gòu)造函數(shù)注入
    • 使用構(gòu)造函數(shù)注入可以確保bean在構(gòu)造時已完全初始化后豫,從而使bean處于不變的狀態(tài)悉尾。這也有助于在單元測試中模擬依賴關(guān)系。
  8. 私有字段注入
    • 盡管可以將 @Autowired 應(yīng)用于私有字段挫酿,但這意味著Spring通過反射繞過了正常的Java訪問控制來注入字段构眯,這可能不是最佳實踐。
  9. 不支持靜態(tài)字段
    • @Autowired 不能用于靜態(tài)字段早龟。這是因為靜態(tài)字段屬于類而不是實例惫霸,而Spring是通過實例進行依賴注入的。
  10. 不支持靜態(tài)方法
    • @Autowired 也不能用于靜態(tài)setter方法或其他靜態(tài)方法

九葱弟、總結(jié)

最佳實踐總結(jié)

  1. 上下文初始化
    • 當(dāng)我們創(chuàng)建 AnnotationConfigApplicationContext 并提供 MyConfiguration 類作為參數(shù)時壹店,Spring 開始初始化上下文。這意味著它會加載所有的bean定義并準(zhǔn)備創(chuàng)建實例芝加。
  2. 組件掃描
    • MyConfiguration 類中硅卢,我們使用了 @ComponentScan 注解指定了掃描的包路徑。這使得Spring掃描指定包和其子包中的所有類,并查找標(biāo)記為 @Component将塑、@Service脉顿、@Repository@Controller 等注解的類。找到后抬旺,Spring 會自動將這些類注冊為bean弊予。
  3. 依賴解析
    • MyController 類中,我們在 myService 字段上使用了 @Autowired 注解开财。這告訴Spring汉柒,當(dāng)創(chuàng)建 MyController bean時,需要找到一個 MyService 類型的bean责鳍,并自動注入到該字段中碾褂。
  4. 實例化并注入
    • 當(dāng)我們從上下文中請求 MyController 類型的bean時,Spring會先創(chuàng)建 MyController 的一個實例历葛。但在此之前正塌,它會查看所有帶有 @Autowired 注解的字段,然后為這些字段找到匹配的bean并注入恤溶。
    • 在我們的例子中乓诽,Spring找到了 MyService 類型的bean并將其注入到了 myService 字段中。
  5. 執(zhí)行業(yè)務(wù)邏輯
    • showService 方法被調(diào)用時咒程,它簡單地打印了 myService 字段鸠天。由于這個字段已經(jīng)被成功地自動注入,所以我們看到了預(yù)期的輸出帐姻,證明 @Autowired 功能正常稠集。
  6. 結(jié)果
    • 最終輸出顯示了 myService 已經(jīng)被成功地注入到 MyController 中,并顯示了其實例的內(nèi)存地址饥瓷。

源碼分析總結(jié)

  1. 核心后處理器

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

    • 檢索Autowired的元數(shù)據(jù)

      • Spring首先使用postProcessMergedBeanDefinition方法確保給定的bean定義與其預(yù)期的自動裝配元數(shù)據(jù)一致棺克。

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

    • 尋找匹配的Autowiring元數(shù)據(jù)

      • findAutowiringMetadata中逆航,Spring確保始終為給定的bean名稱和類獲取最新和相關(guān)的InjectionMetadata鼎文。Spring也利用了緩存機制,以提高性能因俐。
    • 構(gòu)建Autowiring元數(shù)據(jù)

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

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

    • 檢查注解類型

      • AutowiredAnnotationBeanPostProcessor的構(gòu)造方法中,主要的目的是告訴這個后處理器它應(yīng)該處理哪些注解胡嘿。例如, @Autowired就是這些注解之一蛉艾。
  3. 注入階段

    • 處理bean屬性的后處理

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

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

    • 注入元數(shù)據(jù)的實際注入操作

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

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

    • 字段的實際注入

      • AutowiredFieldElement#inject中兵钮,Spring首先會檢查字段的值是否已經(jīng)被緩存。如果已緩存舌界,則從緩存中獲取掘譬,否則重新解析。

      • 然后呻拌,它確保字段是可訪問的葱轩,并將解析的值設(shè)置到目標(biāo)bean的相應(yīng)字段中。

    • 解析依賴

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

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

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

    • 獲取bean的類型

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

      • 使用getType方法,Spring可以通過bean的名字來獲取對應(yīng)bean的類型眷茁。

    • 從Spring容器中獲取bean實例

      • resolveCandidate中炕泳,即從Spring容器中獲取一個bean實例并注入到相應(yīng)的字段或構(gòu)造函數(shù)參數(shù)中。
    • 反射注入

      • 通過field.set(bean, value)來完成實際字段注入的步驟上祈,將解析出的bean實例(value)注入到目標(biāo)bean的對應(yīng)字段上培遵。這是整個@Autowired流程的最終步驟
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末浙芙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子籽腕,更是在濱河造成了極大的恐慌嗡呼,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皇耗,死亡現(xiàn)場離奇詭異南窗,居然都是意外死亡,警方通過查閱死者的電腦和手機郎楼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門万伤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人箭启,你說我怎么就攤上這事壕翩。” “怎么了傅寡?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵放妈,是天一觀的道長。 經(jīng)常有香客問我荐操,道長芜抒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任托启,我火速辦了婚禮宅倒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘屯耸。我一直安慰自己拐迁,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布疗绣。 她就那樣靜靜地躺著线召,像睡著了一般。 火紅的嫁衣襯著肌膚如雪多矮。 梳的紋絲不亂的頭發(fā)上缓淹,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音塔逃,去河邊找鬼讯壶。 笑死,一個胖子當(dāng)著我的面吹牛湾盗,可吹牛的內(nèi)容都是我干的伏蚊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼格粪,長吁一口氣:“原來是場噩夢啊……” “哼躏吊!你這毒婦竟也來了肺孵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤颜阐,失蹤者是張志新(化名)和其女友劉穎平窘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凳怨,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡瑰艘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了肤舞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片紫新。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖李剖,靈堂內(nèi)的尸體忽然破棺而出芒率,到底是詐尸還是另有隱情,我是刑警寧澤篙顺,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布偶芍,位于F島的核電站,受9級特大地震影響德玫,放射性物質(zhì)發(fā)生泄漏匪蟀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一宰僧、第九天 我趴在偏房一處隱蔽的房頂上張望材彪。 院中可真熱鬧,春花似錦琴儿、人聲如沸段化。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽显熏。三九已至,卻和暖如春谜疤,著一層夾襖步出監(jiān)牢的瞬間佃延,已是汗流浹背现诀。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工夷磕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人仔沿。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓坐桩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親封锉。 傳聞我的和親對象是個殘疾皇子绵跷,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容