目录

为什么spring不建议使用基于字段的依赖注入呢?

知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!


Spring 不推荐使用基于字段的依赖注入(Field Injection),主要有以下几个原因:

1. 破坏不可变性(Immutability)

  • 字段注入无法声明 final 字段
    构造器注入允许将依赖字段声明为 final,确保对象一旦创建,依赖关系不可变。而字段注入的字段必须是可变的(非 final),这在设计上降低了安全性和线程安全性。

    java 复制代码
    // 构造器注入:依赖不可变
    private final UserService userService;
    public UserController(UserService userService) {
        this.userService = userService;
    }
    
    // 字段注入:依赖可变(非 final)
    @Autowired
    private UserService userService;

2. 隐藏依赖关系,降低代码可读性

  • 依赖不透明
    字段注入的依赖关系分散在类的各个字段中,难以通过类的公共接口(如构造函数或 Setter 方法)直观看到所有依赖。而构造器注入通过参数列表明确声明所有必需依赖,代码更易维护。

    java 复制代码
    // 构造器注入:依赖关系一目了然
    public OrderService(PaymentService paymentService, InventoryService inventoryService) {
        // ...
    }
    
    // 字段注入:需遍历类中的 @Autowired 字段才能发现依赖
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private InventoryService inventoryService;

3. 增加与容器的耦合性

  • 无法脱离 Spring 容器创建对象
    字段注入依赖反射机制,若直接通过 new 关键字实例化对象,依赖不会被自动注入,导致对象处于不完整状态。而构造器注入允许手动传入依赖,脱离容器也能正常工作。

    java 复制代码
    // 构造器注入:可手动传入依赖
    UserService userService = new UserServiceImpl();
    UserController controller = new UserController(userService);  // 有效
    
    // 字段注入:直接 new 对象时依赖为 null
    UserController controller = new UserController();  // userService = null

4. 测试困难

  • 依赖注入需要容器或反射
    单元测试时,字段注入的对象需要通过反射(如 ReflectionTestUtils)或依赖 Spring 容器(如 @SpringBootTest)才能注入 Mock 对象。构造器注入则可通过普通 Java 代码直接传递 Mock 依赖。

    java 复制代码
    // 构造器注入:直接传递 Mock 对象
    @Test
    void testController() {
        UserService mockService = Mockito.mock(UserService.class);
        UserController controller = new UserController(mockService);
        // ...
    }
    
    // 字段注入:需通过反射设置私有字段
    @Test
    void testController() {
        UserController controller = new UserController();
        ReflectionTestUtils.setField(controller, "userService", Mockito.mock(UserService.class));
        // ...
    }

5. 循环依赖问题

  • 字段注入可能掩盖设计缺陷
    构造器注入在存在循环依赖时会直接抛出 BeanCurrentlyInCreationException,迫使开发者重新设计代码以消除循环依赖。而字段注入(或 Setter 注入)允许 Spring 通过三级缓存解决循环依赖,可能导致开发者忽略代码设计问题。

    java 复制代码
    // 构造器注入:直接暴露循环依赖
    // 抛出异常:Requested bean is currently in creation
    public class A {
        public A(B b) { /* ... */ }
    }
    public class B {
        public B(A a) { /* ... */ }
    }
    
    // 字段注入:Spring 通过三级缓存解决循环依赖(但设计不合理)
    public class A {
        @Autowired private B b;
    }
    public class B {
        @Autowired private A a;
    }

6. 违反单一职责原则

  • 过多的字段注入可能掩盖类的臃肿
    构造器注入的参数列表过长时,会直观提示类可能承担了过多职责(如超过 3-4 个依赖),促使开发者重构代码。而字段注入的分散注解可能掩盖这一问题。

官方建议与演进趋势

  • Spring 官方推荐
    自 Spring Framework 4.x 起,官方文档推荐优先使用 构造器注入 (强制依赖)和 Setter 注入(可选依赖),明确表示字段注入是"不推荐的"(less favored)。

  • Spring Boot 的隐式支持
    从 Spring Boot 2.x 开始,如果类仅有一个构造器,Spring 会默认选择它进行依赖注入,无需显式添加 @Autowired,进一步鼓励构造器注入。

    java 复制代码
    // Spring Boot 自动注入唯一构造器
    public class UserController {
        private final UserService userService;
        public UserController(UserService userService) {  // 自动注入
            this.userService = userService;
        }
    }

适用场景与例外

尽管字段注入存在诸多问题,但在以下场景中可能被容忍:

  1. 原型验证或快速开发
    快速编写临时代码时,字段注入的简洁性可能优先于设计规范。
  2. 框架或库的兼容性
    某些框架(如 JPA 的 @PersistenceContext)可能强制使用字段注入。
  3. 遗留代码维护
    修改旧代码时,若重构成本过高,可暂时保留字段注入。

总结

依赖注入方式 优点 缺点 适用场景
构造器注入 依赖不可变、代码透明、易测试 参数较多时代码冗长 强制依赖、核心业务逻辑
Setter 注入 支持可选依赖、灵活性高 无法保证依赖完整性 可选依赖、动态配置更新
字段注入 代码简洁 隐藏依赖、破坏不可变性、测试困难 临时代码、特定框架需求
本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
Tracy-225 小时前
啥是Spring,有什么用,既然收费,如何免费创建SpringBoot项目,依赖下载不下来的解决方法,解决99%问题!
java·spring
未定义.2215 小时前
Java设计模式实战:策略模式在SimUDuck问题中的应用
java·设计模式·策略模式
_一条咸鱼_6 小时前
大厂Android面试秘籍:Activity 结果回调处理(八)
android·面试·android jetpack
_一条咸鱼_6 小时前
大厂Android面试秘籍:Activity 与 Fragment 交互(九)
android·面试·android jetpack
码熔burning7 小时前
【NIO番外篇】之组件 Channel
java·nio·channel
东阳马生架构7 小时前
Sentinel源码—1.使用演示和简介
后端
卡尔曼的BD SLAMer7 小时前
问题 | 针对SSM(Spring + Spring MVC + MyBatis)框架的去Spring MVC强化版学习路线
java·spring·mvc·mybatis
zhuyasen8 小时前
首个与AI深度融合的Go开发框架sponge,解决Cursor/Trae等工具项目级开发痛点
后端·低代码·go
春生野草8 小时前
0413-多态、Object类方法、访问权限修饰符、装箱拆箱、128陷阱
java·开发语言
拉不动的猪8 小时前
设计模式之------命令模式
前端·javascript·面试