原文链接:Wiring in Spring: @Autowired, @Resource and @Inject

1. Overview 概述

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.annotaion.Resource 和 javax.inject.Inject。而另一个@Autowired注解则是在 Spring 框架包中。

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.

上面的三个注解都可以用到字段或set方法上来实现依赖自动注入,接下来,基于每个注解的执行路径不同,通过一个简单实用的例子来演示它们之间的区别。

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 @Resouece注解

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注解集上的一员,和JakartaEE一起被打包发布。这个注解有三种工作方式,下面是通过优先级进行排序的结果

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

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

这些注解搜索匹配模式,适用于set方法和字段的依赖注入。

2.1. Field Injection @Resource注解的字段依赖注入

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

可以通过字段注入来实现依赖注入,在一个实例变量上使用@Resource注解,来标记这个变量需要关联到一个bean对象即可。

2.1.1. Match by Name 通过名称Name搜索匹配依赖

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

下面示例示范了@Resource的名称匹配模式

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class,classes=ApplicationContextTestResourceNameType.class)
public class FieldResourceInjectionIntegrationTest {@Resource(name="namedFile")private File defaultFile;@Testpublic 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:

浏览代码,在集成测试类 FieldResourceInjectionIntegrationTest 中,第7行,通过使用@Resource注解标明依赖对象的名称,来实现绑定并注入所需的依赖bean对象,代码如下:

@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.

这种配置,会使用名称匹配的模式来搜索关联所需依赖对象,所以,必须定义一个名为nameFile的对象,并放置到Spring的上下文环境中。

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

必须注意的是,bean对象的id和引用属性的值(@Resource中的name)必须匹配

@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对象,就会抛出一个NoSuchBeanDefinitionException异常。可以通过修改属性的名称或者改变@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:

为也演示类型配置模式,只需要去除@Resource注解中的属性即可,示例如下:

@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:

为演示筛选匹配模式,测试用例将被修改,在程序上下文中定义两个同一类型的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 {@Resourceprivate File dependency1;@Resourceprivate File dependency2;@Testpublic void givenResourceAnnotation_WhenField_ThenDependency1Valid(){assertNotNull(dependency1);assertEquals("defaultFile.txt", dependency1.getName());}@Testpublic 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.

这个时候运行,会遇到 NoUniqueBeanDefinitionException 异常,因为在程序上下文环境中出现了两个同类型的bean对象,不知道使用哪一个来注入依赖。

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

为了解决上述问题,需要参考上一个案例的代码

@Resource
private File dependency1;@Resource
private File dependency2;

We have to add the following lines of code:

添加@Qualifier注解并注定需要依赖的对象

@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注解来清除使用哪个对象的疑惑,需要做的就是使用@Qualifier注解来指定需要对象的名称。

2.2. Setter Injection set方法的注入

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

适用于字段依赖注入的工作模式,同样适用于set方法。

2.2.1. Match by Name 通过bean名称匹配

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

与字段进行名称匹配注入的区别就是,需要定义一个set方法,示例如下:

@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;}@Testpublic 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 @Resourceannotation:

通过在引用依赖字段的set方法上加上@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.

这里,使用上一个例子中字义的对象,所以通过注解指定的名称要完全匹配

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.

为了验证set方法上使用@resource注解指定名称的依赖匹配方式,我们将改变属性值,再次运行测试用例,这次,会失败,出现一个 NoSuchBeanDefinitionException 异常。

2.2.2. Match by Type 类型匹配

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

为也演示set方法上类型匹配,创建 MethodByTypeResourceTest 测试类,如下:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class,classes=ApplicationContextTestResourceNameType.class)
public class MethodByTypeResourceIntegrationTest {private File defaultFile;@Resourceprotected void setDefaultFile(File defaultFile) {this.defaultFile = defaultFile;}@Testpublic 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 NoSuchBeanDefinitionExceptionwill be thrown.

为也验证类型匹配的查找依赖模式,我们改变引用变量的类型,如改为String,这时,再执行测试类,就会出现 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 也佐证了引用变量的名称不参与依赖匹配的过程,最终的匹配方法是通过参数的类型来实现的。

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;@Testpublic 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对象,我们也可以与@Resource一直使用@Qualifier来表明依赖哪个对象。

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注解来指定具体使用哪个对象作为依赖进行注入,否则就会出现 NoUniqueBeanDefinitionException 异常。

3. The @Inject Annotation 使用@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.

这些匹配模式适用于字段注入及set方法注入,而为也使用这个注解,我们必须导入这个库。

For Gradle:

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

For Maven:

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

3.1. Field Injection 字段注入

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 类型,它仅作为一个简单的依赖,不会有过多的功能实现。

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

Here's the FieldInjectTest integration test in question:

下面是在字段上使用@Inject注解的示例:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class,classes=ApplicationContextTestInjectType.class)
public class FieldInjectIntegrationTest {@Injectprivate ArbitraryDependency fieldInjectDependency;@Testpublic 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.

如果存在一个类型的多个实现对象会怎么样呢?如果需要指定一个我写的实现需要怎么处理?改动测试用例来强制依赖另一个实现对象。

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 {@Injectprivate ArbitraryDependency defaultDependency;@Injectprivate ArbitraryDependency namedDependency;@Testpublic void givenInjectQualifier_WhenOnField_ThenDefaultFileValid(){assertNotNull(defaultDependency);assertEquals("Arbitrary Dependency",defaultDependency.toString());}@Testpublic 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:

如果在程序上下文中存在一个类的多个实现,且依赖注解如下面进行使用,那么就会出现 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框架用于指出程序上下文中存在多个同一类型的实现类,并且搞不明白应该使用哪一个。为了消除这种疑惑,可以修改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:

加入@Qualifier注解来指定需求依赖的bean名称,下面是修改后的代码

@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名称的正确性,否则就会遇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:

为也演示名称匹配模式,使用下面的测试用例,注意添加了@Named注解

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

We list the application context: 加入对应的bean对象

@Configuration
public class ApplicationContextTestInjectName {@Beanpublic 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.

为了验证名称匹配模式进行依赖注入,需要改变@Name注解中的值为其它的可选项。而修改之后再次运行,就应该出现 NoSuchBeanDefinitionException 异常。

3.2. Setter Injection Set方法的依赖注入

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注解中基于set方法的注入,和使用@Resource注解很像似。相对于字段上使用注解,这次在set方法上加入注解来标记。在字段上进行标记的规则与在set方法上标记是同样的,参考即可。

4. The @Autowired Annotation 使用@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.

这个执行规则优先级在set方法和字段上一致。

4.1. Field Injection 字段上的依赖注入

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 @Autowiredannotation:

用来演示@Autowired注解类型匹配规则的集成测试用例,与用来演示@Inject注解类型匹配规则的集成测试用例类似。这里,我们将使用FieldAutowiredTest测试类来演示@Autowired注解的类型匹配规则,示例如下

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class,classes=ApplicationContextTestAutowiredType.class)
public class FieldAutowiredIntegrationTest {@Autowiredprivate ArbitraryDependency fieldDependency;@Testpublic void givenAutowired_WhenSetOnField_ThenDependencyResolved() {assertNotNull(fieldDependency);assertEquals("Arbitrary Dependency", fieldDependency.toString());}
}

We list the application context for this integration test:

程序上下文中的bean对象:

@Configuration
public class ApplicationContextTestAutowiredType {@Beanpublic 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 的类型,并重新运行测试用例。这次,执行失败,并会抛出 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 {@Beanpublic ArbitraryDependency autowiredFieldDependency() {ArbitraryDependency autowiredFieldDependency =new ArbitraryDependency();return autowiredFieldDependency;}@Beanpublic 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 {@Autowiredprivate ArbitraryDependency fieldDependency1;@Autowiredprivate ArbitraryDependency fieldDependency2;@Testpublic void givenAutowiredQualifier_WhenOnField_ThenDep1Valid(){assertNotNull(fieldDependency1);assertEquals("Arbitrary Dependency", fieldDependency1.toString());}@Testpublic 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对象,且需要的bean对象模糊不清所导致的。Spring框架不知道哪个bean应该被引用,被当做依赖注入。不过,我们可以在代码中加入@Qualifier注解来解决这个问题

@Autowired
private FieldDependency fieldDependency1;@Autowired
private FieldDependency fieldDependency2;

so that the code block looks as follows: 加入@Qualifier后的代码如下

@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:

我们将在同一个测试场景下,来演示字段上的依赖注入,如何使用名称匹配模式来工作的。当使用名称匹配这种模式时,必须使用@ComponentScan注解来扫描bean对象,示例如下:

@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 ArbitraryDependencyclass, which has the @Component annotation:

@ComponentScan注解是用来扫描java包中被@Component注解标记的java类。比如,在上一个代码片段中,com.baeldung.dependency这个包就会扫描,找出所有被@Component注解标记的类,并实例化后放置到程序上下文中。在这个场景中,Spring会检测到ArbitraryDependency这个java类,因为它使用了@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 FieldAutowiredNameTestintegration test; please refer to line 8:

@Component注解中的value属性值,即autowiredFieldDependency,这个是用来告诉Spring,ArbitraryDependency这个类,它的组件名是autowiredFieldDenpency。为了使用@Autowired注解的名称匹配模式,在使用的时候,传递的bean对象名称必须与定义的完全一致。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class,classes=ApplicationContextTestAutowiredName.class)
public class FieldAutowiredNameIntegrationTest {@Autowiredprivate ArbitraryDependency autowiredFieldDependency;@Testpublic void givenAutowiredAnnotation_WhenOnField_ThenDepValid(){assertNotNull(autowiredFieldDependency);assertEquals("Arbitrary Dependency",autowiredFieldDependency.toString());}
}

When we run the FieldAutowiredNameTest integration test, it will pass. 运行,测试通过

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注解是执行了名称匹配模式呢?为也验证这个问题,我们可以改变引用变量的名称,然后再次运行。

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的value属性值,运行时依旧会抛出异常。

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对象(名称匹配),这也就让我们知道,的确使用了名称匹配来搜索bean对象。

4.2. Setter Injection Set方法注入依赖

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 @Injectannotation, we annotate the corresponding setter. The execution paths followed by field-based dependency injection also apply to setter-based injection.

@Autowired注解的set注入与@Resource注解的set注入类似。与字段注入的区别是,不是在字段上使用注解标记,而是在set方法上使用注解标记。而且,@Autowired注解的匹配优先级(类型-@Qualifier-名称)与字段也是一致的。

5. Applying These Annotations 使用这些注解

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 程序总体使用单一的形式(全部使用同一个注解的同一种匹配模式)

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.

这么做的好处就是,当我们升级程序或者修复bug时,对于整个应用程序来说,会最小程序的产生负面影响。在这个场景中,默认的匹配模式是类型匹配。

举例:在程序上下文中的bean对象全采用类型匹配,且全都只有一个实现对象,那么在这种情况下,最简单便捷的就是全部使用优先级是类型匹配的@Autowired注解和@Inject注解。

5.2. (对这里有不同见解的可以留言讨论下哈)Fine-Grained Application Behavior Configuration Through Polymorphism 

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注解,这时,主要采用的是bean名称匹配。

举例:若Service层全部bean对象以固定开头或固定结尾命名,DAO层与Controller层也类似,有固定前缀后缀,那么,在这种情况下,推荐使用匹配优先级最高是bean名称的@Resource注解。

5.3. Dependency Injection Should Be Handled Solely by the Jakarta EE Platform 依赖注入框架被限定为Jakarta时

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进行依赖注入,不依赖于Spring,那么可选的注解就是@Resource和@Inject。之后,再根据使用哪种依赖匹配方式来选择,若类型匹配优先就选@Inject,否则就是@Resource

5.4. Dependency Injection Should Be Handled Solely by the Spring Framework 依赖注入框架被限定为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 总结

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 最后

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.

这里介绍了三种依赖注入的注解,使我们了解的更加深入了,了解每个注解的工作方式可以让我们能进行更好的程序设计与升级维护。

大家点赞关注哈

Wiring in Spring: @Autowired, @Resource and @Inject 三种注解实现依赖注入相关推荐

  1. Wiring in Spring: @Autowired, @Resource and @Inject

    Overview 我们将演示如何使用与依赖注入相关的注释,即@Resource.@Inject和@Autowired注释.这些注释为类提供了一种声明性的方式来解析依赖关系: @Autowired Ar ...

  2. Spring使用注解配置依赖注入

    大部分情况下,使用Spring配置依赖注入时,都是使用注解来进行配置,因为注解比xml要方便和简单.不过类似于数据源对象这种配置信息容易变更的对象除外,这种对象使用xml文件来进行配置会更适合,方便于 ...

  3. Spring IoC和DI XML方式 注解方式 依赖注入 AOP面向切面

    初识Spring框架 时代变迁 原始时代我们用一个jsp搞定一切,但如此开发大型项目时我们遇到了问题,前端美化的代码和后端的代码交织,代码中又有html.js.css样式,又有业务逻辑和数据库访问代码 ...

  4. 【IOC 控制反转】Android 事件依赖注入 ( 事件三要素 | 修饰注解的注解 | 事件依赖注入步骤 )

    文章目录 总结 一.Android 事件设置三要素 二.修饰注解的注解 三.Android 事件依赖注入步骤 总结 Android 依赖注入的核心就是通过反射获取 类 / 方法 / 字段 上的注解 , ...

  5. Spring实战(三)Spring中装配Bean的三种方式---XML、JavaConfig、AutoWire

    创建应用对象之间协作关系的行为称为装配(wiring),这也是依赖注入的本质. Spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系,而开发者需要告诉Spring需要创建哪些 ...

  6. spring boot 开启定时任务的三种方式

    序言 使用SpringBoot创建定时任务非常简单,目前主要有以下三种创建方式: 一.基于注解(@Scheduled) 二.基于接口(SchedulingConfigurer) 前者相信大家都很熟悉, ...

  7. Spring Boot 注册 Servlet 的三种方法,真是太有用了!

    2019独角兽企业重金招聘Python工程师标准>>> 本文栈长教你如何在 Spring Boot 注册 Servlet.Filter.Listener. 你所需具备的基础 什么是 ...

  8. Spring Boot项目(Maven\Gradle)三种启动方式及后台运行详解

    Spring Boot项目三种启动方式及后台运行详解 1 Spring Boot项目三种启动方法 运行Application.java类中的Main方法 项目管理工具启动 Maven项目:mvn sp ...

  9. Spring通过工厂创建对象的三种方法详解(工厂设计模式)

    1.场景描述 在创建对象的过程中,常常创建出的对象并不能直接使用,它可能需要若干步复杂的步骤,处理完成后才能正常使用.比如有一个网络连接的类NetConn,类中有加载配置文件的方法load(),测试网 ...

最新文章

  1. 链表是否带环、环入口、环长度、链表相交问题分析与总结
  2. 理解 iOS 和 macOS 的内存管理
  3. Flutter - International 国际化,Localization 本地化, 使用Intl
  4. 大会直击|微软亚洲研究院刘铁岩:深度学习成功的秘密
  5. C语言内存管理-字符数组与字符指针
  6. asp网页提升速度方法5则_asp技巧
  7. 怎么画韦布尔分布_手机按键寿命测试的样本数量怎么定?一文看懂 简述威布尔分布及其应用...
  8. 新入职了一个卷王 , 天天加班12点!张口闭口就手写spring , 太让人崩溃了......
  9. shell编程之进阶篇四简单流程控制
  10. 关于树的递归问题的一些总结
  11. .net知识和学习方法系列(二十五) .net中的windows service与服务操作
  12. IMDB评分排名算法
  13. 《天长地久》写照真实生活为何被影迷埋没不被欣赏
  14. python 封闭图形面积_Python求阴影部分面积
  15. 计算机专业的高级称呼,软考高级和中级全称~~
  16. 关于“打开数据库时出错: 到主机 的 TCP/IP 连接失败。”的解决方法
  17. python快速入门编程第5章编程题
  18. 【学渣无能狂怒】今天也是为了学习落泪的一天--每天一遍Java再见
  19. [经验分享]TL431的应用及其封装乱象
  20. 电商平台-商品价格的设计与架构

热门文章

  1. 几种靠谱的离职原因回答策略,了解一下?
  2. sharding-jdbc 分库分表的 4种分片策略,还蛮简单的
  3. DocuWare 文件管理与工作流程自动化案例研究——DocuWare 工作流程功能使在家工作的员工能够保持沟通和高效工作,支持混合环境
  4. 规划设计系列1 | 现场踏勘,1架无人机能顶5个人
  5. SAP MM 无料号采购申请单中'评估价格'之填写
  6. android自动获取位置,Android中获取当前位置信息
  7. C语言关于建筑物高度范围问题
  8. C语言实现 开灯问题
  9. 小程序实践(五)--查看全文
  10. Proe Creo 二次开发之动态干涉检查 调研