Spring 中注入 Bean 的方式推荐

在 Spring 中,注入 Bean 的方式有多种,每种方式适用于不同的场景。一般来说,可以选择以下几种注入方式:构造器注入 、setter 注入 、字段注入 (即使用 @Autowired)以及基于注解的注入(如 @Qualifier)。每种方式都有其优缺点,下面是对每种方式的分析,并给出推荐的最佳实践。

1. 构造器注入(推荐方式)

构造器注入是通过构造函数来注入依赖的方式,它是最为推荐的注入方式,具有以下优点:

  • 明确依赖关系:构造器注入可以强制所有必需的依赖在对象创建时就被注入,这有助于避免对象处于不完整状态(即未注入依赖的状态)。
  • 更容易进行单元测试:构造器注入可以确保依赖在创建时就被注入,因此在进行单元测试时,可以轻松地将依赖注入到构造函数中,便于进行 Mock。
  • 不可变性:通过构造器注入,依赖关系通常是不可变的,有助于提高对象的稳定性。
java 复制代码
@Component
public class MyService {
    private final MyRepository myRepository;

    @Autowired
    public MyService(MyRepository myRepository) {
        this.myRepository = myRepository;
    }

    public void serve() {
        System.out.println("Service using " + myRepository);
    }
}

总结:

  • 优点:强制依赖注入,确保对象在创建时完整,适合需要确保所有依赖的场景。
  • 推荐使用:对于所有必需的依赖,优先使用构造器注入。

2. Setter 注入(次选)

Setter 注入是通过 setter 方法来注入依赖。Spring 会在 Bean 创建后调用 setter 方法来注入依赖。

  • 灵活性:可以选择性地注入某些依赖。
  • 不强制依赖 :与构造器注入不同,setter 注入是可选的,不强制所有依赖都必须被注入,可能会导致依赖项没有被注入,从而导致潜在的 NullPointerException。
  • 适用于可选依赖:对于那些可以不注入的依赖,使用 setter 注入较为合适。
java 复制代码
@Component
public class MyService {
    private MyRepository myRepository;

    @Autowired
    public void setMyRepository(MyRepository myRepository) {
        this.myRepository = myRepository;
    }

    public void serve() {
        System.out.println("Service using " + myRepository);
    }
}

总结:

  • 优点:灵活性较高,适用于那些依赖关系可选的场景。
  • 推荐使用:对于可选的依赖,使用 setter 注入。

3. 字段注入(不推荐,除非必须)

字段注入 是通过直接在字段上使用 @Autowired 注解来注入依赖。Spring 会在创建 Bean 时直接注入字段。这种方式简单,但也有一些明显的缺点。

  • 不易测试:字段注入直接操作类的字段,无法通过构造器或 setter 进行手动注入,导致在单元测试中难以模拟依赖。
  • 不可控性:字段注入导致依赖关系不明确,不能像构造器注入那样保证对象创建时一定处于完整状态。
  • 不推荐使用:字段注入不如构造器和 setter 注入明确和清晰,通常不推荐在应用程序中使用。
java 复制代码
@Component
public class MyService {
    @Autowired
    private MyRepository myRepository;

    public void serve() {
        System.out.println("Service using " + myRepository);
    }
}

总结:

  • 优点:代码简洁,易于实现。
  • 缺点:不适合严格要求测试和依赖明确的场景。
  • 推荐使用:尽量避免使用,除非你对注入的依赖非常宽松,且不介意难以进行单元测试。

4. 使用 @Qualifier 区分多个同类型 Bean

当有多个相同类型的 Bean 时,Spring 可能无法确定注入哪一个。此时可以使用 @Qualifier 来明确指定注入的 Bean。

  • 常见应用 :适用于同类型但功能不同的多个 Bean,例如有多个 DataSource 或多个服务实现类。
  • 避免冲突 :使用 @Qualifier 可以明确指明应该注入哪一个 Bean,避免 Spring 抛出 NoUniqueBeanDefinitionException 异常。
java 复制代码
@Component
@Qualifier("myService1")
public class MyService1 implements MyService {
    public void serve() {
        System.out.println("Service 1");
    }
}

@Component
@Qualifier("myService2")
public class MyService2 implements MyService {
    public void serve() {
        System.out.println("Service 2");
    }
}

@Component
public class Consumer {
    private final MyService myService;

    @Autowired
    public Consumer(@Qualifier("myService1") MyService myService) {
        this.myService = myService;
    }

    public void serve() {
        myService.serve();
    }
}

总结:

  • 优点:解决了同类型 Bean 注入冲突的问题。
  • 推荐使用:在同类型 Bean 存在时,通过 @Qualifier 来避免冲突。

结论

  • 首选:构造器注入,因为它提供了强依赖、清晰的依赖关系、便于测试且更具可维护性。
  • 次选:Setter 注入,适合可选依赖的场景,但在生产环境中通常还是偏好构造器注入。
  • 避免使用:字段注入,因为它减少了依赖关系的明确性,且测试起来不方便。

总的来说,尽量在 Spring 中使用 构造器注入 ,只有在特殊情况下(如依赖关系是可选的)才使用 setter 注入。

相关推荐
Elastic 中国社区官方博客2 小时前
我们如何通过 70 多个拉取请求,将 OpenTelemetry Java agent 迁移到 invokedynamic
java·大数据·开发语言·elasticsearch·搜索引擎·全文检索·es
tyn1882 小时前
记录本地安装nacos服务的方法及异常
java·开发语言
步行cgn2 小时前
Spring MVC 视图机制的核心接口与类
java·开发语言·后端·spring·mvc
y1su3 小时前
Leetcode 字符串模板
java·数据结构·算法·leetcode·职场和发展·动态规划
我命由我123454 小时前
Python 开发 - Python 的 pathlib
java·开发语言·后端·python·学习·java-ee·学习方法
苜蓿muxu4 小时前
Java 字符串拼接:+、StringBuilder、StringBuffer的区别
java
摇滚侠4 小时前
《Java并发编程之美》第 4 章 Java 并发包中原子操作类原理剖析 阅读笔记 7
java·笔记·python
旋律4145 小时前
同义方法区别(增加类与删除类)
java·后端
学到老活到老5 小时前
方法执行完了,对象为什么还占着内存?
java·jvm