当前位置: 首页 > news >正文

依赖注入的逻辑基于Java语言

对于一个厨师,要做一道菜。传统的做法是:你需要什么食材,就自己去菜市场买什么。这意味着你必须知道去哪个菜市场、怎么挑选食材、怎么讨价还价等等。你不仅要会做菜,还要会买菜,职责变得复杂了。

而依赖注入就像是有一个贴心的助手,他会提前把你需要的所有食材准备好,直接送到你手上。你只需要专心做菜就行了,不用操心食材从哪里来、怎么来的。

技术层面的深入理解

在软件开发中,**依赖注入(Dependency Injection,简称DI)**是一种设计模式,它的核心思想是:不要让对象自己创建它所依赖的其他对象,而是由外部容器来创建并注入这些依赖对象

传统方式下,如果类A需要使用类B的功能,类A会在内部直接创建类B的实例。这就像厨师自己去买菜一样,造成了紧耦合。依赖注入则是让外部的"容器"来创建类B的实例,然后"注入"给类A使用。

为什么需要依赖注入?

  1. 降低耦合度:类不再负责创建它的依赖对象,只需要声明需要什么依赖
  2. 提高可测试性:可以轻松地注入模拟对象进行单元测试
  3. 增强灵活性:可以在运行时动态地改变依赖关系
  4. 符合开闭原则:对扩展开放,对修改关闭

依赖注入的三种主要方式

  1. 构造器注入:通过构造函数参数注入依赖
  2. Setter注入:通过setter方法注入依赖
  3. 接口注入:通过实现特定接口来注入依赖

Java代码示例演示

第一步:没有依赖注入的传统方式

// 数据库服务类
class DatabaseService {public void save(String data) {System.out.println("保存数据到数据库: " + data);}
}// 邮件服务类
class EmailService {public void sendEmail(String message) {System.out.println("发送邮件: " + message);}
}// 用户服务类 - 传统方式(紧耦合)
class UserService {private DatabaseService databaseService;private EmailService emailService;public UserService() {// 直接在内部创建依赖对象 - 这就是紧耦合的问题所在this.databaseService = new DatabaseService();this.emailService = new EmailService();}public void registerUser(String username) {// 保存用户信息databaseService.save("用户: " + username);// 发送欢迎邮件emailService.sendEmail("欢迎 " + username + " 注册我们的系统!");}
}

问题分析:

  • UserService直接创建了DatabaseService和EmailService的实例
  • 如果要换成其他类型的数据库或邮件服务,必须修改UserService的代码
  • 难以进行单元测试,因为无法模拟这些依赖对象

第二步:使用依赖注入重构

首先定义接口,实现依赖倒置:

// 定义数据库服务接口
interface DatabaseServiceInterface {void save(String data);
}// 定义邮件服务接口
interface EmailServiceInterface {void sendEmail(String message);
}// MySQL数据库实现
class MySQLDatabaseService implements DatabaseServiceInterface {@Overridepublic void save(String data) {System.out.println("保存数据到MySQL数据库: " + data);}
}// MongoDB数据库实现
class MongoDatabaseService implements DatabaseServiceInterface {@Overridepublic void save(String data) {System.out.println("保存数据到MongoDB: " + data);}
}// SMTP邮件服务实现
class SMTPEmailService implements EmailServiceInterface {@Overridepublic void sendEmail(String message) {System.out.println("通过SMTP发送邮件: " + message);}
}// 短信服务实现(也可以发送通知)
class SMSService implements EmailServiceInterface {@Overridepublic void sendEmail(String message) {System.out.println("发送短信通知: " + message);}
}

第三步:构造器注入方式

class UserService {private final DatabaseServiceInterface databaseService;private final EmailServiceInterface emailService;// 通过构造器注入依赖public UserService(DatabaseServiceInterface databaseService, EmailServiceInterface emailService) {this.databaseService = databaseService;this.emailService = emailService;}public void registerUser(String username) {databaseService.save("用户: " + username);emailService.sendEmail("欢迎 " + username + " 注册我们的系统!");}
}

第四步:Setter注入方式

class UserServiceWithSetter {private DatabaseServiceInterface databaseService;private EmailServiceInterface emailService;// 通过setter方法注入依赖public void setDatabaseService(DatabaseServiceInterface databaseService) {this.databaseService = databaseService;}public void setEmailService(EmailServiceInterface emailService) {this.emailService = emailService;}public void registerUser(String username) {if (databaseService == null || emailService == null) {throw new IllegalStateException("依赖服务未正确注入!");}databaseService.save("用户: " + username);emailService.sendEmail("欢迎 " + username + " 注册我们的系统!");}
}

第五步:简单的依赖注入容器

import java.util.HashMap;
import java.util.Map;// 简单的依赖注入容器
class DIContainer {private Map<Class<?>, Object> services = new HashMap<>();// 注册服务public <T> void register(Class<T> serviceType, T implementation) {services.put(serviceType, implementation);}// 获取服务@SuppressWarnings("unchecked")public <T> T getService(Class<T> serviceType) {return (T) services.get(serviceType);}// 创建UserService实例,自动注入依赖public UserService createUserService() {DatabaseServiceInterface dbService = getService(DatabaseServiceInterface.class);EmailServiceInterface emailService = getService(EmailServiceInterface.class);if (dbService == null || emailService == null) {throw new IllegalStateException("必要的服务未注册到容器中!");}return new UserService(dbService, emailService);}
}

第六步:完整的使用示例

public class DependencyInjectionDemo {public static void main(String[] args) {// 创建依赖注入容器DIContainer container = new DIContainer();// 注册具体的实现到容器中container.register(DatabaseServiceInterface.class, new MySQLDatabaseService());container.register(EmailServiceInterface.class, new SMTPEmailService());// 通过容器创建UserService,依赖会自动注入UserService userService = container.createUserService();// 使用服务userService.registerUser("张三");System.out.println("\n--- 切换到不同的实现 ---");// 可以轻松切换到不同的实现container.register(DatabaseServiceInterface.class, new MongoDatabaseService());container.register(EmailServiceInterface.class, new SMSService());UserService userService2 = container.createUserService();userService2.registerUser("李四");System.out.println("\n--- 演示Setter注入 ---");// 演示Setter注入UserServiceWithSetter userServiceSetter = new UserServiceWithSetter();userServiceSetter.setDatabaseService(new MySQLDatabaseService());userServiceSetter.setEmailService(new SMTPEmailService());userServiceSetter.registerUser("王五");}
}

第七步:单元测试的便利性

// 模拟对象用于测试
class MockDatabaseService implements DatabaseServiceInterface {public boolean saveCalled = false;public String savedData = null;@Overridepublic void save(String data) {saveCalled = true;savedData = data;System.out.println("模拟保存: " + data);}
}class MockEmailService implements EmailServiceInterface {public boolean emailSent = false;public String sentMessage = null;@Overridepublic void sendEmail(String message) {emailSent = true;sentMessage = message;System.out.println("模拟发送邮件: " + message);}
}// 简单的测试示例
class UserServiceTest {public static void testUserRegistration() {// 创建模拟对象MockDatabaseService mockDB = new MockDatabaseService();MockEmailService mockEmail = new MockEmailService();// 注入模拟对象UserService userService = new UserService(mockDB, mockEmail);// 执行测试userService.registerUser("测试用户");// 验证结果System.out.println("数据库保存被调用: " + mockDB.saveCalled);System.out.println("保存的数据: " + mockDB.savedData);System.out.println("邮件发送被调用: " + mockEmail.emailSent);System.out.println("发送的邮件: " + mockEmail.sentMessage);}public static void main(String[] args) {System.out.println("=== 单元测试演示 ===");testUserRegistration();}
}

总结

依赖注入就像是一个智能的"服务管家",它负责管理和协调各个对象之间的依赖关系。通过这种方式:

  1. 代码更加灵活:可以轻松地切换不同的实现
  2. 测试更加容易:可以注入模拟对象进行隔离测试
  3. 维护更加简单:修改依赖关系不需要改动核心业务逻辑
  4. 扩展更加方便:添加新功能只需要实现接口并注册到容器
http://www.lryc.cn/news/586004.html

相关文章:

  • 【第五节】部署http接口到ubuntu server上的docker内
  • Eplan API Scripts
  • Transforms
  • Spring Boot 整合 OAuth2 详细教程(适用于 2025 年 Spring Boot 3.x)
  • 力扣-19. 删除链表的倒数第N个节点
  • 什么是 Bootloader?怎么把它移植到 STM32 上?
  • 【6.1.3 漫画分布式锁】
  • 线程属性设置全攻略
  • 14. 请谈一下浏览器的强缓存和协商缓存
  • 9.2 埃尔米特矩阵和酉矩阵
  • Pandas 模块之数据的读取
  • arcgis投影后数据显示问题记录
  • 非程序员如何用 AI 提升日常工作效率:以产品经理为例的落地实践指南
  • error while loading shared libraries
  • 小架构step系列12:单元测试
  • [爬虫实战] 多进程/多线程/协程-异步爬取豆瓣Top250
  • Pytest 跳过测试技巧:灵活控制哪些测试该跑、哪些该跳过
  • linux系统mysql性能优化
  • H2在springboot的单元测试中的应用
  • 多 Agent 强化学习实践指南(一):CTDE PPO 在合作捕食者-猎物游戏中的应用详解
  • 引入了模块但没有使用”,会不会被打包进去
  • 【C++小白逆袭】内存管理从崩溃到精通的秘籍
  • c++反射实现
  • 张量数值计算
  • 跨系统开发代码换行符如何解决
  • 每日一SQL 【销售分析 III】
  • 试用了10款翻译软件后,我只推荐这一款!完全免费还超好用
  • 大模型KV缓存量化误差补偿机制:提升推理效率的关键技术
  • Qt6中出现 OpenCV(4.10.0) Error: Assertion failed
  • 第10讲——一元函数积分学的几何应用