Wiring in Spring: @Autowired, @Resource and @Inject – Spring里的布线 @自动连接、@资源和@注入

最后修改: 2016年 7月 8日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this Spring Framework tutorial, we’ll demonstrate how to use annotations related to dependency injection, namely the @Resource, @Inject, and @Autowired annotations. These annotations provide classes with a declarative way to resolve dependencies:

在这个Spring框架教程中,我们将演示如何使用与依赖注入相关的注解,即@Resource@Inject@Autowired注解。这些注解为类提供了一种解决依赖关系的声明性方法。

@Autowired 
ArbitraryClass arbObject;

As opposed to instantiating them directly (the imperative way):

相对于直接将它们实例化(命令式的方式)。

ArbitraryClass arbObject = new ArbitraryClass();

Two of the three annotations belong to the Java extension package: javax.annotation.Resource and javax.inject.Inject. The @Autowired annotation belongs to the org.springframework.beans.factory.annotation package.

这三个注解中有两个属于Java扩展包。javax.annotation.Resourcejavax.inject.Inject@Autowired注解属于org.springframework.beans.factory.annotation包。

Each of these annotations can resolve dependencies either by field injection or by setter injection. We’ll use a simplified, but practical example to demonstrate the distinction between the three annotations, based on the execution paths taken by each annotation.

这些注解中的每一个都可以通过字段注入或设置器注入来解决依赖关系。我们将使用一个简化但实用的例子,根据每个注解所采取的执行路径,来证明这三种注解之间的区别。

The examples will focus on how to use the three injection annotations during integration testing. The dependency required by the test can either be an arbitrary file or an arbitrary class.

这些例子将集中在如何在集成测试中使用三个注入注解。测试所需的依赖性可以是一个任意的文件或一个任意的类。

2. The @Resource Annotation

2、@Resource Annotation

The @Resource annotation is part of the JSR-250 annotation collection, and is packaged with Jakarta EE. This annotation has the following execution paths, listed by precedence:

@Resource注解是JSR-250注解集合的一部分,并随Jakarta EE打包。该注解有以下执行路径,按优先级排列。

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

These execution paths are applicable to both setter and field injection.

这些执行路径同时适用于setter和field的注入。

2.1. Field Injection

2.1.字段注入

We can resolve dependencies by field injection by annotating an instance variable with the @Resource annotation.

我们可以通过用@Resource注解对实例变量进行注解,通过字段注入来解决依赖关系。

2.1.1. Match by Name

2.1.1.按名称匹配

We’ll use the following integration test to demonstrate match-by-name field injection:

我们将使用下面的集成测试来演示逐名字段注入的匹配。

@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());
    }
}

Let’s go through the code. In the FieldResourceInjectionTest integration test, at line 7, we resolved the dependency by name by passing in the bean name as an attribute value to the @Resource annotation:

让我们浏览一下代码。在FieldResourceInjectionTest集成测试中,在第7行,我们通过将bean名称作为属性值传递给@Resource注解来解决依赖关系。

@Resource(name="namedFile")
private File defaultFile;

This configuration will resolve dependencies using the match-by-name execution path. We must define the bean namedFile in the ApplicationContextTestResourceNameType application context.

此配置将使用逐个名称的匹配执行路径来解决依赖关系。我们必须在ApplicationContextTestResourceNameType应用上下文中定义BeannamedFile

Note that the bean id and the corresponding reference attribute value must match:

请注意,Bean的id和相应的引用属性值必须匹配。

@Configuration
public class ApplicationContextTestResourceNameType {

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

If we fail to define the bean in the application context, it will result in an org.springframework.beans.factory.NoSuchBeanDefinitionException being thrown. We can demonstrate this by changing the attribute value passed into the @Bean annotation in the ApplicationContextTestResourceNameType application context, or changing the attribute value passed into the @Resource annotation in the FieldResourceInjectionTest integration test.

如果我们未能在应用上下文中定义 bean,将导致抛出 org.springframework.beans.factory.NoSuchBeanDefinitionException。我们可以通过改变ApplicationContextTestResourceNameType应用上下文中传递到@Bean注解的属性值,或者改变FieldResourceInjectionTest集成测试中传递到@Resource注解的属性值来证明。

2.1.2. Match by Type

To demonstrate the match-by-type execution path, we just remove the attribute value at line 7 of the FieldResourceInjectionTest integration test:

为了演示逐个类型的执行路径,我们只需在FieldResourceInjectionTest集成测试的第7行删除属性值。

@Resource
private File defaultFile;

Then we run the test again.

然后我们再次运行测试。

The test will still pass because if the @Resource annotation doesn’t receive a bean name as an attribute value, the Spring Framework will proceed with the next level of precedence, match-by-type, in order to try resolve the dependency.

测试仍然会通过,因为如果@Resource注解没有收到作为属性值的Bean名称,Spring框架将进行下一级的优先级,即逐个类型的匹配,以尝试解决依赖关系。

2.1.3. Match by Qualifier

To demonstrate the match-by-qualifier execution path, the integration testing scenario will be modified so that there are two beans defined in the ApplicationContextTestResourceQualifier application context:

为了演示逐个限定符的执行路径,集成测试场景将被修改,以便在ApplicationContextTestResourceQualifier应用上下文中定义有两个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;
    }
}

We’ll use the QualifierResourceInjectionTest integration test to demonstrate match-by-qualifier dependency resolution. In this scenario, a specific bean dependency needs to be injected into each reference variable:

我们将使用QualifierResourceInjectionTest集成测试来演示逐个限定符的依赖性解析。在这种情况下,一个特定的Bean依赖性需要被注入到每个引用变量中。

@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());
    }
}

When we run the integration test, an org.springframework.beans.factory.NoUniqueBeanDefinitionException will be thrown. This will happen because the application context will find two bean definitions of type File, and won’t know which bean should resolve the dependency.

当我们运行集成测试时,将抛出org.springframework.beans.factory.NoUniqueBeanDefinitionException。这是因为应用程序上下文会发现两个File类型的Bean定义,并且不知道哪个Bean应该解决这个依赖关系。

To resolve this issue, we need to refer to line 7 to line 10 of the QualifierResourceInjectionTest integration test:

为了解决这个问题,我们需要参考QualifierResourceInjectionTest集成测试的第7至第10行。

@Resource
private File dependency1;

@Resource
private File dependency2;

We have to add the following lines of code:

我们必须添加以下几行代码。

@Qualifier("defaultFile")

@Qualifier("namedFile")

So that the code block looks as follows:

因此,该代码块看起来如下。

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

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

When we run the integration test again, it should pass. Our test demonstrates that even if we define multiple beans in an application context, we can use the @Qualifier annotation to clear any confusion by allowing us to inject specific dependencies into a class.

当我们再次运行集成测试时,它应该通过。我们的测试表明,即使我们在一个应用上下文中定义了多个Bean,我们也可以使用@Qualifier注解来清除任何混淆,允许我们将特定的依赖注入到一个类中。

2.2. Setter Injection

2.2.设置器注入</b

The execution paths taken when injecting dependencies on a field are applicable to setter-based injection as well.

在字段上注入依赖关系时的执行路径也适用于基于setter的注入。

2.2.1. Match by Name

The only difference is the MethodResourceInjectionTest integration test has a setter method:

唯一的区别是MethodResourceInjectionTest集成测试有一个setter方法。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceNameType.class)
public class MethodResourceInjectionIntegrationTest {

    private File defaultFile;

    @Resource(name="namedFile")
    protected void setDefaultFile(File defaultFile) {
        this.defaultFile = defaultFile;
    }

    @Test
    public void givenResourceAnnotation_WhenSetter_ThenDependencyValid(){
        assertNotNull(defaultFile);
        assertEquals("namedFile.txt", defaultFile.getName());
    }
}

We resolve dependencies by setter injection by annotating a reference variable’s corresponding setter method. Then we pass the name of the bean dependency as an attribute value to the @Resource annotation:

我们通过注解引用变量对应的setter方法,通过setter注入来解决依赖关系。然后我们将Bean依赖的名称作为属性值传递给@Resource注解。

private File defaultFile;

@Resource(name="namedFile")
protected void setDefaultFile(File defaultFile) {
    this.defaultFile = defaultFile;
}

We’ll reuse the namedFile bean dependency in this example. The bean name and the corresponding attribute value must match.

在这个例子中,我们将重新使用namedFileBean的依赖关系。Bean的名称和相应的属性值必须匹配。

When we run the integration test, it will pass.

当我们运行集成测试时,它将通过。

In order for us to verify that the match-by-name execution path resolved the dependency, we need to change the attribute value passed to the @Resource annotation to a value of our choice and run the test again. This time, the test will fail with a NoSuchBeanDefinitionException.

为了验证逐名执行路径是否解决了依赖关系,我们需要将传递给@Resource注解的属性值改为我们选择的值,然后再次运行测试。这一次,测试将以NoSuchBeanDefinitionException失败。

2.2.2. Match by Type

To demonstrate setter-based, match-by-type execution, we will use the MethodByTypeResourceTest integration test:

为了演示基于setter的、逐一匹配的执行,我们将使用MethodByTypeResourceTest集成测试。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceNameType.class)
public class MethodByTypeResourceIntegrationTest {

    private File defaultFile;

    @Resource
    protected void setDefaultFile(File defaultFile) {
        this.defaultFile = defaultFile;
    }

    @Test
    public void givenResourceAnnotation_WhenSetter_ThenValidDependency(){
        assertNotNull(defaultFile);
        assertEquals("namedFile.txt", defaultFile.getName());
    }
}

When we run this test, it will pass.

当我们运行这个测试时,它将通过。

In order for us to verify that the match-by-type execution path resolved the File dependency, we need to change the class type of the defaultFile variable to another class type like String. Then we can execute the MethodByTypeResourceTest integration test again, and this time a NoSuchBeanDefinitionException will be thrown.

为了让我们验证逐个类型的执行路径是否解决了File依赖性,我们需要将defaultFile变量的类类型改为另一种类类型,如String。然后我们可以再次执行MethodByTypeResourceTest集成测试,这次将抛出NoSuchBeanDefinitionException

The exception verifies that match-by-type was indeed used to resolve the File dependency. The NoSuchBeanDefinitionException confirms that the reference variable name doesn’t need to match the bean name. Instead, dependency resolution depends on the bean’s class type matching the reference variable’s class type.

该异常验证了确实使用了逐个类型的匹配来解决文件依赖性。NoSuchBeanDefinitionException证实了引用变量的名称不需要与 bean 的名称相匹配。相反,依赖关系的解决取决于 bean 的类类型与引用变量的类类型相匹配。

2.2.3. Match by Qualifier

We will use the MethodByQualifierResourceTest integration test to demonstrate the match-by-qualifier execution path:

我们将使用MethodByQualifierResourceTest集成测试来演示逐个限定词匹配的执行路径。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceQualifier.class)
public class MethodByQualifierResourceIntegrationTest {

    private File arbDependency;
    private File anotherArbDependency;

    @Test
    public void givenResourceQualifier_WhenSetter_ThenValidDependencies(){
      assertNotNull(arbDependency);
        assertEquals("namedFile.txt", arbDependency.getName());
        assertNotNull(anotherArbDependency);
        assertEquals("defaultFile.txt", anotherArbDependency.getName());
    }

    @Resource
    @Qualifier("namedFile")
    public void setArbDependency(File arbDependency) {
        this.arbDependency = arbDependency;
    }

    @Resource
    @Qualifier("defaultFile")
    public void setAnotherArbDependency(File anotherArbDependency) {
        this.anotherArbDependency = anotherArbDependency;
    }
}

Our test demonstrates that even if we define multiple bean implementations of a particular type in an application context, we can use a @Qualifier annotation together with the @Resource annotation to resolve a dependency.

我们的测试表明,即使我们在应用程序上下文中定义了多个特定类型的 bean 实现,我们也可以使用 @Qualifier 注释和 @Resource 注释来解决依赖性。

Similar to field-based dependency injection, if we define multiple beans in an application context, we must use a @Qualifier annotation to specify which bean to use to resolve dependencies, or a NoUniqueBeanDefinitionException will be thrown.

与基于字段的依赖注入类似,如果我们在应用程序上下文中定义了多个 bean,我们必须使用 @Qualifier 注释来指定使用哪个 bean 来解决依赖关系,否则将抛出 NoUniqueBeanDefinitionException

3. The @Inject Annotation

3.@Inject 注释

The @Inject annotation belongs to the JSR-330 annotations collection. This annotation has the following execution paths, listed by precedence:

@Inject注解属于JSR-330注解集合。该注解有以下执行路径,按优先级排列。

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

These execution paths are applicable to both setter and field injection. In order for us to access the @Inject annotation, we have to declare the javax.inject library as a Gradle or Maven dependency.

这些执行路径同时适用于setter和field injection。为了访问@Inject注解,我们必须将javax.injection库声明为Gradle或Maven的依赖项。

For Gradle:

对于Gradle。

testCompile group: 'javax.inject', name: 'javax.inject', version: '1'

For Maven:

对于Maven来说。

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

3.1. Field Injection

3.1.字段注入

3.1.1. Match by Type

We’ll modify the integration test example to use another type of dependency, namely the ArbitraryDependency class. The ArbitraryDependency class dependency merely serves as a simple dependency and holds no further significance:

我们将修改集成测试的例子,使用另一种类型的依赖,即ArbitraryDependency类。ArbitraryDependency类的依赖性只是作为一个简单的依赖关系,没有进一步的意义。

@Component
public class ArbitraryDependency {

    private final String label = "Arbitrary Dependency";

    public String toString() {
        return label;
    }
}

Here’s the FieldInjectTest integration test in question:

下面是有关的FieldInjectTest集成测试。

@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());
    }
}

Unlike the @Resource annotation, which resolves dependencies by name first, the default behavior of the @Inject annotation is to resolve dependencies by type.

@Resource注解不同,@Inject注解的默认行为是按类型解析依赖关系。

This means that even if the class reference variable name differs from the bean name, the dependency will still be resolved, provided that the bean is defined in the application context. Note how the reference variable name in the following test:

这意味着即使类的引用变量名与 bean 名不同,只要 bean 在应用程序上下文中被定义,该依赖关系仍将被解决。请注意下面的测试中的引用变量名是怎样的。

@Inject
private ArbitraryDependency fieldInjectDependency;

differs from the bean name configured in the application context:

与应用环境中配置的Bean名称不同。

@Bean
public ArbitraryDependency injectDependency() {
    ArbitraryDependency injectDependency = new ArbitraryDependency();
    return injectDependency;
}

When we execute the test, we’re able to resolve the dependency.

当我们执行测试时,我们能够解决这个依赖关系。

3.1.2. Match by Qualifier

What if there are multiple implementations of a particular class type, and a certain class requires a specific bean? Let’s modify the integration testing example so that it requires another dependency.

如果某个类的类型有多个实现,而某个类需要一个特定的Bean,怎么办?让我们修改一下集成测试的例子,使其需要另一个依赖关系。

In this example, we subclass the ArbitraryDependency class, used in the match-by-type example, to create the AnotherArbitraryDependency class:

在这个例子中,我们对ArbitraryDependency类进行子类化,该类在逐个类型的例子中使用,以创建AnotherArbitraryDependency类。

public class AnotherArbitraryDependency extends ArbitraryDependency {

    private final String label = "Another Arbitrary Dependency";

    public String toString() {
        return label;
    }
}

The objective of each test case is to ensure that we inject each dependency correctly into each reference variable:

每个测试案例的目标是确保我们将每个依赖关系正确地注入到每个参考变量中。

@Inject
private ArbitraryDependency defaultDependency;

@Inject
private ArbitraryDependency namedDependency;

We can use the FieldQualifierInjectTest integration test to demonstrate match by qualifier:

我们可以使用FieldQualifierInjectTest集成测试来演示按限定词匹配。

@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());
    }
}

If we have multiple implementations of a particular class in an application context, and the FieldQualifierInjectTest integration test attempts to inject the dependencies in the manner listed below, a NoUniqueBeanDefinitionException will be thrown:

如果我们在一个应用上下文中有多个特定类的实现,并且FieldQualifierInjectTest集成测试试图以下面列出的方式注入依赖关系,那么将抛出NoUniqueBeanDefinitionException

@Inject 
private ArbitraryDependency defaultDependency;

@Inject 
private ArbitraryDependency namedDependency;

Throwing this exception is the Spring Framework’s way of pointing out that there are multiple implementations of a certain class and it is confused about which one to use. In order to elucidate the confusion, we can go to line 7 and 10 of the FieldQualifierInjectTest integration test:

抛出这个异常是Spring框架指出某个类有多个实现的方式,它对使用哪一个感到困惑。为了澄清这个困惑,我们可以去看FieldQualifierInjectTest集成测试的第7行和第10行。

@Inject
private ArbitraryDependency defaultDependency;

@Inject
private ArbitraryDependency namedDependency;

We can pass the required bean name to the @Qualifier annotation, which we use together with the @Inject annotation. This is how the code block will now look:

我们可以将所需的bean名称传递给@Qualifier注解,我们将其与@Inject注解一起使用。这就是现在的代码块的样子。

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

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

The @Qualifier annotation expects a strict match when receiving a bean name. We must ensure that the bean name is passed to the Qualifier correctly, otherwise, a NoUniqueBeanDefinitionException will be thrown. If we run the test again, it should pass.

@Qualifier 注解在接收 bean 名称时期望进行严格匹配。我们必须确保Bean名称被正确地传递给Qualifier,否则将抛出NoUniqueBeanDefinitionException。如果我们再次运行该测试,它应该会通过。

3.1.3. Match by Name

The FieldByNameInjectTest integration test used to demonstrate match by name is similar to the match by type execution path. The only difference is now we require a specific bean, as opposed to a specific type. In this example, we subclass the ArbitraryDependency class again to produce the YetAnotherArbitraryDependency class:

用于演示按名称匹配的FieldByNameInjectTest集成测试与按类型匹配的执行路径类似。唯一的区别是现在我们需要一个特定的Bean,而不是一个特定的类型。在这个例子中,我们再次对ArbitraryDependency类进行子类化,产生YetAnotherArbitraryDependency类。

public class YetAnotherArbitraryDependency extends ArbitraryDependency {

    private final String label = "Yet Another Arbitrary Dependency";

    public String toString() {
        return label;
    }
}

In order to demonstrate the match-by-name execution path, we will use the following integration test:

为了演示逐个名字的匹配执行路径,我们将使用下面的集成测试。

@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());
    }
}

We list the application context:

我们列出了应用背景。

@Configuration
public class ApplicationContextTestInjectName {

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

If we run the integration test, it will pass.

如果我们运行集成测试,它将通过。

In order to verify that we injected the dependency by the match-by-name execution path, we need to change the value, yetAnotherFieldInjectDependency, that was passed in to the @Named annotation to another name of our choice. When we run the test again, a NoSuchBeanDefinitionException will be thrown.

为了验证我们是否通过逐个名称的匹配执行路径注入了依赖,我们需要将传递给@Named注解的yetAnotherFieldInjectDependency的值改为我们选择的另一个名称。当我们再次运行测试时,将会抛出一个NoSuchBeanDefinitionException

3.2. Setter Injection

3.2.设置器注入</b

Setter-based injection for the @Inject annotation is similar to the approach used for the @Resource setter-based injection. Instead of annotating the reference variable, we annotate the corresponding setter method. The execution paths followed by field-based dependency injection also apply to setter based injection.

@Inject 注释的基于设置器的注入与@Resource基于设置器的注入所使用的方法类似。我们不注解引用变量,而是注解相应的设置者方法。基于字段的依赖注入所遵循的执行路径也适用于基于setter的注入。

4. The @Autowired Annotation

4.@Autowired注释

The behaviour of the @Autowired annotation is similar to the @Inject annotation. The only difference is that the @Autowired annotation is part of the Spring framework. This annotation has the same execution paths as the @Inject annotation, listed in order of precedence:

@Autowired注解的行为与@Inject注解相似。唯一不同的是,@Autowired 注解是 Spring 框架的一部分。该注解的执行路径与@Inject注解相同,按优先级排列。

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

These execution paths are applicable to both setter and field injection.

这些执行路径同时适用于setter和field的注入。

4.1. Field Injection

4.1.字段注入

4.1.1. Match by Type

The integration testing example used to demonstrate the @Autowired match-by-type execution path will be similar to the test used to demonstrate the @Inject match-by-type execution path. We use the following FieldAutowiredTest integration test to demonstrate match-by-type using the @Autowired annotation:

用于演示 @Autowired 逐个类型匹配执行路径的集成测试示例将与用于演示 @Inject 逐个类型匹配执行路径的测试类似。我们使用下面的FieldAutowiredTest集成测试来演示使用@Autowired注解的逐个类型的匹配。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestAutowiredType.class)
public class FieldAutowiredIntegrationTest {

    @Autowired
    private ArbitraryDependency fieldDependency;

    @Test
    public void givenAutowired_WhenSetOnField_ThenDependencyResolved() {
        assertNotNull(fieldDependency);
        assertEquals("Arbitrary Dependency", fieldDependency.toString());
    }
}

We list the application context for this integration test:

我们列出这个集成测试的应用环境。

@Configuration
public class ApplicationContextTestAutowiredType {

    @Bean
    public ArbitraryDependency autowiredFieldDependency() {
        ArbitraryDependency autowiredFieldDependency =
          new ArbitraryDependency();
        return autowiredFieldDependency;
    }
}

We use this integration test to demonstrate that match-by-type takes first precedence over the other execution paths. Notice the reference variable name on line 8 of the FieldAutowiredTest integration test:

我们用这个集成测试来证明,逐个类型的匹配优先于其他执行路径。注意FieldAutowiredTest集成测试第8行的引用变量名称。

@Autowired
private ArbitraryDependency fieldDependency;

This is different than the bean name in the application context:

这与应用程序上下文中的Bean名称不同。

@Bean
public ArbitraryDependency autowiredFieldDependency() {
    ArbitraryDependency autowiredFieldDependency =
      new ArbitraryDependency();
    return autowiredFieldDependency;
}

When we run the test, it should pass.

当我们运行该测试时,它应该通过。

In order to confirm that the dependency was indeed resolved using the match-by-type execution path, we need to change the type of the fieldDependency reference variable and run the integration test again. This time, the FieldAutowiredTest integration test will fail, with a NoSuchBeanDefinitionException being thrown. This verifies that we used match-by-type to resolve the dependency.

为了确认依赖性确实是通过逐个类型的执行路径解决的,我们需要改变fieldDependency引用变量的类型,并再次运行集成测试。这一次,FieldAutowiredTest 集成测试将失败,抛出NoSuchBeanDefinitionException。这就验证了我们使用了逐个类型的匹配来解决依赖关系。

4.1.2. Match by Qualifier

What if we’re faced with a situation where we’ve defined multiple bean implementations in the application context:

如果我们面临的情况是,我们在应用环境中定义了多个Bean的实现。

@Configuration
public class ApplicationContextTestAutowiredQualifier {

    @Bean
    public ArbitraryDependency autowiredFieldDependency() {
        ArbitraryDependency autowiredFieldDependency =
          new ArbitraryDependency();
        return autowiredFieldDependency;
    }

    @Bean
    public ArbitraryDependency anotherAutowiredFieldDependency() {
        ArbitraryDependency anotherAutowiredFieldDependency =
          new AnotherArbitraryDependency();
        return anotherAutowiredFieldDependency;
    }
}

If we execute the following FieldQualifierAutowiredTest integration test, a NoUniqueBeanDefinitionException will be thrown:

如果我们执行以下FieldQualifierAutowiredTest集成测试,将抛出NoUniqueBeanDefinitionException

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestAutowiredQualifier.class)
public class FieldQualifierAutowiredIntegrationTest {

    @Autowired
    private ArbitraryDependency fieldDependency1;

    @Autowired
    private ArbitraryDependency fieldDependency2;

    @Test
    public void givenAutowiredQualifier_WhenOnField_ThenDep1Valid(){
        assertNotNull(fieldDependency1);
        assertEquals("Arbitrary Dependency", fieldDependency1.toString());
    }

    @Test
    public void givenAutowiredQualifier_WhenOnField_ThenDep2Valid(){
        assertNotNull(fieldDependency2);
        assertEquals("Another Arbitrary Dependency",
          fieldDependency2.toString());
    }
}

The exception is due to the ambiguity caused by the two beans defined in the application context. The Spring Framework doesn’t know which bean dependency should be autowired to which reference variable. We can resolve this issue by adding the @Qualifier annotation to lines 7 and 10 of the FieldQualifierAutowiredTest integration test:

这个异常是由于应用上下文中定义的两个Bean引起的歧义。Spring框架不知道哪个Bean依赖应该被自动连接到哪个引用变量。我们可以通过在FieldQualifierAutowiredTest集成测试的第7和第10行添加@Qualifier注解来解决这个问题。

@Autowired
private FieldDependency fieldDependency1;

@Autowired
private FieldDependency fieldDependency2;

so that the code block looks as follows:

这样,代码块看起来如下。

@Autowired
@Qualifier("autowiredFieldDependency")
private FieldDependency fieldDependency1;

@Autowired
@Qualifier("anotherAutowiredFieldDependency")
private FieldDependency fieldDependency2;

When we run the test again, it will pass.

当我们再次运行该测试时,它将通过。

4.1.3. Match by Name

We’ll use the same integration test scenario to demonstrate the match-by-name execution path using the @Autowired annotation to inject a field dependency. When autowiring dependencies by name, the @ComponentScan annotation must be used with the application context, ApplicationContextTestAutowiredName:

我们将使用相同的集成测试场景来演示使用@Autowired注解来注入字段依赖的逐名匹配执行路径。当按名称自动匹配依赖关系时,@ComponentScan 注解必须与应用程序上下文一起使用,ApplicationContextTestAutowiredName

@Configuration
@ComponentScan(basePackages={"com.baeldung.dependency"})
    public class ApplicationContextTestAutowiredName {
}

We use the @ComponentScan annotation to search packages for Java classes that have been annotated with the @Component annotation. For example, in the application context, the com.baeldung.dependency package will be scanned for classes that have been annotated with the @Component annotation. In this scenario, the Spring Framework must detect the ArbitraryDependency class, which has the @Component annotation:

我们使用@ComponentScan注解来搜索包中已经被注解为@Component的Java类。例如,在应用程序上下文中,com.baeldung.dependency包将被扫描,以查找已被注解为@Component的类。在这种情况下,Spring框架必须检测到ArbitraryDependency类,它具有@Component注解。

@Component(value="autowiredFieldDependency")
public class ArbitraryDependency {

    private final String label = "Arbitrary Dependency";

    public String toString() {
        return label;
    }
}

The attribute value, autowiredFieldDependency, passed into the @Component annotation, tells the Spring Framework that the ArbitraryDependency class is a component named autowiredFieldDependency. In order for the @Autowired annotation to resolve dependencies by name, the component name must correspond with the field name defined in the FieldAutowiredNameTest integration test; please refer to line 8:

传递到@Component注解中的属性值autowiredFieldDependency告诉Spring框架,ArbitraryDependency类是一个名为autowiredFieldDependency的组件。为了让@Autowired注解按名称解析依赖关系,组件名称必须与FieldAutowiredNameTest集成测试中定义的字段名称一致;请参考第8行。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestAutowiredName.class)
public class FieldAutowiredNameIntegrationTest {

    @Autowired
    private ArbitraryDependency autowiredFieldDependency;

    @Test
    public void givenAutowired_WhenSetOnField_ThenDependencyResolved(){
        assertNotNull(autowiredFieldDependency);
        assertEquals("Arbitrary Dependency",
          autowiredFieldDependency.toString());
	}
}

When we run the FieldAutowiredNameTest integration test, it will pass.

当我们运行FieldAutowiredNameTest集成测试时,它将通过。

But how do we know that the @Autowired annotation really did invoke the match-by-name execution path? We can change the name of the reference variable autowiredFieldDependency to another name of our choice, then run the test again.

但是我们怎么知道@Autowired注解真的调用了逐个名字的执行路径?我们可以将引用变量autowiredFieldDependency的名称改为我们选择的另一个名称,然后再次运行测试。

This time, the test will fail and a NoUniqueBeanDefinitionException is thrown. A similar check would be to change the @Component attribute value, autowiredFieldDependency, to another value of our choice and run the test again. A NoUniqueBeanDefinitionException will also be thrown.

这一次,测试将失败,并抛出一个NoUniqueBeanDefinitionException。类似的检查是将@Component属性值autowiredFieldDependency改为我们选择的另一个值并再次运行测试。一个NoUniqueBeanDefinitionException也将被抛出。

This exception is proof that if we use an incorrect bean name, no valid bean will be found. That’s how we know the match-by-name execution path was invoked.

这个异常证明,如果我们使用一个不正确的Bean名称,就不会找到有效的Bean。这就是我们如何知道按名称匹配的执行路径被调用的原因。

4.2. Setter Injection

4.2.设置器注入</b

Setter-based injection for the @Autowired annotation is similar to the approach demonstrated for the @Resource setter-based injection. Instead of annotating the reference variable with the @Inject annotation, we annotate the corresponding setter. The execution paths followed by field-based dependency injection also apply to setter-based injection.

@Autowired注解的基于设置器的注入与@Resource基于设置器的注入所展示的方法类似。我们没有用@Inject注解来注解引用变量,而是注解了相应的设置器。基于字段的依赖注入所遵循的执行路径也适用于基于设置器的注入。

5. Applying These Annotations

5.应用这些注释

This raises the question of which annotation should be used and under what circumstances. The answer to these questions depends on the design scenario faced by the application in question, and how the developer wishes to leverage polymorphism based on the default execution paths of each annotation.

这就提出了在什么情况下应该使用哪个注解的问题。这些问题的答案取决于有关应用程序所面临的设计方案,以及开发者希望如何利用基于每个注释的默认执行路径的多态性。

5.1. Application-Wide Use of Singletons Through Polymorphism

5.1.通过多态性在整个应用中使用单数

If the design is such that application behaviors are based on implementations of an interface or an abstract class, and these behaviors are used throughout the application, then we can use either the @Inject or @Autowired annotation.

如果设计是这样的:应用程序的行为是基于接口或抽象类的实现,并且这些行为被用于整个应用程序,那么我们可以使用@Inject@Autowired注解。

The benefit of this approach is that when we upgrade the application, or apply a patch in order to fix a bug, classes can be swapped out with minimal negative impact to the overall application behavior. In this scenario, the primary default execution path is match-by-type.

这种方法的好处是,当我们升级应用程序,或者为了修复一个错误而应用一个补丁时,类可以被换掉,而对整个应用程序的行为产生最小的负面影响。在这种情况下,主要的默认执行路径是按类型匹配。

5.2. Fine-Grained Application Behavior Configuration Through Polymorphism

5.2.通过多态性进行精细的应用程序行为配置

If the design is such that the application has complex behavior, each behavior is based on different interfaces/abstract classes, and the usage of each of these implementations varies across the application, then we can use the @Resource annotation. In this scenario, the primary default execution path is match-by-name.

如果设计是这样的:应用程序有复杂的行为,每个行为都基于不同的接口/抽象类,而且每个实现的用法在整个应用程序中都不一样,那么我们可以使用@Resource注解。在这种情况下,主要的默认执行路径是逐名匹配。

5.3. Dependency Injection Should Be Handled Solely by the Jakarta EE Platform

5.3.依赖注入应该只由Jakarta EE平台来处理

If there is a design mandate for all dependencies to be injected by the Jakarta EE Platform as opposed to Spring, then the choice is between the @Resource annotation and the @Inject annotation. We should narrow down the final decision between the two annotations based on which default execution path is required.

如果在设计上要求所有的依赖都由Jakarta EE平台注入,而不是由Spring注入,那么就需要在@Resource注解和@Inject注解之间进行选择。我们应该根据需要哪种默认执行路径来缩小这两个注解的最终决定。

5.4. Dependency Injection Should Be Handled Solely by the Spring Framework

5.4.依赖注入应该完全由Spring框架来处理

If the mandate is for all dependencies to be handled by the Spring Framework, the only choice is the @Autowired annotation.

如果任务是由Spring框架处理所有的依赖关系,那么唯一的选择就是@Autowired注解。

5.5. Discussion Summary

5.5.讨论摘要

The table below summarizes our discussion.

下表概述了我们的讨论。

Scenario @Resource @Inject @Autowired
Application-wide use of singletons through polymorphism
Fine-grained application behavior configuration through polymorphism
Dependency injection should be handled solely by the Jakarta EE platform
Dependency injection should be handled solely by the Spring Framework

6. Conclusion

6.结论

In this article, we aimed to provide a deeper insight into the behavior of each annotation. Understanding how each annotation behaves will contribute to better overall application design and maintenance.

在这篇文章中,我们旨在对每个注释的行为提供更深入的了解。了解每个注释的行为方式将有助于更好地进行整体的应用设计和维护。

The code used during the discussion can be found on GitHub.

讨论中使用的代码可以在GitHub上找到。