Java学习随笔:Spring 的依赖注入到底解决了什么问题(2026-09-21)
刚开始学 Spring 的时候,“控制反转”和“依赖注入”这两个词让我困惑了很久。字面意思都认识,但合在一起就不知道在说什么。直到我自己用普通 Java 写了一段代码,再对比 Spring 的写法,才慢慢体会到它们想解决的问题其实很朴素:对象之间的依赖关系,究竟应该由谁来负责建立。
在不用 Spring 的写法里,如果一个类需要另一个类的服务,通常会在内部直接创建出一个实例。这种写法看起来直接,问题却不少:调用方和被调用方被紧紧地绑在一起,一旦被调用方换了实现、改了构造方式,所有直接创建它的地方都得跟着改;如果这个依赖还有自己的依赖,层层嵌套下去,构造一个对象就会变成一件很麻烦的事;更别说在测试的时候,想把真实实现换成替身,也几乎无从下手。
依赖注入的思路是把这件事反过来:类不再自己创建依赖,而是声明“我需要什么”,由外部的容器负责把合适的实例送进来。注入的方式主要有三种:构造器注入、Setter 注入和字段注入。构造器注入把依赖写在构造方法参数里,对象一旦创建,依赖就是完整的,而且字段可以声明为 final,天然保证了不可变,也更安全,所以我更愿意优先选择它。字段注入写起来最省事,直接在字段上加注解就行,但它把依赖藏了起来,脱离了容器就很难使用,测试和排查都更麻烦。
这样做带来的好处是明显的。首先,类与类之间从“硬编码的创建关系”变成了“基于接口的协作关系”,替换实现只需要改动配置或者装配的地方,调用方的代码可以保持不动。其次,对象的创建和依赖的装配被集中到容器里,谁依赖谁一目了然,方便统一管理生命周期。最后,测试时可以把依赖替换成 mock 或者桩对象,让单元测试不再依赖真实环境。
依赖注入还引出了 Bean 的作用域和生命周期这些概念。默认情况下,容器里的对象是单例的,整个应用里只有一份实例,被所有需要它的地方共享。这在大多数无状态的服务里是合适的,但如果这个对象会保存与某次请求相关的数据,单例就会带来并发问题,这时候要么换成原型作用域,要么干脆不把它做成共享的组件。理解了这一点,也就明白了为什么 Spring 里被注入的组件通常都被要求写成无状态的。
当然,依赖注入也不是越多越好。如果只是为了注入而注入,把一个简单的工具类也交给容器管理,代码反而会变得绕。判断标准其实很简单:这个依赖是否需要在不同环境下替换、是否带有状态、是否需要被多个地方共享。如果答案都是否,直接创建一个对象往往更清楚。
这次梳理让我意识到,很多框架概念之所以难懂,是因为我们跳过了它要解决的问题,直接去记它的用法。回过头看,“控制反转”说的并不是什么高深的技巧,只是把创建对象的控制权从业务代码手里交出去,让它们各司其职而已。

浙公网安备 33010602011771号