高层模块指承担业务逻辑、策略决策的部分(如orderservice),底层模块指具体实现细节的部分(如mysqluserrepository);关键看调用关系与契约定义权,而非文件位置或工具属性。

什么是依赖倒置原则里的“高层模块”和“底层模块”
“高层模块”不是指代码写在 Main.java 里就叫高层,而是指承担业务逻辑、策略决策、流程编排的部分——比如 OrderService 处理下单流程;“底层模块”也不是指工具类,而是具体干活的实现:比如 MySQLUserRepository 或 RedisCache。关键看谁调用谁、谁决定行为契约。
常见错误现象:OrderService 直接 new MySQLUserRepository(),或者 import 了 com.mysql.cj.jdbc.Driver;这说明高层在编译期就绑死了底层技术细节,一换数据库就得改业务代码。
- 判断依据:如果删掉某个
.jar(比如spring-boot-starter-data-redis),你的PaymentService编译报错,那它大概率违反了依赖倒置 - 真正解耦点不在运行时(Spring Bean 注入只是手段),而在编译依赖图:高层模块的
pom.xml不该声明任何数据库/消息队列/HTTP 客户端的依赖 - 接口定义位置很重要——
UserRepository接口必须和OrderService在同一个模块(或领域包),不能放在infrastructure模块里由底层实现反向定义
怎么让 OrderService 不依赖 MySQLUserRepository
核心动作是把“谁来做”交给接口,把“怎么做”推给实现类,且接口由高层模块定义。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 在
domain或application模块中定义UserRepository接口,只包含findById(Long id)、save(User user)这类业务语义方法 -
OrderService构造函数接收UserRepository(不是其实现类),内部只调用接口方法 -
MySQLUserRepository放在infrastructure模块,实现UserRepository,并负责 JDBC 细节、SQL 拼接、连接管理 - Spring 配置里用
@Bean或@ConditionalOnClass把实现注入进去——但OrderService的源码里不能出现任何 MySQL 相关 import
典型错误:在 UserRepository 接口里加 findByNameLike(String pattern),结果发现只有 MySQL 支持 LIKE,MongoDB 实现不了。这就是接口被底层技术反向污染了。
为什么用 interface 而不是 abstract class 或 @FunctionalInterface
不是语法限制,是职责边界问题。依赖倒置要隔离的是“能力契约”,不是“共享行为”。
-
interface强制实现类只承诺能力,不继承状态或默认逻辑;而abstract class容易塞进 JDBC 连接池配置、日志模板等基础设施代码,导致高层模块间接依赖底层细节 -
@FunctionalInterface只能表达单一行为(如Function<user string></user>),但UserRepository是一组协作方法(查+存+删),需要明确的命名契约,而不是匿名函数流 - Java 8+ 允许
interface有default方法,但别滥用——一旦在接口里写default日志逻辑或重试封装,就等于把底层横切关注点泄漏到高层契约中
Spring Boot 项目里最容易漏掉的依赖倒置破口
自动配置掩盖了很多编译依赖问题。你以为解耦了,其实只是 Spring 帮你延迟加载了。
-
application.properties里写spring.datasource.url=...看似无害,但如果OrderService的单元测试想用内存 H2 数据库,就必须引入h2依赖——这说明测试代码被迫感知底层存储选型 - 使用
Lombok的@Data在实体类上,结果User类里出现@Column(name = "user_name"),等于把 JPA 映射细节泄露到领域模型 - 在
OrderService里直接调用RabbitTemplate.convertAndSend(...),哪怕用了@Autowired RabbitTemplate,也违反了——应该定义EventPublisher接口,由基础设施模块提供 RabbitMQ 实现
最隐蔽的坑:当所有实现类都只有一个(比如目前只用 MySQL),开发者会下意识把接口当成“为未来扩展留的空壳”,然后在接口方法签名里悄悄加上 Connection conn 参数——以为“反正以后可以改”,但这时编译依赖已经形成,重构成本远超预期。










