Spring 中的 @Autowired 注解

1、概览

Spring 2.5 开始,框架引入了注解驱动的依赖注入功能。该功能的主要注解是 @Autowired 。它允许 Spring 解析并注入所依赖的 Bean 到 Bean 中。

本文将带你了解如何启用自动装配以及自动装配 Bean 的各种方法,以及如何使用 @Qualifier 注解来解决 Bean 冲突以及潜在的异常情况。

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

启用注解注入后,可以在属性、Setter 和构造器上使用自动装配。

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 setFormatter(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 容器成功启动,并抛出 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、自动装配消岐

默认情况下,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 的具体实现。因此,在构建 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 进行装配

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 名称装配

Spring 使用 Bean 的名称作为默认 Qualifier value。它会检查容器,并查找与要自动装配的属性名称完全相同的 Bean 来进行装配。

因此,在如下示例中,Spring 将 fooFormatter 属性名与 FooFormatter 实现相匹配。因此,在构建 FooService 时,它会注入该特定实现:

public class FooService {
 @Qualifier 
private Formatter fooFormatter; 
}

6、总结

本文介绍了 Spring 中的自动装配以及 @Autowired 注解不同的使用方法。还介绍了如何解决依赖 Bean 不存在或 Bean 注入不明确的问题。


Ref:https://www.baeldung.com/spring-autowire