概述

我在开发中经常使用@Autowired进行注入,但曾多次被要求解释使用“@Resource”、“@Autowired”和“@Inject”注入 Spring bean 之间的区别,后查看了多个文章,总结了如下内容

AnnotationPackageSource
@Resourcejavax.annotationJava
@Injectjavax.injectJava
@Qualifierjavax.injectJava
@Autowired
org.springframework.bean.factorySpring

为了探索每个注释的行为,我启动了Spring Tool Suite并开始调试代码。我在研究中使用了 Spring 3.0.5.RELEASE。以下是我的发现的总结

代码

我想知道“@Resource”、“@Autowired”和“@Inject”是如何解决依赖关系的。我创建了一个名为“Party”的接口并创建了两个实现类。这允许我在不使用具体类型的情况下注入 bean。这提供了我在有多个类型匹配时确定 Spring 如何解析 bean 所需的灵活性

public interface Party {

}

“Person”是一个组件,它实现了“Party”。

@Component
public class Person implements Party {

}

“Organization”是一个组件,它实现了“Party”。

@Component
public class Organization implements Party {

}

我设置了一个 Spring 上下文,它扫描这两个包以查找标有“@Component”的 bean。

<context:component-scan base-package="com.bughz.organization"/>
<context:component-scan base-package="com.bughz.person"/>

测试

测试 1:不明确的 Bean 

在这个测试中,我注入了一个在 Spring 上下文中具有多个实现的“Party”bean。

@Resource
private Party party;
@Autowired
private Party party;
@Inject
private Party party;

在所有三种情况下,都会抛出“NoSuchBeanDefinitionException”。虽然此异常的名称暗示未找到任何 bean,但该消息说明已找到两个 bean。所有这些注释都会导致相同的异常。

org.springframework.beans.factory.NoSuchBeanDefinitionException:
No unique bean of type [com.bughz.Party] is defined:
expected single matching bean but found 2: [organization, person]

测试 2:字段名称 

在这个测试中,我将 Party 字段命名为 person。默认情况下,标有“@Component”的 bean 将与类具有相同的名称。因此,“Person”类的名称是 person。

@Resource
private Party person;
@Autowired
private Party person;
@Inject
private Party person;

'@Resource' 也可以采用可选的 'name' 属性。这相当于上面的“@Resource”代码。在这种情况下,字段变量名称仍为“party”。“@Autowired”或“@Inject”没有等效的语法。相反,您必须使用“@Qualifier”。稍后将介绍此语法。

@Resource(name="person")
private Party party;

所有这四种样式都注入了“Person”bean。

测试 3:字段类型 

在此测试中,我将类型更改为“Person”。

@Resource
private Person party;
@Autowired
private Person party;
@Inject
private Person party;

所有这些注释都注入了“Person”bean。

测试 4:默认名称限定符 

在这个测试中,我使用“@Qualifier”注释来指向“Person”组件的默认名称。

@Resource
@Qualifier("person")
private Party party;
@Autowired
@Qualifier("person")
private Party party;
@Inject
@Qualifier("person")
private Party party;

所有这些注释都注入了“Person”bean。

测试 5:限定名称 

我在“Person”类中添加了一个“@Qualifier”注释

@Component
@Qualifier("personBean")
public class Person implements Party {

}

在这个测试中,我使用“@Qualifier”注释来指向“Person”组件的限定名称。

@Resource
@Qualifier("personBean")
private Party party;
@Autowired
@Qualifier("personBean")
private Party party;
@Inject
@Qualifier("personBean")
private Party party;

所有这些注释都注入了“Person”bean。

测试 6:Bean 列表 在这个测试中,我注入了一个 Bean 列表。

@Resource
private List<Party> parties;
@Autowired
private List<Party> parties;
@Inject
private List<Party> parties;

所有这些注释都将 2 个 bean 注入到列表中。这也可以通过“@Qualifier”来完成。每个标有特定限定符的 bean 都将添加到列表中。

测试 7:冲突消息 

在这个测试中,我添加了一个错误的“@Qualifier”和一个匹配的字段名称。

@Resource
@Qualifier("bad")
private Party person;
@Autowired
@Qualifier("bad")
private Party person;
@Inject
@Qualifier("bad")
private Party person;

在这种情况下,标有“@Resource”的字段使用字段名称并忽略“@Qualifier”。结果,“Person” bean 被注入。

但是,“@Autowired”和“@Inject”字段会引发“NoSuchBeanDefinitionException”错误,因为它找不到与“@Qualifier”匹配的 bean。

org.springframework.beans.factory.NoSuchBeanDefinitionException:
No matching bean of type [com.bughz.Party] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency.
Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true),
@org.springframework.beans.factory.annotation.Qualifier(value=bad)}

结论

除了测试 2 和 7 之外,配置和结果是相同的。当我查看引擎盖时,我确定“@Autowired”和“@Inject”注释的行为相同。这两个注释都使用“AutowiredAnnotationBeanPostProcessor”来注入依赖项。'@Autowired' 和 '@Inject' 可以互换使用来注入 Spring bean。但是,“@Resource”注释使用“CommonAnnotationBeanPostProcessor”来注入依赖项。即使它们使用不同的后处理器类,它们的行为也几乎相同。以下是它们的执行路径的摘要。

@Autowired 和 @Inject

  1. 按类型匹配

  2. 限定词的限制

  3. 按名称匹配

@Resource

  1. 按名称匹配

  2. 按类型匹配

  3. 限定符限制(如果按名称找到匹配则忽略)

虽然可以说“@Resource”在名称上比“@Autowired”和“@Inject”执行得更快,但可以忽略不计。这不是支持一种语法优于其他语法的充分理由。不过,我确实喜欢 '@Resource' 注释,因为它是简洁的符号风格。

@Resource(name="person")

@Autowired
@Qualifier("person")

@Inject
@Qualifier("person")

您可能会争辩说,如果您使用字段名称来标识 bean 名称,它们可以同样简洁。

@Resource
private Party person;
@Autowired
private Party person;
@Inject
private Party person;

确实如此,但是如果你想重构你的代码会发生什么?通过简单地重命名字段名称,您不再指的是同一个 bean。在使用注释连接 bean 时,我推荐以下做法。


Spring 注解风格最佳实践

  1. 显式命名您的组件 [@Component("beanName")]

  2. 将 '@Resource' 与 'name' 属性一起使用 [@Resource(name="beanName")]

  3. 除非您想创建类似 bean 的列表,否则请避免使用“@Qualifier”注释。例如,您可能希望使用特定的“@Qualifier”注释来标记一组规则。这种方法使得将一组规则类注入到可用于处理数据的列表中变得很简单。

  4. 扫描组件的特定包 [context:component-scan base-package="com.bughz.person"]。虽然这将导致更多的组件扫描配置,但它减少了您将不必要的组件添加到 Spring 上下文的机会。

遵循这些指南将增加 Spring 注解配置的可读性和稳定性。