1. 概述
从Spring 2.5开始,该框架引入了注释驱动的依赖注入。此功能的主要注释是@Autowired,它允许Spring解决并将协作豆子注入我们的豆子中。
了解Spring组件扫描背后的机制,以及如何根据自己的需要进行调整
快速介绍控制反转和依赖注入的概念,然后使用Spring框架进行简单演示
在本教程中,我们将首先了解如何启用自动布线以及自动连接 Bean 的各种方法。之后,我们将讨论如何使用@Qualifier注释以及潜在的异常情况来解决 Bean 冲突。
2. 启用@Autowired注释
Spring框架支持自动依赖注入。换句话说,通过在Spring配置文件中声明所有Bean依赖项,Spring容器可以自动连接协作bean之间的关系。这称为弹簧豆自动布线。
要在应用程序中使用基于 Java 的配置,让我们启用注释驱动的注入来加载我们的 Spring 配置:
@Configuration
@ComponentScan("com.baeldung.autowire.sample")
public class AppConfig {}
或者,<上下文:注释-配置>注释主要用于激活Spring XML文件中的依赖关系注入注释。
此外,弹簧靴引入了@SpringBootApplication注释。此单个批注等效于使用@Configuration、@EnableAutoConfiguration和@ComponentScan。
让我们在应用程序的主类中使用此注释:
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
因此,当我们运行此Spring Boot应用程序时,它将自动扫描当前包及其子包中的组件。因此,它将在Spring的应用程序上下文中注册它们,并允许我们使用@Autowired注入bean。
3. 使用@Autowired
启用注释注入后,我们可以对属性、设置器和构造函数使用自动布线。
3.1. 属性@Autowired
让我们看看如何使用@Autowired来注释属性。这消除了对获取器和二传器的需求。
首先,让我们定义一个 foo 格式化字节:
然后,我们将使用字段定义上的@Autowired将此 bean 注入 FooService bean 中:
@Component
public class FooService {
@Autowired
private FooFormatter fooFormatter;
}
因此,当创建 Foo 服务时,Spring 会注入 fooFormatter。
3.2. @Autowired
现在,让我们尝试在 setter 方法上添加@Autowired注释。
在下面的示例中,当创建 Foo 服务时,使用 Foo 格式的实例调用 setter 方法:
public class FooService {
private FooFormatter fooFormatter;
@Autowired
public void setFormatter(FooFormatter fooFormatter) {
this.fooFormatter = fooFormatter;
}
}
3.3. 构造函数@Autowired
最后,让我们在构造函数上使用@Autowired。
我们将看到,一个 Foo 格式化器的实例是由 Spring 注入的,作为 Foo 服务构造函数的参数:
public class FooService {
private FooFormatter fooFormatter;
@Autowired
public FooService(FooFormatter fooFormatter) {
this.fooFormatter = fooFormatter;
}
}
4. @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;
}
5. 自动布线消歧
默认情况下,Spring 按类型解析@Autowired条目。如果容器中有多个相同类型的 Bean 可用,则框架将引发致命异常。
为了解决这个冲突,我们需要明确地告诉Spring我们要注射哪种豆子。
5.1. @Qualifier自动布线
例如,让我们看看如何使用@Qualifier注释来指示所需的bean。
首先,我们将定义 2 个格式化程序类型的 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";
}
}
现在,让我们尝试将格式化程序 Bean 注入到 FooService 类中:
public class FooService {
@Autowired
private Formatter formatter;
}
在我们的示例中,有两个格式化程序的具体实现可用于 Spring 容器。因此,在构造 FooService 时,Spring 将引发一个无独一无二的定义异常:
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;
}
当有多个相同类型的豆子时,最好使用@Qualifier以避免歧义。
请注意,@Qualifier注释的值与 FooFormatter 实现的@Component注释中声明的名称匹配。
5.2. 通过自定义限定符自动布线
Spring还允许我们创建自己的自定义@Qualifier注释。为此,我们应该为@Qualifier注释提供定义:
@Qualifier
@Target({
ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface FormatterType {
String 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中指定的值限制了应用限定符的位置,在我们的示例中,限定符是字段、方法、类型和参数。
5.3. 按名称自动布线
Spring 使用豆子的名称作为默认限定符值。 它将检查容器并查找具有确切名称作为属性的bean以自动连接它。
因此,在我们的示例中,Spring 将 foo 格式化器属性名称与 Foo 格式化器实现相匹配。因此,它在构建 FooService 时注入了该特定实现:
public class FooService {
@Autowired
private Formatter fooFormatter;
}
6. 结论
在本文中,我们讨论了自动布线及其使用方式的不同方法。我们还研究了解决由缺少 Bean 或模棱两可的 Bean 导致的两种常见自动连线异常的方法。