Spring @Autowired 指南
1. 概述
从 Spring 2.5 开始,该框架引入了注解驱动的依赖注入。此功能的主要注释是*@Autowired*。它允许 Spring 解析协作 bean 并将其注入到我们的 bean 中。
在本教程中,我们将首先了解如何启用自动装配以及自动装配 bean 的 各种 方法。之后,我们将讨论使用**@Qualifier注解解决 bean 冲突**,以及潜在的异常情况。
2. 启用*@Autowired*注解
Spring 框架支持自动依赖注入。换句话说,通过在 Spring 配置文件中声明所有 bean 依赖项,Spring 容器可以自动装配协作 bean 之间的关系。这称为Spring bean 自动装配。
要在我们的应用程序中使用基于 Java 的配置,让我们启用注解驱动注入 来加载我们的 Spring 配置:
@Configuration
@ComponentScan("com.codingman.autowire.sample")
public class AppConfig {}
或者,注解 主要用于激活 Spring XML 文件中的依赖注入注解。
此外,Spring Boot 引入了@SpringBootApplication 注解。此单个注释等效于使用*@Configuration*、@EnableAutoConfiguration和 @ComponentScan。
让我们在应用程序的主类中使用这个注解:
@SpringBootApplication
class VehicleFactoryApplication {
public static void main(String[] args) {
SpringApplication.run(VehicleFactoryApplication.class, args);
}
}
因此,当我们运行这个 Spring Boot 应用程序时,它会自动扫描当前包及其子包中的组件。因此它将在 Spring 的应用程序上下文中注册它们,并允许我们使用*@Autowired*注入 bean 。
3. 使用*@Autowired*
启用注解注入后,我们可以对属性、设置器和构造器使用自动装配。
3.1 @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
现在让我们尝试在 setter 方法上添加*@Autowired*注解。
在以下示例中,在创建FooService时使用FooFormatter的实例调用 setter 方法:
public class FooService {
private FooFormatter fooFormatter;
@Autowired
public void setFooFormatter(FooFormatter fooFormatter) {
this.fooFormatter = fooFormatter;
}
}
3.3. @Autowired在构造函数上
最后,让我们在构造函数上使用*@Autowired*。
我们将看到Spring 将 FooFormatter 的实例作为FooService 构造函数的参数注入:
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. Autowire歧义
默认情况下,Spring 按类型解析*@Autowired*条目。如果容器中有多个相同类型的 bean 可用,框架将抛出一个致命异常。
为了解决这个冲突,我们需要明确地告诉 Spring 我们要注入哪个bean。
5.1 @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的具体实现。因此, Spring 在构造FooService时会抛出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注解的值与我们的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 来指定自定义值:
@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元注释中指定的值限制了应用限定符的位置,在我们的示例中是字段、方法、类型和参数。
5.3. 按名称自动装配
**Spring 使用 bean 的名称作为默认限定符值。**它将检查容器并查找具有确切名称的 bean 作为属性来自动装配它。
因此,在我们的示例中,Spring 将fooFormatter属性名称与FooFormatter实现相匹配。因此,它在构造FooService时注入了该特定实现:
public class FooService {
@Autowired
private Formatter fooFormatter;
}