<译>使用Spring 进行自动装配:@Autowired, @Resource 和 @Inject注解详解

本文翻译自baeldung的文章《Wiring in Spring: @Autowired, @Resource and @Inject》,文中的引用和括号内的斜线内容为译者自己的理解和感悟,仅供参考

1. 概述

这篇关于Spring Framework的文章将为你演示三种与依赖注入相关的注解的使用,它们分别是:@Resource, @Inject, 和 @Autowired。这些注解为你的类提供了通过声明解决依赖的方式,举个例子:

@Autowired
ArbitraryClass arbObject;

而不是通过直接实例化对象的方式(通过显式调用构造方法),比如:

ArbitraryClass arbObject = new ArbitraryClass();

三种注解的其中两个分别属于Java的扩展包:javax.annotation.Resourcejavax.inject.Inject。(Java官方的扩展包)而@Autowired注解来自于org.springframework.beans.factory.annotation 包。(spring框架提供的注解包

三种注解都可以通过字段注入或setter方法注入的形式来解决依赖问题。在下面这个简单而实用的例子中,将基于三种注解各自的执行方式来演示它们使用上的区别。

例子将借助集成测试专注于展示如何使用这三种注解,在测试中的依赖关系可以是任意的文件或任意的类。

文章示例中展示的仅为部分源码,可在文末点击GitHub链接前往查看和下载完整源码

2. @Resource注解

@Resource注解是JSR-250中定义的注解集中的一员,并且归入了Java EE的规范包中。

JSR是由JCP(Java Community Process)定制的Java标准化技术规范,可以看做是官方给开发者提供的接口标准,很多厂商会对此进行实现,但也有很多会定制自己的规范。

该注解的执行过程的优先级顺序如下:

  1. 命名匹配(Match by Name)
  2. 类型匹配(Match by Type)
  3. 限定符匹配(Match by Qualifier)

这些执行过程均可以适用于setter方法注入字段注入

2.1 字段注入

字段注入来解决依赖是通过在实例字段上使用@Resource注解的方式进行的。

2.1.1 命名匹配

下面的集成测试案例演示了如何通过命名匹配的形式进行字段注入:

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

    @Resource(name="namedFile")
    private File defaultFile;
     
    @Test
    public void givenResourceAnnotation_WhenOnField_ThenDependencyValid(){
        assertNotNull(defaultFile);
        assertEquals("namedFile.txt", defaultFile.getName());
    }
}

我们一起浏览这段代码。在FieldResourceInjectionTest单元测试类中的第7行,在@Resource注解的属性中传入了bean的名称实现了通过命名来解决依赖:

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

这个配置将使用“命名匹配”规则来控制依赖关系。被命名为namedFile的bean必须定义在ApplicationContextTestResourceNameType这个应用容器中。(因为示例中的集成测试指定加载了该容器类

请注意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 类型匹配

想演示通过类型匹配的执行方式,只需要移除在FieldResourceInjectionTest 集成测试类里第7行的属性,就像下面这样:

@Resource
private File defaultFile;

然后再次运行它。

这个测试依然将会通过,因为当@Resource注解没有接受到属性值传入的指定bean名称时,Spring Framework将会通过下一优先级即类型匹配的方式来解决依赖注入。

2.1.3 限定符匹配

下面要演示限定符匹配的执行方式,那么原来的集成测试场景将要做一些改变:定义两个bean在ApplicationContextTestResourceQualifier应用容器中:

@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 QualifierResourceInjectionTest {
 
    @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注解通过指定依赖注入bean清除了未明确的困惑。

2.2 Setter方法注入

在字段上注入依赖项执行过程适用于使用基于setter方法的注入。

2.2.1 命名匹配

与上面的例子的区别是,MethodResourceInjectionTest集成测试中有一个setter方法:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceNameType.class)
public class MethodResourceInjectionTest {
 
    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 类型匹配

为了演示基于setter方法的类型匹配执行过程,我们将使用MethodByTypeResourceTest集成测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceNameType.class)
public class MethodByTypeResourceTest {
 
    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

这个异常异常验证了该过程确实使用了类型匹配的规则来解析File依赖项。NoSuchBeanDefinitionException异常的抛出确认了引用变量的名称不需要匹配bean的名字。与之相反,依赖项解析依赖是通过bean的类型与引用变量的类型进行匹配。

2.2.3 限定符匹配

下面这个MethodByQualifierResourceTest集成测试将被用来演示限定符匹配的执行过程:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceQualifier.class)
public class MethodByQualifierResourceTest {
 
    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注解集。这个注解具有以下按优先级的执行过程:

  1. 类型匹配(Match by Type)
  2. 限定符匹配(Match by Qualifier)
  3. 命名匹配(Match by Name)

这些执行过程适用于Setter方法注入和字段注入。为了使用@Inject注解,需要将javax.inject库通过Gradle或Maven进行依赖包引入。

使用Gradle:

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

使用Maven:

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

3.1 字段注入

3.1.1 类型匹配

集成测试中的示例将被修改为使用其它类型的依赖项,即ArbitraryDependency类。该依赖项只是作为一个简单依赖类,并且不具备更多的意义。代码如下:

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

这一例子中的集成测试如下:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestInjectType.class)
public class FieldInjectTest {
 
    @Inject
    private ArbitraryDependency fieldInjectDependency;
 
    @Test
    public void givenInjectAnnotation_WhenOnField_ThenValidDependency(){
        assertNotNull(fieldInjectDependency);
        assertEquals("Arbitrary Dependency",
          fieldInjectDependency.toString());
    }
}

不像@Resource注解优先使用命名匹配规则,@Inject注解的默认行为是优先使用类型匹配规则来解决依赖。

这意味着即使一个类型依赖变量的命名与对容器中对应定义着的bean命名不同,依赖依然会被正常处理。

成员变量的命名如下:

@Inject
private ArbitraryDependency fieldInjectDependency;

与容器中配置的bean的命名有所不同:

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

当测试被运行时,这个依赖是可以被正常处理的。

3.1.2 限定符匹配

如果一个特别的类有多个不同的实现并且一个确定的类需要一个指定的bean时,又该如何处理呢?下面我们修改集成测试的例子来指定另一个依赖。

在下面的例子中,我们通过继承在“类型匹配”例子中使用的ArbitraryDependency类创建了AnotherArbitraryDependency子类:

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 FieldQualifierInjectTest {
 
    @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集成测试尝试以下面列出的方式去注入依赖:

@Inject
private ArbitraryDependency defaultDependency;
 
@Inject
private ArbitraryDependency namedDependency;

那么将会抛出一个NoUniqueBeanDefinitionException异常。

抛出此异常是因为Spring Framework指出某个类具有有多个实现,并且对于使用哪个类产生困惑。为了阐明这种混淆的关系,请转到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 按名称匹配

用于演示按名称匹配的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 FieldByNameInjectTest {
 
    @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不同于现在的名称。再次运行测试,这次将会抛出NoSuchBeanDefinitionException异常。

以上例子可以看出@Named与@Qualifier注解的使用方式和作用基本相同,都是通过bean名称来指定依赖,区别在于@Named注解与@Inject注解同属于JSR-330注解集,而@Qualifier是Spring Framework提供的注解(javax.inject包中同样提供了一个名为@Qualifier的注解,但该注解的作用是用来标识作为限定符注解,@Named注解即为官方的一个标准实现)。使用的取舍在于你对Spring Framework依赖程度的期望。

3.2 Setter方法注入

@Inject注解的setter方法注入与@Resource注解相类似。不再注释引用变量,而是注释相应的setter方法。基于字段的依赖注入方式也同样适用于setter方法注入。

4. @Autowired 注解

@Autowired注解的注入过程类似于@Inject注释。唯一的区别是@Autowired注解是Spring框架的一部分。

Spring 的官方文档同样也说明了这一点,@Autowired与@Inject注解具有相同的依赖处理过程。

此注解与@Inject注解具有相同的注入过程优先级:

  1. 类型匹配(Match by Type)
  2. 限定符匹配(Match by Qualifier)
  3. 命名匹配(Match by Name)

这些注入过程同样适用于setter方法注入和字段注入。

4.1 字段注入

4.1.1 类型匹配

演示@Autowired注解按类型匹配的示例与演示@Inject注解按类型匹配的示例相类似。下面这个FieldAutowiredTest集成测试将用来演示@Autowired注解进行类型匹配的过程:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestAutowiredType.class)
public class FieldAutowiredTest {
 
    @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 限定符匹配

如果遇到了应用容器中定义了多个实现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;
    }
}

再执行下面的集成测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestAutowiredQualifier.class)
public class FieldQualifierAutowiredTest {
 
    @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());
    }
}

将会抛出NoUniqueBeanDefinitionException

这个异常是由容器中定义了两个bean引起的歧义导致的。Spring Framework容器不知道哪个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 按名称匹配

我们使用相同的集成测试场景来演示@Autowired注解如何通过名称匹配注入字段依赖关系。当通过名称自动注入时,需要在ApplicationContextTestAutowiredName应用容器上使用@ComponentScan注解:

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

@ComponentScan用来标注在哪些包中搜索带@Component注解的Java类。例如,应用程序将扫描com.baeldung.dependency包中使用@Component注解的类。在现在的场景中,ArbitraryDependency 类由于使用了@Component注解,它将会被Spring Framework检查到:

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

赋给@Component注解的value属性值autowiredFieldDependency将告诉Spring Framework,ArbitraryDependency类是一个名为autowiredFieldDependency的Bean。为了使@Autowired按名称解析依赖,组件名称必须与FieldAutowiredNameTest测试中定义的字段名称相对应;参考第8行:

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

运行集成测试,它将顺利通过。

但是我们怎么知道@Autowired注解确实使用了按名称匹配的方式呢?将成员变量autowiredFieldDependency的名称更改为另一个名称,再次运行测试。

这一次,测试将执行失败并抛出NoUniqueBeanDefinitionException。类似的检查方式是将@Component的value值autowiredFieldDependency修改为另一个值并再次运行测试。同样将抛出NoUniqueBeanDefinitionException

这个异常证明了如果使用了错误的bean名称,将不会找到有效的bean。因此, @Autowired注解使用了按名称匹配的方式。

4.2 Setter方法注入

@Autowired注解的setter方法注入与基于@Resource注解的注入方式类似。基于字段的注入过程同样适用于基于setter方法注入。

5.应用这些注解

现在出现了一个问题,应该在什么情况下应用这些注解?问题的答案在于应用程序所使用的设计方案以及开发者期望如何基于注解不同的注入方式来利用多态性。

5.1 通过多态在应用程序范围内使用单例

如果使用多态只是基于接口和抽象类来实现应用程序行为,并且这些行为在整个应用程序中使用,则应该使用@Inject@Autowired注解。

这种方法的好处将在应用程序升级或需要补丁来修复错误时体现出来;其次在对类进行更换时可以对应用程序造成最小的负面影响。在此方案中,处理依赖的主要方式是按类型匹配;

5.2 通过多态细粒度配置应用程序行为

如果多态的使用是为了使应用程序拥有更复杂的行为——每一个行为都基于不同的接口或抽象类,并且使用它们的实现会因应用程序而异,则使用@Resource注解。在此方案中,处理依赖的主要方式是按名称匹配。

5.3 在Java EE平台处理依赖注入

如果选择在JavaEE平台而不是Spring处理所有的依赖,那么可以在@Resource注解和@Inject注解中选择其一。你应该基于它们的默认处理方式缩小两种注解之间的最终使用范围。

5.4 在Spring框架中处理依赖注入

如果应用仅仅依赖于Spring框架,那唯一选择就是@Autowired注解。

5.5 总结

下表对以上讨论进行了总结:

场景 @Resource @Inject @Autowired
通过多态在应用程序范围使用单例 ×
通过多态细粒度配置应用程序行为 × ×
依赖注入仅由使用Java EE平台 ×
依赖注入仅使用Spring Framework × ×

6. 结论

本文旨在更深入地了解每个注解的处理过程。了解每个注解的注入规则将有助于更好地进行整体应用的设计和维护。

文中使用的代码均可以在GitHub中找到。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容