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 导致的两种常见自动连线异常的方法。