一文讓您搞清楚@Resources, @Inject和@Autowired的區別

@TOCjava

本文簡述這三個Spring應用裏經常使用的註解區別。spring

@Resources

官方文檔裏對@Resources的說明:app

The @Resource annotation is part of the JSR-250 annotation collection and is packaged with Jakarta EE.

什麼是JSR-250呢?訪問這個連接:https://jcp.org/en/jsr/detail...框架

裏面有不少PDF能夠下載:測試

打開一看,其實就是Java支持的註解的文檔。這些文檔是最權威的:spa

文檔裏介紹,@Resources對Bean的注入按照以下的優先級進行:3d

  1. Match by Name
  2. Match by Type
  3. Match by Qualifier

Match by Name

咱們來看看Match by name的例子。下面的代碼試圖經過Match by Name注入一個名稱爲namedFile的Bean:code

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceNameType.class)
public class FieldResourceInjectionIntegrationTest {
 
    @Resource(name="namedFile")
    private File defaultFile;
 
    @Test
    public void givenResourceAnnotation_WhenOnField_ThenDependencyValid(){
        assertNotNull(defaultFile);
        assertEquals("namedFile.txt", defaultFile.getName());
    }
}

Bean的定義在以下代碼裏:blog

@Configuration
public class ApplicationContextTestResourceNameType {
 
    @Bean(name="namedFile")
    public File namedFile() {
        File namedFile = new File("namedFile.txt");
        return namedFile;
    }
}

運行時,申明Bean依賴處的@Resource的Name屬性和Bean定義處@Bean的Name屬性值一致,Match by Name測試經過。rem

Match by Type

將使用bean的消費者代碼裏@Resource註解的name屬性去掉,使其變成下面這樣:

@Resource
private File defaultFile;

測試仍然經過,是由於Match by Name的探測機制執行失敗後,進行下一輪Match by Type的探測,這一輪成功了。

Match by Qualifier

定義兩個Bean:

@Configuration
public class ApplicationContextTestResourceQualifier {
 
    @Bean(name="defaultFile")
    public File defaultFile() {
        File defaultFile = new File("defaultFile.txt");
        return defaultFile;
    }
 
    @Bean(name="namedFile")
    public File namedFile() {
        File namedFile = new File("namedFile.txt");
        return namedFile;
    }
}

測試代碼:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceQualifier.class)
public class QualifierResourceInjectionIntegrationTest {
 
    @Resource
    private File dependency1;
     
    @Resource
    private File dependency2;
 
    @Test
    public void givenResourceAnnotation_WhenField_ThenDependency1Valid(){
        assertNotNull(dependency1);
        assertEquals("defaultFile.txt", dependency1.getName());
    }
 
    @Test
    public void givenResourceQualifier_WhenField_ThenDependency2Valid(){
        assertNotNull(dependency2);
        assertEquals("namedFile.txt", dependency2.getName());
    }
}

這一次執行失敗,遇到異常org.springframework.beans.factory.NoUniqueBeanDefinitionException.

緣由是由於咱們的測試代碼裏,沒有指定注入Bean的名稱,所以Spring的Match by Name探測失敗,進行Match by Type時,探測到兩個類型同樣的Bean,Spring框架不知道注入哪個,因此就報異常了。

避免這個異常也很容易,使用@Qualifier.代碼以下:

@Resource
@Qualifier("defaultFile")
private File dependency1;
 
@Resource
@Qualifier("namedFile")
private File dependency2;

@Inject

這個註解定義在JSR-330裏,文檔連接:

https://jcp.org/en/jsr/detail...

注入的優先級:

  1. Match by Type
  2. Match by Qualifier
  3. Match by Name

Match by Type

注意@Inject注入的最高優先級方式爲Match by Type,而非@Resource的Match by Name.

任意定義一個待注入的Component:

@Component
public class ArbitraryDependency {
 
    private final String label = "Arbitrary Dependency";
 
    public String toString() {
        return label;
    }
}

使用@Inject注入:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestInjectType.class)
public class FieldInjectIntegrationTest {
 
    @Inject
    private ArbitraryDependency fieldInjectDependency;
 
    @Test
    public void givenInjectAnnotation_WhenOnField_ThenValidDependency(){
        assertNotNull(fieldInjectDependency);
        assertEquals("Arbitrary Dependency",
          fieldInjectDependency.toString());
    }
}

Match by Qualifier

定義一個新的待注入組件:

public class AnotherArbitraryDependency extends ArbitraryDependency {
 
    private final String label = "Another Arbitrary Dependency";
 
    public String toString() {
        return label;
    }
}

測試代碼:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestInjectQualifier.class)
public class FieldQualifierInjectIntegrationTest {
 
    @Inject
    private ArbitraryDependency defaultDependency;
 
    @Inject
    private ArbitraryDependency namedDependency;
 
    @Test
    public void givenInjectQualifier_WhenOnField_ThenDefaultFileValid(){
        assertNotNull(defaultDependency);
        assertEquals("Arbitrary Dependency",
          defaultDependency.toString());
    }
 
    @Test
    public void givenInjectQualifier_WhenOnField_ThenNamedFileValid(){
        assertNotNull(defaultDependency);
        assertEquals("Another Arbitrary Dependency",
          namedDependency.toString());
    }
}

和以前@Resource的第一次試圖經過Match by Type注入同樣失敗,遇到異常:NoUniqueBeanDefinitionException

利用@Qualifier避免這個異常:

@Inject
@Qualifier("defaultFile")
private ArbitraryDependency defaultDependency;
 
@Inject
@Qualifier("namedFile")
private ArbitraryDependency namedDependency;

Match by Name

public class YetAnotherArbitraryDependency extends ArbitraryDependency {
 
    private final String label = "Yet Another Arbitrary Dependency";
 
    public String toString() {
        return label;
    }
}

消費者代碼:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestInjectName.class)
public class FieldByNameInjectIntegrationTest {
 
    @Inject
    @Named("yetAnotherFieldInjectDependency")
    private ArbitraryDependency yetAnotherFieldInjectDependency;
 
    @Test
    public void givenInjectQualifier_WhenSetOnField_ThenDependencyValid(){
        assertNotNull(yetAnotherFieldInjectDependency);
        assertEquals("Yet Another Arbitrary Dependency",
          yetAnotherFieldInjectDependency.toString());
    }
}

application context代碼:

@Configuration
public class ApplicationContextTestInjectName {
 
    @Bean
    public ArbitraryDependency yetAnotherFieldInjectDependency() {
        ArbitraryDependency yetAnotherFieldInjectDependency =
          new YetAnotherArbitraryDependency();
        return yetAnotherFieldInjectDependency;
    }
}

測試經過

@Autowired

這個註解和@Inject的用法一致,惟一區別就是@Autowired 屬於Spring框架提供的註解。例子略。

要獲取更多Jerry的原創文章,請關注公衆號"汪子熙":

相關文章
相關標籤/搜索