- Overview
我们将演示如何使用与依赖注入相关的注释,即@Resource、@Inject和@Autowired注释。这些注释为类提供了一种声明性的方式来解析依赖关系:
@Autowired
ArbitraryClass arbObject;
与直接实例化它们(命令式方式)相反:
ArbitraryClass arbObject = new ArbitraryClass();
三个注解中的两个属于 Java 扩展包:javax.annotation.Resource 和 javax.inject.Inject。@Autowired注解属于 org.springframework.beans.factory.annotation 包。
这些注释中的每一个都可以通过字段注入或setter注入来解决依赖关系。我们将使用一个简化但实际的示例来演示三个注释之间的区别,基于每个注释所采用的执行路径。
这些示例将重点介绍如何在集成测试期间使用三个注入注释。测试所需的依赖项可以是任意文档,也可以是任意类。
- The @Resource Annotation
@Resource注释是JSR-250注释集合中的一部分,与 Jakarta EE 打包在一起。
@Resource注释具有以下执行路径,按优先级列出:1.Match by Name
2.Match by Type
3.Match by Qualifier这些执行路径适用于setter 和 field 注入
2.1. Field Injection
我们可以通过字段注入来解决依赖关系,方法是使用 @Resource 注释注释实例变量。
2.1.1. Match by Name
我们将使用以下集成测试来演示按名称匹配字段注入:
@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 注释来按名称解析依赖关系:
@Resource(name="namedFile")
private File defaultFile;
此配置将使用按名称匹配的执行路径解析依赖项。我们必须在 ApplicationContextTestResourceNameType 应用进程上下文中定义 bean namedFile。
请注意,Bean ID 和相应的引用属性值必须匹配:
@Configuration
public class ApplicationContextTestResourceNameType {
@Bean(name="namedFile")
public File namedFile() {
File namedFile = new File("namedFile.txt");
return namedFile;
}
}
如果我们未能在应用进程上下文中定义 bean,则会导致抛出 org.springframework.beans.factory.NoSuchBeanDefinitionException。我们可以通过更改传递到 ApplicationContextTestResourceNameType 应用进程上下文中的@Bean注释的属性值,或者更改传递到 FieldResourceInjectionTest 集成测试中@Resource注释的属性值来证明这一点。
2.1.2. Match by Type
为了演示按类型匹配的执行路径,我们只需删除 FieldResourceInjectionTest 集成测试第 7 行的属性值:
@Resource
private File defaultFile;
然后我们再次运行测试, 测试仍然会通过,因为如果@Resource注释没有收到 Bean 名称作为属性值,Spring
框架将继续下一个优先级,按类型匹配,以尝试解决依赖关系。
2.1.3. Match by Qualifier
为了演示按限定符匹配的执行路径,将修改集成测试方案,以便在 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;
}
}
我们将使用 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());
}
}
当我们运行集成测试时,将抛出一个org.springframework.beans.factory.NoUniqueBeanDefinitionException。发生这种情况是因为应用进程上下文将找到两个 File 类型的 Bean 定义,并且不知道哪个 Bean 应该解决依赖关系。
要解决此问题,我们需要参考 QualifierResourceInjectionTest 集成测试的第 7 行到第 10 行:
@Resource
private File dependency1;
@Resource
private File dependency2;
我们必须添加以下代码行:
@Qualifier("defaultFile")
@Qualifier("namedFile")
因此,代码块如下所示:
@Resource
@Qualifier("defaultFile")
private File dependency1;
@Resource
@Qualifier("namedFile")
private File dependency2;
当我们再次运行集成测试时,它应该通过。我们的测试表明,即使我们在应用进程上下文中定义了多个 bean,我们也可以使用 @Qualifier 注释通过允许我们将特定的依赖项注入类来消除任何混淆。
2.2. Setter Injection
在字段上注入依赖项时采用的执行路径也适用于基于setter的注入。
2.2.1. Match by Name
唯一的区别是 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());
}
}
我们通过对引用变量的相应 setter 方法添加注解,通过注入 setter 来解决依赖关系。然后我们将 bean 依赖项的名称作为属性值传递给@Resource注解:
private File defaultFile;
@Resource(name="namedFile")
protected void setDefaultFile(File defaultFile) {
this.defaultFile = defaultFile;
}
在此示例中,我们将重用 namedFile Bean 依赖项。Bean 名称和相应的属性值必须匹配。
当我们运行集成测试时,它将通过。
为了验证按名称匹配的执行路径是否解决了依赖关系,我们需要将传递给@Resource注释的属性值更改为我们选择的值,然后再次运行测试。这一次,测试将失败,并显示NoSuchBeanDefinitionException。
2.2.2. Match by Type
为了演示基于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());
}
}
当我们运行这个测试时,它会通过。
为了验证按类型匹配的执行路径是否解决了 File 依赖关系,我们需要将 defaultFile 变量的类类型更改为另一个类类型,如 String。然后我们可以再次执行 MethodByTypeResourceTest 集成测试,这次将抛出 NoSuchBeanDefinitionException。
该异常确实验证了使用按类型匹配来解决文档依赖性。 NoSuchBeanDefinitionException 确认引用变量名称不需要与 bean 名称匹配。相反,依赖项解析取决于与引用变量的类类型相匹配的 bean 的类类型。
2.2.3. Match by Qualifier
我们将使用 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;
}
}
我们的测试表明,即使我们在应用进程上下文中定义了特定类型的多个 bean 实现,我们也可以使用 @Qualifier 注释和 @Resource 注释来解决依赖关系。
与基于字段的依赖注入类似,如果我们在应用进程上下文中定义多个 bean,我们必须使用 @Qualifier 注释来指定使用哪个 bean 来解决依赖关系,否则将抛出 NoUniqueBeanDefinitionException。
- The @Inject Annotation
@Inject注释属于 JSR-330 注释集合。此批注具有以下执行路径,按优先级列出:
1.Match by Type
2.Match by Qualifier
3.Match by Name这些执行路径适用于setter和field注入。为了访问@Inject注解,我们必须将javax.inject库声明为Gradle或Maven依赖项。
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
我们将修改集成测试示例以使用另一种类型的依赖项,即 ArbitraryDependency 类。ArbitraryDependency 类依赖项仅用作一个简单的依赖项,没有进一步的意义:
@Component
public class ArbitraryDependency {
private final String label = "Arbitrary Dependency";
public String toString() {
return label;
}
}
以下是有问题的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());
}
}
与首先按名称解析依赖项的@Resource注解不同,@Inject注解的默认行为是按类型解析依赖关系。
这意味着,即使类引用变量名与 Bean 名不同,只要在应用进程上下文中定义了 Bean,仍将解析依赖关系。请注意以下测试中的引用变量名称:
@Inject
private ArbitraryDependency fieldInjectDependency;
与在应用进程上下文中配置的 Bean 名称不同:
@Bean
public ArbitraryDependency injectDependency() {
ArbitraryDependency injectDependency = new ArbitraryDependency();
return injectDependency;
}
当我们执行测试时,我们能够解决依赖关系。
3.1.2. Match by Qualifier
如果特定类类型有多个实现,并且某个类需要特定的 Bean,该怎幺办?让我们修改集成测试示例,使其需要另一个依赖项。
在此示例中,我们对 ArbitraryDependency 类进行继承(在按类型匹配示例中使用)来创建 OtherArbitraryDependency 类:
public class AnotherArbitraryDependency extends ArbitraryDependency {
private final String label = "Another Arbitrary Dependency";
public String toString() {
return label;
}
}
每个测试用例的目标是确保我们将每个依赖项正确注入每个引用变量:
@Inject
private ArbitraryDependency defaultDependency;
@Inject
private ArbitraryDependency namedDependency;
我们可以使用 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());
}
}
如果我们在应用进程上下文中有多个特定类的实现,并且 FieldQualifierInjectTest 集成测试尝试以下面列出的方式注入依赖项,则会抛出 NoUniqueBeanDefinitionException:
@Inject
private ArbitraryDependency defaultDependency;
@Inject
private ArbitraryDependency namedDependency;
抛出这个异常是 Spring 框架指出某个类有多个实现的方式,并且对使用哪个实现感到困惑。为了阐明混淆,我们可以转到 FieldQualifierInjectTest 集成测试的第 7 行和第 10 行:
@Inject
private ArbitraryDependency defaultDependency;
@Inject
private ArbitraryDependency namedDependency;
我们可以将所需的 bean 名称传递给 @Qualifier 注释,并将其与@Inject注释一起使用。代码块现在的样子如下:
@Inject
@Qualifier("defaultFile")
private ArbitraryDependency defaultDependency;
@Inject
@Qualifier("namedFile")
private ArbitraryDependency namedDependency;
@Qualifier 注释在接收 bean 名称时期望严格匹配。我们必须确保将 bean 名称正确传递给 Qualifier,否则将抛出 NoUniqueBeanDefinitionException。如果我们再次运行测试,它应该会通过。
3.1.3. Match by Name
用于演示按名称匹配的 FieldByNameInjectTest 集成测试类似于按类型匹配执行路径。唯一的区别是现在我们需要一个特定的 bean,而不是一个特定的类型。在此示例中,我们再次对 ArbitraryDependency 类进行子类化以生成 YetAnotherArbitraryDependency 类:
public class YetAnotherArbitraryDependency extends ArbitraryDependency {
private final String label = "Yet Another Arbitrary Dependency";
public String toString() {
return label;
}
}
为了演示按名称匹配的执行路径,我们将使用以下集成测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestInjectName.class)
public class FieldByNameInjectIntegrationTest {
@Inject
@Named("yetAnotherFieldInjectDependency")
private ArbitraryDependency yetAnotherFieldInjectDependency;
@Test
public void givenInjectQualifier_WhenSetOnField_ThenDependencyValid(){
assertNotNull(yetAnotherFieldInjectDependency);
assertEquals("Yet Another Arbitrary Dependency",
yetAnotherFieldInjectDependency.toString());
}
}
我们列出应用进程上下文:
@Configuration
public class ApplicationContextTestInjectName {
@Bean
public ArbitraryDependency yetAnotherFieldInjectDependency() {
ArbitraryDependency yetAnotherFieldInjectDependency =
new YetAnotherArbitraryDependency();
return yetAnotherFieldInjectDependency;
}
}
如果我们运行集成测试,它将通过。
为了验证我们是否通过按名称匹配的执行路径注入了依赖关系,我们需要将传递给@Named注释的值 yetAnotherFieldInjectDependency更改为我们选择的另一个名称。当我们再次运行测试时,将抛出一个 NoSuchBeanDefinitionException。
3.2. Setter Injection
@Inject注解基于setter的注入类似于@Resource基于setter的注入。我们没有注释引用变量,而是注释相应的 setter 方法。基于字段的依赖注入所遵循的执行路径也适用于基于setter的注入。
- The @Autowired Annotation
@Autowired注释的行为类似于@Inject注释。唯一的区别是@Autowired注解是 Spring 框架的一部分。此批注与@Inject批注具有相同的执行路径,按优先顺序排列:
1.Match by Type
2.Match by Qualifier
3.Match by Name这些执行路径适用于setter和field注入。
4.1. Field Injection
4.1.1. Match by Type
用于演示@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());
}
}
我们列出了此集成测试的应用进程上下文:
@Configuration
public class ApplicationContextTestAutowiredType {
@Bean
public ArbitraryDependency autowiredFieldDependency() {
ArbitraryDependency autowiredFieldDependency =
new ArbitraryDependency();
return autowiredFieldDependency;
}
}
我们使用此集成测试来证明按类型匹配优先于其他执行路径。请注意 FieldAutowiredTest 集成测试第 8 行的引用变量名称:
@Autowired
private ArbitraryDependency fieldDependency;
这与应用进程上下文中的 bean 名称不同:
@Bean
public ArbitraryDependency autowiredFieldDependency() {
ArbitraryDependency autowiredFieldDependency =
new ArbitraryDependency();
return autowiredFieldDependency;
}
当我们运行测试时,它应该通过。
为了确认使用按类型匹配的执行路径确实解决了依赖关系,我们需要更改 fieldDependency 引用变量的类型并再次运行集成测试。这一次,FieldAutowiredTest集成测试将失败,并抛出NoSuchBeanDefinitionException。这将验证我们是否使用了按类型匹配来解析依赖项。
4.1.2. Match by Qualifier
如果我们面临在应用进程上下文中定义多个 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;
}
}
如果我们执行以下 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());
}
}
异常是由于应用进程上下文中定义的两个 Bean 引起的歧义。Spring 框架不知道哪个 Bean 依赖项应该自动连接到哪个引用变量。我们可以通过将@Qualifier注释添加到 FieldQualifierAutowiredTest 集成测试的第 7 行和第 10 行来解决此问题:
@Autowired
private FieldDependency fieldDependency1;
@Autowired
private FieldDependency fieldDependency2;
这样代码块看起来如下:
@Autowired
@Qualifier("autowiredFieldDependency")
private FieldDependency fieldDependency1;
@Autowired
@Qualifier("anotherAutowiredFieldDependency")
private FieldDependency fieldDependency2;
当我们再次运行测试时,它将通过。
4.1.3. Match by Name
我们将使用相同的集成测试场景来演示使用@Autowired 注释注入字段依赖项的按名称匹配执行路径。当按名称自动装配依赖项时,@ComponentScan 注释必须与应用进程上下文ApplicationContextTestAutowiredName一起使用
@Configuration
@ComponentScan(basePackages={"com.baeldung.dependency"})
public class ApplicationContextTestAutowiredName {
}
我们使用@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;
}
}
传递给@Component注释的属性值autowiredFieldDependency告诉Spring Framework,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());
}
}
当我们运行 FieldAutowiredNameTest 集成测试时,它将通过。
但是,我们如何知道@Autowired注释确实调用了按名称匹配的执行路径呢?我们可以将引用变量 autowiredFieldDependency 的名称更改为我们选择的另一个名称,然后再次运行测试。这一次,测试将失败,并抛出 NoUniqueBeanDefinitionException。类似的检查是将@Component属性值 autowiredFieldDependency更改为我们选择的另一个值,然后再次运行测试。还将抛出 NoUniqueBeanDefinitionException。 此异常证明,如果我们使用不正确的 bean 名称,则找不到有效的 bean。这就是我们知道如何知道调用了按名称匹配的执行路径。
4.2. Setter Injection
@Autowired注解的基于setter注入类似于@Resource注解基于setter注入所演示的方法。我们不是用@Inject注释来注释引用变量,而是注释相应的setter。基于字段的依赖关系注入所遵循的执行路径也适用于基于setter的注入。
- Applying These Annotations
这就提出了应该使用哪种注释以及在什幺情况下使用的问题。这些问题的答案取决于相关应用进程面临的设计方案,以及开发人员希望如何根据每个注释的默认执行路径利用多态性。
5.1. Application-Wide Use of Singletons Through Polymorphism
(通过多态性在应用范围内使用单例)
如果设计使得应用进程行为基于接口或抽象类的实现,并且这些行为在整个应用进程中使用,那么我们可以使用@Inject或@Autowired注释。
这种方法的好处是,当我们升级应用进程或应用补丁以修复错误时,可以换出类,对整个应用进程行为的负面影响最小。在此方案中,主要的默认执行路径是按类型匹配的。
5.2. Fine-Grained Application Behavior Configuration Through Polymorphism
(基于多态性的细粒度应用进程行为配置)
如果设计使得应用进程具有复杂的行为,则每个行为都基于不同的接口/抽象类,并且每个实现的用法在整个应用进程中都不同,那幺我们可以使用@Resource注释。在此方案中,主要的默认执行路径是按名称匹配。
5.3. Dependency Injection Should Be Handled Solely by the Jakarta EE Platform(依赖注入应仅由Jakarta EE平台处理)
如果Jakarta EE平台而不是Spring对所有依赖项都注入了设计要求,那幺选择是在@Resource注释和@Inject注释之间进行选择。我们应该根据需要的默认执行路径来缩小两个注释之间的最终决定范围。
5.4. Dependency Injection Should Be Handled Solely by the Spring Framework(依赖注入应该完全由 Spring 框架处理)
如果强制要求所有依赖项都由 Spring 框架处理,则唯一的选择是@Autowired注释。
5.5. Discussion Summary(讨论总结)
下面的表格总结了我们的讨论。