1、概览 {#1概览}
从 Spring 2.5 开始,框架引入了注解驱动的依赖注入功能。该功能的主要注解是 @Autowired
。它允许 Spring 解析并注入所依赖的 Bean 到 Bean 中。
本文将带你了解如何启用自动装配以及自动装配 Bean 的各种方法,以及如何使用 @Qualifier
注解来解决 Bean 冲突以及潜在的异常情况。
2、启用 @Autowired 注解 {#2启用-autowired-注解}
Spring 框架支持自动依赖注入。换句话说,通过在 Spring 配置文件中声明所有 Bean 的依赖关系,Spring 容器可以自动装配依赖 Bean 之间的关系。这就是所谓的 Spring Bean 自动装配。
首先启用注解驱动注入来加载 Spring 配置(基于 Java 的配置):
@Configuration
@ComponentScan("com.baeldung.autowire.sample")
public class AppConfig {}
<context:annotation-config>
主要用于激活 Spring XML 文件中的依赖注入注解。
Spring Boot 还引入了 @SpringBootApplication
注解。这个注解相当于使用 @Configuration
、@EnableAutoConfiguration
和 @ComponentScan
。
在应用的 main 类中使用这个注解:
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
当运行 Spring Boot 应用时,它会自动扫描当前包及其子包中的组件。因此,它将在 Spring 的 Application Context 注册这些组件,并允许使用 @Autowired
注入 Bean。
3、使用 @Autowired {#3使用-autowired}
启用注解注入后,可以在属性、Setter 和构造器上使用自动装配。
3.1、在属性上使用 @Autowired {#31在属性上使用-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;
}
Spring 会在创建 FooService
时注入 fooFormatter
。
3.2、在 Setter 方法上使用 @Autowired {#32在-setter-方法上使用-autowired}
现在,尝试在 Setter 方法上添加 @Autowired
注解。
在下面的示例中,创建 FooService
时会调用 FooFormatter
实例的 Setter 方法:
public class FooService {
private FooFormatter fooFormatter;
@Autowired
public void setFormatter(FooFormatter fooFormatter) {
this.fooFormatter = fooFormatter;
}
}
3.3、在构造函数上使用 @Autowired {#33在构造函数上使用-autowired}
最后,在构造函数上使用 @Autowired
。
如下,Spring 注入了一个 FooFormatter
实例作为 FooService
构造函数的参数:
public class FooService {
private FooFormatter fooFormatter;
@Autowired
public FooService(FooFormatter fooFormatter) {
this.fooFormatter = fooFormatter;
}
}
4、@Autowired 和可选依赖 {#4autowired-和可选依赖}
在构建 Bean 时,@Autowired
依赖应该可用。否则,如果 Spring 无法解析用于装配的 Bean,它就会阻止 Spring 容器成功启动,并抛出 NoSuchBeanDefinitionException
异常:
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)}
要解决这个问题,需要声明一个 required
类型的 Bean:
public class FooService {
@Autowired(required = false)
private FooDAO dataAccessor;
}
5、自动装配消岐 {#5自动装配消岐}
默认情况下,Spring 按类型解析 @Autowired
依赖。如果容器中存在多个相同类型的 Bean,框架将抛出异常。
要解决这一冲突,需要明确告诉 Spring 要注入哪个 Bean。
5.1、使用 @Qualifier 进行自动装配 {#51使用-qualifier-进行自动装配}
可以使用 @Qualifier
注解来指示所需的 Bean。
首先,定义 2 个 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
注解的 value
值与 FooFormatter
实现的 @Component
注解中声明的 name
一致。
5.2、通过自定义 Qualifier 进行装配 {#52通过自定义-qualifier-进行装配}
Spring 还允许创建自定义的 @Qualifier
注解。
在自定义注解中,以元注解形式定义 @Qualifier
注解:
@Qualifier
@Target({
ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface FormatterType {
String value();
}
然后,可以在各种实现中使用 FormatterType
来指定自定义 value
:
@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";
}
}
最后,通过自定义注解进行装配:
@Component
public class FooService {
@Autowired
@FormatterType("Foo")
private Formatter formatter;
}
在 @Target
元注解中指定的值限制了应用 Qualifier 的位置,在本例中就是字段、方法、类型和参数。
5.3、根据 Bean 名称装配 {#53根据-bean-名称装配}
Spring 使用 Bean 的名称作为默认 Qualifier value。它会检查容器,并查找与要自动装配的属性名称完全相同的 Bean 来进行装配。
因此,在如下示例中,Spring 将 fooFormatter
属性名与 FooFormatter
实现相匹配。因此,在构建 FooService
时,它会注入该特定实现:
public class FooService {
@Qualifier
private Formatter fooFormatter;
}
6、总结 {#6总结}
本文介绍了 Spring 中的自动装配以及 @Autowired
注解不同的使用方法。还介绍了如何解决依赖 Bean 不存在或 Bean 注入不明确的问题。
Ref:https://www.baeldung.com/spring-autowire