1. 概述
@Resource、@Inject和@Autowired是在Java开发中都会经常用到的注解,这些注解为类提供了一种声明性的方式来解决依赖关系
| 12
 
 | @Autowired AnotherClass object;
 
 | 
与之对应的命令式实例化:
| 1
 | AnotherClass object = new AnotherClass();
 | 
其中两个来自java扩展包:javax.annotation.Resource和javax.inject.Inject。@Autowired注解属于org.springframework.beans.factory.annotation包。
这些注解都可以通过字段注入或者setter注入来解决依赖关系,下面会分别比较一下它们之间的区别以及执行路径。
2. @Resource
@Resource注释是JSR-250注释集合的一部分,并与 Jakarta EE 一起打包。此注解具有以下执行路径,按优先级列出:
- 按名称匹配
- 按类型匹配
- 按限定符匹配
这些执行路径适用于 setter 和 field 注入。
2.1 字段注入
我们可以通过使用@Resource注释来注释实例变量来通过字段注入来解决依赖关系。
2.1.1 按名称匹配
我们将使用以下测试来演示按名称匹配字段注入:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | @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());
 }
 }
 
 | 
看一下代码。在FieldResourceInjectionTest测试中,在第 7 行,我们将 bean 名称作为属性值传递给*@Resource*注释来按名称解析依项:
| 12
 
 | @Resource(name="namedFile")private File defaultFile;
 
 | 
此配置将使用按名称匹配执行路径解析依赖关系。我们必须在ApplicationContextTestResourceNameType应用程序上下文中定义 bean namedFile 。
注意 bean id 和对应的引用属性值必须匹配:
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | @Configurationpublic class ApplicationContextTestResourceNameType {
 
 @Bean(name="namedFile")
 public File namedFile() {
 File namedFile = new File("namedFile.txt");
 return namedFile;
 }
 }
 
 | 
如果我们未能在应用程序上下文中定义 bean,会抛出NoSuchBeanDefinitionException。我们可以通过更改ApplicationContextTestResourceNameType应用程序上下文中传递给@Bean注解的属性值,或者更改FieldResourceInjectionTest测试中传递给*@Resource*注解的属性值来证明这一点。
2.1.2. 按类型匹配
为了演示按类型匹配的执行路径,我们只删除FieldResourceInjectionTest测试第 7 行的属性值:
| 12
 
 | @Resourceprivate File defaultFile;
 
 | 
然后我们再次运行测试。
测试仍然会通过,因为如果*@Resource*注释没有接收到 bean 名称作为属性值,Spring 框架将继续进行下一级优先级,按类型匹配,以尝试解决依赖关系。
2.1.3 按限定符匹配
为了演示 match-by-qualifier 执行路径,修改测试场景,以便在ApplicationContextTestResourceQualifier应用程序上下文中定义两个 bean:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | @Configurationpublic 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;
 }
 }
 
 | 
我们使用QualifierResourceInjectionTest测试来演示逐个匹配的依赖关系解析。在这种情况下,需要将特定的 bean 依赖注入到每个引用的变量中:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | @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());
 }
 }
 
 | 
当我们运行测试时,会抛出NoUniqueBeanDefinitionException 。这会发生,因为应用程序上下文将找到两个类型为File的 bean 定义,并且不知道哪个 bean 应该解决依赖关系。
要解决这个问题,我们需要参考QualifierResourceInjectionTest测试的第 7 行到第 10 行:
| 12
 3
 4
 5
 
 | @Resourceprivate File dependency1;
 
 @Resource
 private File dependency2;
 
 | 
我们必须添加以下代码:
| 12
 3
 
 | @Qualifier("defaultFile")
 @Qualifier("namedFile")
 
 | 
使代码如下所示:
| 12
 3
 4
 5
 6
 7
 
 | @Resource@Qualifier("defaultFile")
 private File dependency1;
 
 @Resource
 @Qualifier("namedFile")
 private File dependency2;
 
 | 
再次运行测试时,通过。
测试表明,即使我们在应用程序上下文中定义了多个 bean,我们也可以使用*@Qualifier*注释通过允许我们将特定的依赖项注入到一个类中来消除任何混淆。
2.2. Setter注入
在字段上注入依赖项时所采用的执行路径也适用于setter注入。
2.2.1 按名称匹配
唯一的区别是MethodResourceInjectionTest有一个 setter 方法:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | @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());
 }
 }
 
 | 
我们通过注解引用变量的相应 setter 方法,通过 setter 注入来解决依赖关系。然后我们将bean依赖的名称作为属性值传递给*@Resource*注解:
| 12
 3
 4
 5
 6
 
 | private File defaultFile;
 @Resource(name="namedFile")
 protected void setDefaultFile(File defaultFile) {
 this.defaultFile = defaultFile;
 }
 
 | 
在本例中,我们将重用namedFile bean 依赖项。bean 名称和相应的属性值必须匹配。
运行测试,通过。
为了让我们验证 match-by-name 执行路径是否解决了依赖关系,我们需要将传递给*@Resource注释的属性值更改为我们选择的值并再次运行测试。这一次,测试失败并出现NoSuchBeanDefinitionException*。
2.2.2 按类型匹配
为了演示基于 setter、按类型匹配的执行,我们将使用MethodByTypeResourceTest测试:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | @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());
 }
 }
 
 | 
运行这个测试时,通过。
为了让我们验证按类型匹配的执行路径是否解决了File依赖关系,我们需要将defaultFile变量的类类型更改为另一个类类型,如String。然后我们可以再次执行MethodByTypeResourceTest测试,这次会抛出NoSuchBeanDefinitionException 。
该异常验证是否确实使用了按类型匹配来解决文件依赖关系。NoSuchBeanDefinitionException确认引用变量名称不需要与 bean 名称匹配。相反,依赖解析取决于 bean 的类类型与引用变量的类类型匹配。
2.2.3 按限定符匹配
我们使用MethodByQualifierResourceTest测试来演示 match-by-qualifier 执行路径:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 
 | @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;
 }
 }
 
 | 
我们的测试表明,即使我们在应用程序上下文中定义了特定类型的多个 bean 实现,我们也可以使用*@Qualifier注释和@Resource*注释来解决依赖关系。
类似于基于字段的依赖注入,如果我们在一个应用上下文中定义多个bean,我们必须使用 @Qualifier 注解来指定使用哪个bean来解析依赖,否则会抛出NoUniqueBeanDefinitionException 。
3. @Inject
@Inject注释属于JSR-330注释集合。此注解具有以下执行路径,按优先级列出:
- 按类型匹配
- 按限定符匹配
- 按名称匹配
这些执行路径适用于 setter 和 field 注入。为了让我们访问*@Inject注解,我们必须将javax.inject*库声明为依赖项。
| 12
 3
 4
 5
 
 | <dependency><groupId>javax.inject</groupId>
 <artifactId>javax.inject</artifactId>
 <version>1</version>
 </dependency>
 
 | 
3.1 字段注入
3.1.1 按类型匹配
我们将修改测试示例以使用另一种类型的依赖项,即ArbitraryDependency类。ArbitraryDependency类依赖仅作为一个简单的依赖,并没有其他意义:
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | @Componentpublic class ArbitraryDependency {
 
 private final String label = "Arbitrary Dependency";
 
 public String toString() {
 return label;
 }
 }
 
 | 
这是有错误的FieldInjectTest测试:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | @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());
 }
 }
 
 | 
与*@Resource注解首先按名称解析依赖关系不同,@* Inject注解的默认行为是按类型解析依赖关系。
这意味着即使类引用变量名称与 bean 名称不同,依赖关系仍然会被解析,前提是 bean 是在应用程序上下文中定义的。请注意以下测试中引用变量名称的方式:
| 12
 
 | @Injectprivate ArbitraryDependency fieldInjectDependency;
 
 | 
与应用程序上下文中配置的 bean 名称不同:
| 12
 3
 4
 5
 
 | @Beanpublic ArbitraryDependency injectDependency() {
 ArbitraryDependency injectDependency = new ArbitraryDependency();
 return injectDependency;
 }
 
 | 
当我们执行测试时,我们能够解决依赖关系。
3.1.2 按限定符匹配
如果一个特定的类类型有多个实现,并且某个类需要一个特定的 bean,该怎么办?让我们修改测试示例,使其需要另一个依赖项。
在此示例中,我们将ArbitraryDependency类(在按类型匹配示例中使用)进行子类化,以创建AnotherArbitraryDependency类:
| 12
 3
 4
 5
 6
 7
 8
 
 | public class AnotherArbitraryDependency extends ArbitraryDependency {
 private final String label = "Another Arbitrary Dependency";
 
 public String toString() {
 return label;
 }
 }
 
 | 
每个测试用例的目标是确保我们将每个依赖项正确地注入每个引用变量中:
| 12
 3
 4
 5
 
 | @Injectprivate ArbitraryDependency defaultDependency;
 
 @Inject
 private ArbitraryDependency namedDependency;
 
 | 
我们可以使用FieldQualifierInjectTest测试来演示限定符匹配:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | @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());
 }
 }
 
 | 
如果我们在应用程序上下文中有特定类的多个实现,并且FieldQualifierInjectTest测试尝试以下面列出的方式注入依赖项,则会抛出NoUniqueBeanDefinitionException
| 12
 3
 4
 5
 
 | @Inject private ArbitraryDependency defaultDependency;
 
 @Inject
 private ArbitraryDependency namedDependency;
 
 | 
抛出这个异常是 Spring 框架指出某个类有多个实现的方式,它对使用哪一个感到困惑。为了阐明混淆,我们可以转到FieldQualifierInjectTest测试的第 7 行和第 10 行:
| 12
 3
 4
 5
 
 | @Injectprivate ArbitraryDependency defaultDependency;
 
 @Inject
 private ArbitraryDependency namedDependency;
 
 | 
我们可以将所需的 bean 名称传递给*@Qualifier注释,我们将其与@Inject*注释一起使用。这就是代码块现在的样子:
| 12
 3
 4
 5
 6
 7
 
 | @Inject@Qualifier("defaultFile")
 private ArbitraryDependency defaultDependency;
 
 @Inject
 @Qualifier("namedFile")
 private ArbitraryDependency namedDependency;
 
 | 
@Qualifier注解在接收 bean 名称时要求严格匹配。我们必须确保将 bean 名称正确传递给Qualifier,否则将抛出*NoUniqueBeanDefinitionException 。*如果我们再次运行测试,它应该会通过。
3.1.3 按名称匹配
用于演示按名称匹配的FieldByNameInjectTest测试类似于按类型匹配执行路径。唯一的区别是现在我们需要一个特定的 bean,而不是一个特定的类型。在此示例中,我们再次对ArbitraryDependency类进行子类化以生成YetAnotherArbitraryDependency类:
| 12
 3
 4
 5
 6
 7
 8
 
 | public class YetAnotherArbitraryDependency extends ArbitraryDependency {
 private final String label = "Yet Another Arbitrary Dependency";
 
 public String toString() {
 return label;
 }
 }
 
 | 
为了演示按名称匹配的执行路径,我们将使用以下测试:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | @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());
 }
 }
 
 | 
我们列出应用程序上下文:
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | @Configurationpublic class ApplicationContextTestInjectName {
 
 @Bean
 public ArbitraryDependency yetAnotherFieldInjectDependency() {
 ArbitraryDependency yetAnotherFieldInjectDependency = new YetAnotherArbitraryDependency();
 return yetAnotherFieldInjectDependency;
 }
 }
 
 | 
如果我们运行测试,它将通过。
为了验证我们是否通过按名称匹配执行路径注入了依赖项,我们需要将传入*@Named注解的值yetAnotherFieldInjectDependency更改为我们选择的另一个名称。当我们再次运行测试时,会抛出NoSuchBeanDefinitionException* 。
3.2 Setter注入
@Inject注解的基于设置器的注入类似于用于基于*@Resource*设置器的注入的方法。不是把注解在变量上面,而是注解在相应的 setter 方法。基于 setter 的注入所遵循的执行路径和基于字段的依赖注入也相同。
4. @Autowired
@Autowired注解的行为类似于*@Inject注解。唯一的区别是@Autowired*注解是 Spring 框架的一部分。此注解与@Inject注解具有相同的执行路径,按优先顺序列出:
- 按类型匹配
- 按限定符匹配
- 按名称匹配
这些执行路径适用于 setter 和 field 注入。
4.1 字段注入
4.1.1 按类型匹配
用于演示*@Autowired按类型匹配执行路径的测试示例将类似于用于演示@Inject按类型匹配执行路径的测试。我们使用以下FieldAutowiredTest测试来演示使用@Autowired*注释的按类型匹配:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | @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());
 }
 }
 
 | 
此测试的应用程序上下文:
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | @Configurationpublic class ApplicationContextTestAutowiredType {
 
 @Bean
 public ArbitraryDependency autowiredFieldDependency() {
 ArbitraryDependency autowiredFieldDependency = new ArbitraryDependency();
 return autowiredFieldDependency;
 }
 }
 
 | 
我们使用此测试来证明按类型匹配优先于其他执行路径。注意FieldAutowiredTest测试第 8 行的引用变量名称:
| 12
 
 | @Autowiredprivate ArbitraryDependency fieldDependency;
 
 | 
这与应用程序上下文中的 bean 名称不同:
| 12
 3
 4
 5
 6
 
 | @Beanpublic ArbitraryDependency autowiredFieldDependency() {
 ArbitraryDependency autowiredFieldDependency =
 new ArbitraryDependency();
 return autowiredFieldDependency;
 }
 
 | 
当我们运行测试时,它应该会通过。
为了确认依赖确实是使用 match-by-type 执行路径解决的,我们需要更改fieldDependency引用变量的类型并再次运行测试。这一次,FieldAutowiredTest测试将失败,并引发NoSuchBeanDefinitionException。这验证了我们使用了按类型匹配来解决依赖关系。
4.1.2 按限定符匹配
如果我们遇到在应用程序上下文中定义了多个 bean 实现的情况怎么办:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | @Configurationpublic class ApplicationContextTestAutowiredQualifier {
 
 @Bean
 public ArbitraryDependency autowiredFieldDependency() {
 ArbitraryDependency autowiredFieldDependency =
 new ArbitraryDependency();
 return autowiredFieldDependency;
 }
 
 @Bean
 public ArbitraryDependency anotherAutowiredFieldDependency() {
 ArbitraryDependency anotherAutowiredFieldDependency =
 new AnotherArbitraryDependency();
 return anotherAutowiredFieldDependency;
 }
 }
 
 | 
如果我们执行以下FieldQualifierAutowiredTest测试,则会抛出NoUniqueBeanDefinitionException :
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | @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());
 }
 }
 
 | 
异常是由于应用程序上下文中定义的两个 bean 引起的歧义。Spring 框架不知道哪个 bean 依赖项应该自动装配到哪个引用变量。我们可以通过在FieldQualifierAutowiredTest测试的第 7 行和第 10 行添加*@Qualifier*注释来解决此问题:
| 12
 3
 4
 5
 
 | @Autowiredprivate FieldDependency fieldDependency1;
 
 @Autowired
 private FieldDependency fieldDependency2;
 
 | 
使代码块如下所示:
| 12
 3
 4
 5
 6
 7
 
 | @Autowired@Qualifier("autowiredFieldDependency")
 private FieldDependency fieldDependency1;
 
 @Autowired
 @Qualifier("anotherAutowiredFieldDependency")
 private FieldDependency fieldDependency2;
 
 | 
当我们再次运行测试时,它将会通过。
4.1.3 按名称匹配
我们使用相同的测试场景来演示使用*@Autowired*注释注入字段依赖项的按名称匹配执行路径。
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | @Component("autowiredFieldDependency")public class ArbitraryDependency {
 
 private final String label = "Arbitrary Dependency";
 
 public String toString() {
 return label;
 }
 }
 
 | 
传递到*@Component注释的属性值autowiredFieldDependency告诉 Spring框架ArbitraryDependency类是一个名为autowiredFieldDependency的组件。为了让@Autowired注解通过名称解析依赖,组件名称必须与FieldAutowiredNameTest*测试中定义的字段名称相对应;请参考第7行:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | @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());
 }
 }
 
 | 
当我们运行FieldAutowiredNameTest测试时,它将会通过。
但是我们怎么知道*@Autowired注解确实调用了按名称匹配的执行路径呢?我们可以将引用变量autowiredFieldDependency*的名称更改为我们选择的另一个名称,然后再次运行测试。
这一次,测试将失败并抛出NoUniqueBeanDefinitionException。
类似的检查是将*@Component属性值autowiredFieldDependency更改为我们选择的另一个值并再次运行测试。NoUniqueBeanDefinitionException也会*被抛出。
这个异常证明如果我们使用不正确的 bean 名称,将找不到有效的 bean。这就是我们知道调用了按名称匹配执行路径的方式。
4.2 Setter注入
@Autowired注解的基于设置器的注入类似于为基于*@Resource*的setter注入的方法。所遵循的执行路径也和基于字段的依赖注入相同。
5. 怎么使用
下面提出了应该使用哪种注解以及在什么情况下使用的问题。这些问题的答案取决于应用面临的设计场景,以及开发人员希望如何利用基于每个注释的默认执行路径的多态性。
5.1 通过多态性在应用程序范围内使用单例
如果设计使得应用程序行为基于接口或抽象类的实现,并且这些行为在整个应用程序中使用,那么我们可以使用*@Inject或@Autowired*注解。
这种方法的好处是,当我们升级应用程序或应用补丁来修复错误时,可以更换实现,而对整体应用程序行为的影响最小。在这种情况下,默认执行路径是按类型匹配。
5.2 通过多态进行细粒度的应用程序行为配置
如果设计使得应用程序具有复杂的行为,每个行为都基于不同的接口或者抽象类,并且每个实现的用法在应用程序中有所不同,那么我们可以使用*@Resource*注解。在这种情况下,默认执行路径是按名称匹配。
5.3 依赖注入应该由 Jakarta EE 平台单独处理
如果使用 Jakarta EE 平台而不是 Spring 注入所有依赖项的设计要求,那么选择是在*@Resource注释和@Inject*注释之间进行选择。我们应该根据需要哪个默认执行路径来缩小两个注释之间的选择。
5.4 依赖注入应该由 Spring 框架单独处理
如果要求所有依赖项都由 Spring 框架处理,则唯一的选择是*@Autowired*注释。
5.5 讨论总结
下表总结了我们的讨论
| 设想 | @Resource | @Inject | @Autowired | 
| 通过多态性在应用程序范围内使用单例 | ✗ | ✔ | ✔ | 
| 通过多态进行细粒度的应用程序行为配置 | ✔ | ✗ | ✗ | 
| 依赖注入应该由 Jakarta EE 平台单独处理 | ✔ | ✔ | ✗ | 
| 依赖注入应该由 Spring Framework 单独处理 | ✗ | ✗ | ✔ | 
6 结论
在本文中,我们更深入地了解每个注释的行为。了解每个注释的行为方式将有助于更好的整体应用程序设计和维护。