Spring @Autowired指南

2023/05/13

1. 概述

从Spring 2.5开始,Spring引入了注解驱动的依赖注入。这个特性的主要注解是@Autowired。 它允许Spring解析依赖bean并将其注入到我们的bean中

在本文中,我们将首先了解如何启用自动装配以及自动装配bean的各种方法。 之后,我们将介绍使用@Qualifier注解解决bean冲突,以及潜在的异常情况。

2. 启用@Autowired注解

Spring框架支持自动依赖注入。 换句话说,通过在Spring配置文件中声明所有bean依赖项,Spring容器可以自动装配协作bean之间的关系。 这称为Spring bean自动装配

要在我们的应用程序中使用基于Java的配置,我们需要启用注解驱动注入来加载我们的Spring配置:


@Configuration
@ComponentScan("cn.tuyucheng.taketoday.autowire.sample")
public class AppConfig {

}

或者,使用Spring XML文件配置时可以声明<context:annotation-config>。

此外,Spring Boot引入了@SpringBootApplication注解。 这个注解等效于@Configuration、@EnableAutoConfiguration和@ComponentScan这三个注解。

让我们在应用程序的主类上使用此注解:


@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

当我们运行这个Spring Boot应用程序时,它会自动扫描当前包及其子包中的bean。 然后在Spring的应用程序上下文中注册它们,并允许我们使用@Autowired注入bean。

3. @Autowired的使用

启用注解驱动后,我们可以在成员变量、setter方法和构造方法上使用@Autowired注解

3.1 在属性上使用@Autowired

让我们看看如何在属性上使用@Autowired注解,这消除了对getter和setter方法的需要。

首先,让我们定义一个fooFormatter bean:

public interface Formatter {
    String format();
}

@Component("fooFormatter")
public class FooFormatter {

    public String format() {
        return "foo";
    }
}

然后,我们将在字段定义上使用@Autowired将此bean注入进FooService bean:


@Component
public class FooService {
    @Autowired
    private FooFormatter fooFormatter;
}

因此,Spring在创建FooService类型的bean时会自动注入fooFormatter。

3.2 在setter方法上使用@Autowired

现在,让我们尝试在setter方法上使用@Autowired注解。

在以下示例中,创建FooService时使用FooFormatter的实例调用setFooFormatter()方法:

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 [cn.tuyucheng.taketoday.autowired.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属性设置为false:

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 [cn.tuyucheng.taketoday.autowire.sample.Formatter] is defined: 
expected single matching bean but found 2: barFormatter,fooFormatter

我们可以通过使用@Qualifier注解指定需要注入的bean的名称来避免这种情况

public class FooService {
    @Autowired
    @Qualifier("fooFormatter")
    private Formatter formatter;
}

当有多个相同类型的bean时,最好使用@Qualifier注解来避免这种情况

请注意,@Qualifier注解value属性的值需要与我们在FooFormatter实现类中@Component注解声明的名称匹配。

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来指定自定义值:


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

最后,我们的自定义@FormatterType注解可用于自动注入:


@Component
public class FooService {
    @Autowired
    @FormatterType("Foo")
    private Formatter formatter;
}

@Target元注解中指定的值限制了@FormatterType可以使用的位置,在我们的示例中可以在字段、方法、类和参数上使用。

5.3 按名称自动注入

Spring使用bean的名称作为默认限定符值。它将检查容器并查找具有确切名称的bean作为属性来自动装配它。

在下面的例子中,Spring将字段名称fooFormatter与名为fooFormatter的bean相匹配。 因此,它在构建FooService时会注入FooFormatter类型的bean:

public class FooService {
    @Autowired
    private Formatter fooFormatter;
}

6. 总结

在本文中,我们讨论了自动注入及其不同的使用方法。我们还说明了解决由缺少bean或不明确bean注入引起的两种常见自动注入异常的方法。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章