概述

自Spring 2.5版本开始,框架引入了基于注解的依赖注入(Dependency Injection)。主要的注解是@Autowired。它使得Spring能够自动识别并注入协作的bean到我们的bean中。

在这个教程中,我们将首先了解如何启用自动注入以及注入bean的各种方式。随后,我们将讨论如何使用@Qualifier注解解决bean冲突,以及可能的异常情况。

启用@Autowired注解

Spring框架支持自动依赖注入。换句话说,通过在Spring配置文件中声明所有bean依赖,Spring容器可以自动管理协作bean之间的关系。这就是所谓的Spring bean自动注入

要在应用程序中使用基于Java的配置,我们可以启用注解驱动的注入来加载Spring配置:

@Configuration
@ComponentScan("com.baeldung.autowire.sample")
public class AppConfig {}

另外,<context:annotation-config>注解主要用于激活Spring XML文件中的依赖注入注解。

此外,Spring Boot引入了@SpringBootApplication注解。这个单一注解等同于使用@Configuration@EnableAutoConfiguration@ComponentScan

让我们在应用程序的主类中使用这个注解:

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

因此,当运行Spring Boot应用时,它会自动扫描当前包及其子包中的组件,并将它们注册到Spring的应用上下文中,允许我们使用@Autowired进行bean注入。

使用@Autowired

启用注解注入后,我们可以在属性、setter方法和构造函数上使用自动注入。

@Autowired在属性上的使用

让我们看看如何使用@Autowired注解标记属性,从而消除getter和setter的需要。首先,定义一个fooFormatter bean:

@Component("fooFormatter")
public class FooFormatter {
    public String format() {
        return "foo";
    }
}

然后,我们使用@Autowired在字段定义上将此bean注入到FooService bean中:

@Component
public class FooService {  
    @Autowired
    private FooFormatter fooFormatter;
}

结果,当FooService创建时,Spring会注入fooFormatter

@Autowired在setter方法上的使用

现在尝试在setter方法上添加@Autowired注解。

在下面的例子中,当FooService创建时,setter方法会接收到FooFormatter实例:

public class FooService {
    private FooFormatter fooFormatter;
    @Autowired
    public void setFormatter(FooFormatter fooFormatter) {
        this.fooFormatter = fooFormatter;
    }
}

@Autowired在构造函数上的使用

最后,我们在构造函数上使用@Autowired

我们会看到Spring作为参数注入一个FooFormatter实例到FooService的构造函数中:

public class FooService {
    private FooFormatter fooFormatter;
    @Autowired
    public FooService(FooFormatter fooFormatter) {
        this.fooFormatter = fooFormatter;
    }
}

@Autowired与可选依赖

在bean构建时,@Autowired依赖应该是可用的。否则,如果Spring无法为注入找到bean,它会抛出异常。

因此,这会导致Spring容器无法成功启动,抛出类似以下形式的异常:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type [com.autowire.sample.FooDAO] 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)}

要解决这个问题,我们需要声明所需类型的bean:

public class FooService {
    @Autowired(required = false)
    private FooDAO dataAccessor; 
}

自动注入歧义解决

默认情况下,Spring通过类型来解析@Autowired条目。如果容器中有多个相同类型的bean,框架会抛出致命异常。

要解决这种冲突,我们需要明确告诉Spring我们想要注入哪个bean。

使用@Qualifier进行注入

例如,让我们看看如何使用@Qualifier注解来指示所需的bean。

首先,我们定义两个类型为Formatter的bean:

@Component("fooFormatter")
public class FooFormatter implements Formatter {
    public String format() {
        return "foo";
    }
}
@Component("barFormatter")
public class BarFormatter implements Formatter {
    public String format() {
        return "bar";
    }
}

现在尝试将Formatter bean注入到FooService类中:

public class FooService {
    @Autowired
    private Formatter formatter;
}

在本例中,Spring容器有两份Formatter的具体实现。因此,当构建FooService时,Spring会抛出NoUniqueBeanDefinitionException异常:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
No qualifying bean of type [com.autowire.sample.Formatter] is defined: 
expected single matching bean but found 2: barFormatter,fooFormatter

我们可以通过@Qualifier注解来避免这种情况,具体做法是:

public class FooService {
    @Autowired
    @Qualifier("fooFormatter")
    private Formatter formatter;
}

当存在多个相同类型的bean时,使用@Qualifier来避免歧义是一个好主意。

请注意,@Qualifier注解的值应与我们在@Component注解中为FooFormatter实现声明的名称匹配。

使用自定义@Qualifier进行注入

Spring还允许我们创建自己的自定义@Qualifier注解。为此,我们需要提供@Qualifier注解的定义:

@Qualifier
@Target({
  ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface FormatterType {  
    String value();
}

然后,我们可以在各种实现中使用FormatterType指定一个自定义值:

@FormatterType("Foo")
@Component
public class FooFormatter implements Formatter {
    public String format() {
        return "foo";
    }
}
@FormatterType("Bar")
@Component
public class BarFormatter implements Formatter {
    public String format() {
        return "bar";
    }
}

最后,我们的自定义@Qualifier注解准备好用于自动注入:

@Component
public class FooService {  
    @Autowired
    @FormatterType("Foo")
    private Formatter formatter;
}

@Target 注解中的值限制了注解的适用范围,在我们的例子中,它适用于字段、方法、类型和参数。

使用名称进行注入

Spring使用bean的名称作为默认的注解值。它会检查容器,寻找与属性名完全匹配的bean,然后注入它。

因此,在我们的示例中,Spring会将fooFormatter属性名与FooFormatter实现匹配。因此,在构建FooService时,它会注入特定的实现:

public class FooService {
 @Autowired 
private Formatter fooFormatter; 
}

总结

本文讨论了自动注入及其使用方式。我们还探讨了解决由缺失bean或模糊的bean注入引发的两种常见自动注入异常的方法。

这篇文章的源代码可以在GitHub项目中获取。


« 上一篇: Java 中枚举指南
» 下一篇: Spring中Bean的作用域