Administrator
发布于 2025-03-06 / 2 阅读
0
0

Spring 依赖注入方式对比与最佳实践

在 Spring 或者其他依赖注入框架中,不同的注入方式各有优缺点。本文对 @Autowired@Resource、构造器注入,以及 @RequiredArgsConstructor(Lombok 提供的简化构造器注入的注解)进行详细对比,并给出最佳实践。


1. @Autowired 注入

用法:

@Component
public class MyService {
    @Autowired
    private DependencyService dependencyService;
}

优缺点:

优点:

  • 方便,直接标注在字段上,Spring 自动注入。

  • 可以用于字段、构造器和 setter 方法,使用灵活。

缺点:

  • 字段注入(Field Injection)不推荐,因为:

    • 不利于单元测试(无法直接在测试中构造实例并注入依赖)。

    • 隐藏依赖关系,类的依赖项不明显,影响可读性。

    • 不便于构造函数初始化,无法强制依赖项的存在。


2. @Resource 注入

用法:

@Component
public class MyService {
    @Resource
    private DependencyService dependencyService;
}

优缺点:

优点:

  • @Resource 是 JSR-250 规范的一部分,兼容 Spring 和 Java EE,支持 name 指定具体 Bean。

  • @Autowired 更直观,避免 @Autowired 的 required 属性问题(@Resource 默认必须存在 Bean)。

缺点:

  • 也是字段注入,同样不利于测试和依赖管理。

  • 失去了 Spring 强大的 @Primary@Qualifier 机制。


3. 构造器注入(Constructor Injection)【推荐 ✅】

用法:

@Component
public class MyService {
    private final DependencyService dependencyService;

    @Autowired  // Spring 4.3+ 可省略
    public MyService(DependencyService dependencyService) {
        this.dependencyService = dependencyService;
    }
}

优缺点:

优点:

  • 强制依赖注入,如果某个依赖缺失,代码无法编译,避免 NPE。

  • 有利于单元测试,可以直接用 new MyService(mockDependencyService) 创建实例。

  • 依赖关系清晰,构造方法明确表明这个类需要哪些依赖。

缺点:

  • 当依赖项过多时,构造函数可能变得很长(通常这是代码设计的问题,可以考虑拆分)。


4. @RequiredArgsConstructor(Lombok)注入【推荐 ✅】

用法:

@RequiredArgsConstructor
@Component
public class MyService {
    private final DependencyService dependencyService;
}

优缺点:

优点:

  • 本质上是构造器注入的简化形式,自动为 final 字段生成构造方法。

  • 避免冗长的构造方法声明,提高可读性。

缺点:

  • 依赖 Lombok,如果团队不使用 Lombok 可能会有额外学习成本。

  • 不能自定义构造方法(除非搭配 @AllArgsConstructor)。


推荐的最佳实践

  1. 优先使用构造器注入(Constructor Injection)

    • 依赖项明确,避免 NPE,方便测试。

    • Spring 4.3+ 不需要 @Autowired,自动注入构造函数参数。

  2. Lombok 的 @RequiredArgsConstructor 可以简化构造器注入

    • 适用于 final 依赖项,减少样板代码。

  3. 不推荐字段注入(@Autowired@Resource

    • 不利于测试,隐藏依赖关系。

    • 只有在特殊情况下才用(如 AOP 代理、Spring 内部管理 Bean)。

  4. Setter 注入适用于可选依赖

    @Component
    public class MyService {
        private DependencyService dependencyService;
    
        @Autowired
        public void setDependencyService(DependencyService dependencyService) {
            this.dependencyService = dependencyService;
        }
    }
    
    • 但通常应当避免,除非有明确需求(例如可以延迟注入)。


最终结论

注入方式

适用场景

推荐程度

@Autowired(字段注入)

方便但不推荐,影响可维护性

@Resource(字段注入)

适用于 JSR-250 兼容场景,但仍是字段注入

构造器注入

推荐,强制依赖注入,利于测试

✅✅✅

@RequiredArgsConstructor(Lombok)

简化构造器注入,推荐

✅✅

如果你的项目支持 Lombok,推荐 @RequiredArgsConstructor,否则推荐手写构造器注入。


评论