依赖注入到底难不难配
很多人第一次接触依赖注入(Dependency Injection,简称 DI)时,听到这个词总觉得高大上,心里打鼓:这玩意儿是不是得懂设计模式、背一堆概念才能用?其实没那么玄乎。就像你去咖啡店点单,不用自己带杯子、奶和咖啡豆,服务员全给你准备好端上来——依赖注入就是这个“端上来”的过程。
从一个常见场景说起
假设你在写一个用户注册功能,需要发邮件通知。最简单的写法是在注册逻辑里直接 new 一个 EmailService:
class UserService {
public void register(String email) {
EmailService emailService = new EmailService();
emailService.send(email, "欢迎注册");
// 其他逻辑...
}
}
问题来了:如果哪天你想换成发短信,或者测试时不想真发邮件,就得改代码。耦合太紧,扩展起来头疼。
换种方式:把“服务”交出去
这时候依赖注入就派上用场了。你不自己创建 EmailService,而是让外部“塞”进来:
class UserService {
private EmailService emailService;
public UserService(EmailService emailService) {
this.emailService = emailService;
}
public void register(String email) {
emailService.send(email, "欢迎注册");
}
}
你看,UserService 不再关心 EmailService 怎么来,只管用就行。谁来创建?交给框架或者启动类去管。
配置真的复杂吗
有人说“配置一堆 XML 或注解太麻烦”。确实,早些年 Spring 用 XML 配置 DI,动辄几百行,看着吓人。但现在主流都是注解驱动,简洁多了。
比如在 Spring Boot 里,你只需要加个 @Service 和 @Autowired:
@Service
class EmailService {
public void send(String to, String msg) {
// 发送逻辑
}
}
@Service
class UserService {
@Autowired
private EmailService emailService;
public void register(String email) {
emailService.send(email, "欢迎注册");
}
}
框架启动时自动扫描、创建对象、完成注入。几乎不用手动写配置。如果你用的是 .NET Core 或者 NestJS,也都是类似风格,约定大于配置。
什么时候会觉得“复杂”
如果你项目小,就几个类,硬编码也能搞定,那引入 DI 确实有点杀鸡用牛刀。但当项目变大,模块多了,测试频繁了,你会发现手动管理依赖越来越累。
另一个容易觉得“复杂”的点是循环依赖。比如 A 依赖 B,B 又依赖 A。框架虽然能处理一部分,但最好还是从设计上避免。这不是 DI 本身的问题,而是代码结构需要优化。
实际体验:用熟了反而省事
很多开发者一开始抵触,是因为不熟悉。一旦习惯了,反而觉得手动 new 才麻烦。尤其写单元测试时,你可以轻松 mock 一个假的 EmailService 塞进去,不用改一行业务代码。
而且现代框架基本都默认开启 DI,像 Spring Boot、ASP.NET Core、NestJS,你甚至感觉不到“配置”的存在。它就像空气,平时不注意,没了才觉得憋得慌。