聊聊依賴注入註解@Resource和@Autowired

碼農小胖哥發表於2020-06-08

1. 前言

@Resource@Autowired註解都可以在Spring Framework應用中進行宣告式的依賴注入。而且面試中經常涉及到這兩個註解的知識點。今天我們來總結一下它們。

2. @Resource

全稱javax.annotation.Resource,它屬於JSR-250規範的一個註解,包含Jakarta EEJ2EE)中。Spring提供了對該註解的支援。我們來詳細瞭解一下該註解的規則。

該註解使用在成員屬性和setter方法上。預設情況下@Resource按照名稱注入,如果沒有顯式宣告名稱則按照變數名稱或者方法中對應的引數名稱進行注入。

Resource註解流程

如果我們希望在目標Bean中體現多型我們可以這樣寫:

/**
 * 多型的體現.
 *
 * @author felord.cn
 * @since 9 :26
 */
@Component
public class ResourceTest {
    @Resource
    private ApplicationRunner applicationRunner;    
    @Resource
    private ApplicationRunner runner;
    // ...
}

Qualifier 約束參見 Spring 註解 @Qualifier 詳細解析

3. @Autowired

@Autowired通常適用於建構函式,成員變數以及方法上。它的機制是這樣的:

Autowired流程

這個註解我們是需要好好聊聊的,日常使用頻率相當高。

3.1 標註在構造上

通過在目標Bean的建構函式上標註就可以注入對應的Bean

package cn.felord;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

/**
 * @author felord.cn
 * @since 9:26
 **/
@Component
public class AutowiredTest {
 private final ApplicationRunner applicationRunner;

    @Autowired
    public AutowiredTest(ApplicationRunner applicationRunner) {
        this.applicationRunner = applicationRunner;
    }
}

Spring Framework 4.3開始,@Autowired如果目標Bean只定義一個建構函式,則不再需要在該建構函式上新增@Autowired註解。如果目標Bean有幾個建構函式可用,並且沒有主/預設建構函式,則必須至少有一個建構函式被@Autowired標記,以指示Spring IoC容器使用哪個建構函式。

3.2 標註在成員變數上

@Resource一樣,@Autowired也可以標註到目標Bean的成員變數上。

/**
 * @author felord.cn
 * @since 9:26
 **/
@Component
public class AutowiredTest {
    @Autowired
    private ApplicationRunner applicationRunner;
    
    // ...
    
}

3.3 標註到方法上

一般setter方法上使用的比較多。而且一個 @Autowired 支援注入多個引數。

/**
 * The type Autowired test.
 *
 * @author felord.cn
 * @since 9 :26
 */
@Component
public class AutowiredTest {

    private ApplicationRunner applicationRunner;
    private EmployeeMapper employeeMapper;
    private DepartmentMapper departmentMapper;

    /**
     * Sets application runner.
     *
     * @param applicationRunner the application runner
     */
    @Autowired
    public void setApplicationRunner(ApplicationRunner applicationRunner) {
        this.applicationRunner = applicationRunner;
    }


    /**
     * 支援多引數
     *
     * @param employeeMapper   the employee mapper
     * @param departmentMapper the department mapper
     */
    @Autowired
    public void prepare(EmployeeMapper employeeMapper, DepartmentMapper departmentMapper) {
        this.employeeMapper = employeeMapper;
        this.departmentMapper = departmentMapper;
    }

}

你以為這就完了?下面這種方式估計大多數人並沒有在意過。

/**
 * The type Autowired test.
 *
 * @author felord.cn
 * @since 9 :26
 */
@Component
public class AutowiredTest {
    // 注入 陣列
    @Autowired
    private MovieCatalog[] movieCatalogs;
    
    private Map<String, Movie> movies;
    
    private Set<CustomerPreferenceDao> customerPreferenceDaos;
    
    // 注入 set
    @Autowired
    public MovieRecommender(Set<CustomerPreferenceDao> customerPreferenceDaos) {
        this.customerPreferenceDaos = customerPreferenceDaos;
    }
            
    // 注入 map 
    @Autowired
    public void setMovieCatalogs(Map<String, Movie> movies) {
        this.movies = movies;
    }
   
    // ...
}

可以把Bean注入目標Bean的陣列、集合容器中去。預設情況下,當給定注入點沒有匹配的候選Bean可用時,自動裝配將失敗。至少應有一個匹配元素。

如果您希望元素按照特定順序排序,則元素Bean可以實現org.springframework.core.Ordered介面或者對應註解@Order或標準@Priority。基於某些機制不建議使用註解方式來排序,否則無法達到預期期望,推薦使用介面Ordered

3.4 裝配可選

@Resource沒有提供可選擇裝配的特性,一旦無法裝配則會丟擲異常;而@Autowired提供了required屬性(預設值為true)以避免這種情況,設定@Autowiredfalse

/**
 * The type Autowired test.
 *
 * @author felord.cn
 * @since 9 :26
 */
@Component
public class AutowiredTest {
    // 一旦找不到 movieFinder  不會異常  而初始化為 null
    @Autowired(required = false)
    private MovieFinder movieFinder;
    // ...
}

這裡也有騷操作,你可以忽略required屬性。通過 Java 8java.util.Optional來表明候選Bean可選。

/**
 * The type Autowired test.
 *
 * @author felord.cn
 * @since 9 :26
 */
@Component
public class AutowiredTest {
public class SimpleMovieLister {
    // 使用 Optional 表明候選Bean可選
    @Autowired
    public void setMovieFinder(Optional<MovieFinder> movieFinder) {
     //   ...
    }
}

Spring 5.0開始,您還可以使用@Nullable註解,這個註解可以你自己實現檢測邏輯或者直接使用 JSR-305提供的javax.annotation.Nullable

/**
 * The type Autowired test.
 *
 * @author felord.cn
 * @since 9 :26
 */
@Component
public class AutowiredTest {
public class SimpleMovieLister {
    // 使用 @Nullable 註解表明候選Bean可選
    @Autowired
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
      //  ...
    }
}

4. @Inject

Spring 3.0開始,Spring提供對JSR-330標準註解(依賴注入)的支援。 你需要引入依賴:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

然後你就可以使用相關的註解來進行依賴注入了,其中主要註解為@javax.inject.Inject。大部分情況下該註解都可以代替@Autowired使用,但@Inject沒有required屬性,不過它也可以與java.util.Optional或使用@Nullable來達到同樣的效果。

大部分情況下沒有人喜歡額外引入Jakarta EE依賴來使用一個已經擁有的功能,Spring堵死了Jakarta EE依賴注入的生態。

5. 總結

@Resource@Autowired的優先順序順序不同(參見上圖),另外@Resource屬於 Jakarta EE規範而@Autowired屬於Spring範疇,@Resource無法使用在構造引數中,@Autowired支援required屬性。從物件導向來說,@Resource更加適用於多型性的細粒度注入,而@Autowired更多專注於多型的單例注入。@Inject 則沒必要過多討論,只作為一個添頭。好了今天就到這裡,多多關注:碼農小胖哥,更多幹貨知識分享。

關注公眾號:Felordcn 獲取更多資訊

個人部落格:https://felord.cn

相關文章