Contents

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; 
}