在刚开始学习 Java 开发时,我们通常会这样创建对象:
UserService userService = new UserService();
OrderService orderService = new OrderService(userService);
这样的方式简单直观,但在实际开发中,尤其是复杂的企业级项目中,这种“手动 new”的方式很快会暴露出种种问题。
而 Spring 框架的 IoC(控制反转)容器 正是为了解决这些问题而诞生的。
本文将通过代码示例和场景分析,讲述为什么我们要让 Spring 来管理 Bean,而不是自己 new。
一、从一个简单例子说起
假设我们有一个电商系统中的订单模块:
public class OrderService {
private final UserService userService = new UserService();
private final PaymentService paymentService = new PaymentService();
public void createOrder() {
userService.validateUser();
paymentService.pay();
}
}
这段代码看似没问题,但已经埋下了“灾难”的种子。
二、手动 new 的五大问题
1. 强耦合 —— 改动一个类,牵一发动全身
OrderService 直接在内部 new 了 UserService 和 PaymentService,这意味着它强依赖于具体实现类。
一旦我们希望使用新的支付方式:
public class PaymentServiceV2 extends PaymentService { ... }
想切换到新版本,就得改源码:
private final PaymentService paymentService = new PaymentServiceV2(); // 改这里
这违反了 开闭原则(OCP):软件应该对扩展开放,对修改关闭。
2. 无法单元测试 —— Mock 失效
如果要对 OrderService 做单元测试,你可能希望模拟依赖:
PaymentService mockPaymentService = Mockito.mock(PaymentService.class);
OrderService orderService = new OrderService(userService, mockPaymentService);
但如果 OrderService 内部自己 new:
private final PaymentService paymentService = new PaymentService();
那你根本无法替换掉这个实例。
结果是:测试会真的调起支付接口 ❌
Mock 彻底失效,测试不可控。
3. AOP 失效 —— 无法被 Spring 增强
假设 PaymentService 上有事务注解:
@Service
@Transactional
public class PaymentService { ... }
当你在 OrderService 中自己 new 时:
private final PaymentService paymentService = new PaymentService();
此时这个对象不是由 Spring 创建的,也就不会被 Spring AOP 代理。
事务、日志、限流、缓存等增强功能都会失效。
而如果是通过 Spring 注入:
@Autowired
private PaymentService paymentService;
Spring 会生成代理对象,AOP 才能生效 ✅。
4. 生命周期不可控
Spring 容器会统一管理 Bean 的生命周期:
- 创建时机(单例/多例)
- 销毁顺序
- 初始化钩子(@PostConstruct)
但如果你自己 new:
- Spring 不知道它的存在;
- 也不会帮你清理、监控或追踪它;
- 依赖的资源可能泄露。
5. 难以扩展、维护成本高
有同学说可以通过构造函数传入依赖类的方式解决问题1和2,但这已经是依赖式注入(DI)的思想了,只是没有进行控制反转(IoC)。(下面会介绍依赖式注入和控制反转)
但随着项目复杂度上升,类与类之间的依赖会越来越多。
如果所有依赖都手动 new,构造函数会变得臃肿:
OrderService orderService = new OrderService(
new UserService(
new UserDao(new DataSource(...))
),
new PaymentService(
new PaymentDao(new DataSource(...))
)
);
而如果交给 Spring,只需:
@Autowired
private UserService userService;
@Autowired
private PaymentService paymentService;
Spring 自动解析依赖关系,代码清爽、可维护。
三、Spring 是如何解决这些问题的?
Spring 的核心思想之一是 IoC(Inversion of Control,控制反转)。
所谓控制反转,就是把对象创建与依赖关系的管理交由容器完成,而不是在内部显式 new 出来。把“谁来控制对象的创建”从程序员反转给框架
而 依赖注入(Dependency Injection, DI) 是实现 IoC 的主要方式 —— 容器在创建对象时,会自动将其所需依赖“注入”进去,让组件之间解耦。具体实现方式是“对象不自己创建依赖,而由外部注入”,例如@Autowired、构造函数注入等。
也就是说:
- 以前我们手动 new;
- 现在由 Spring 容器统一创建、管理和注入依赖。
四、总结
| 维度 | 手动 new | Spring IoC 管理 |
|---|---|---|
| 对象创建 | 程序员手动 | 容器自动 |
| 依赖注入 | 硬编码 | 自动装配 |
| 可测试性 | 差 | 强 |
| 可扩展性 | 差 | 强 |
| 生命周期 | 程序员控制 | 容器管理 |
| AOP 支持 | 无 | 有 |
| 耦合度 | 高 | 低 |
🧩 一句话总结
Spring 管理 Bean 的本质,不是为了“少写几行代码”,
而是为了让系统具备解耦、扩展、测试、演化的能力。