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

JAVA设计模式:依赖倒转原则(DIP)在Spring框架中的实践体现

文章目录

  • 一、DIP原则深度解析
    • 1.1 核心定义
    • 1.2 现实比喻
  • 二、Spring中的DIP实现机制
    • 2.1 传统实现 vs Spring实现对比
  • 三、Spring中DIP的完整示例
    • 3.1 领域模型定义
    • 3.2 具体实现
    • 3.3 高层业务类
      • 3.4 配置类
  • 四、Spring实现DIP的关键技术
    • 4.1 依赖注入方式对比
    • 4.2 自动装配注解
  • 五、DIP在Spring中的实践建议
  • 六、典型应用场景
    • 6.1 数据库切换
    • 6.2 多支付渠道
  • 七、常见误区及规避
  • 八、Spring Boot中的最佳实践
  • 九、总结

在这里插入图片描述

一、DIP原则深度解析

1.1 核心定义

依赖倒转原则(Dependency Inversion Principle) :高层模块不应该依赖低层模块,二者都应该依赖抽象 。抽象不应该依赖细节,细节应该依赖抽象。

1.2 现实比喻

想象一家智能家居系统:

  • 高层模块:智能家居控制中心(业务逻辑)

  • 低层模块:具体设备(灯泡、空调、摄像头)

  • 抽象接口:统一设备控制协议

  • 控制中心通过标准协议控制设备,无需关心具体设备型号。新增设备只需实现协议接口,无需修改控制中心代码。

二、Spring中的DIP实现机制

Spring框架通过两大核心功能实现DIP:

  1. 控制反转(IoC):将对象创建权交给容器
  2. 依赖注入(DI):通过构造函数/Setter/字段注入依赖

2.1 传统实现 vs Spring实现对比

// 传统方式(违反DIP)
public class OrderService {private MySQLOrderDao orderDao = new MySQLOrderDao(); // 直接依赖具体实现public void createOrder() {orderDao.save();}
}// Spring实现(符合DIP)
@Service
public class OrderService {private final OrderRepository orderRepository; // 依赖抽象接口@Autowiredpublic OrderService(OrderRepository orderRepository) {this.orderRepository = orderRepository;}
}@Repository
public class JpaOrderRepository implements OrderRepository {// 实现接口
}

三、Spring中DIP的完整示例

3.1 领域模型定义

// 抽象层
public interface PaymentGateway {void processPayment(BigDecimal amount);
}public interface NotificationService {void sendNotification(String message);
}

3.2 具体实现

// 支付实现
@Component("alipay")
public class AlipayGateway implements PaymentGateway {@Overridepublic void processPayment(BigDecimal amount) {System.out.println("支付宝支付:" + amount);}
}// 通知实现 
@Component
public class EmailNotification implements NotificationService {@Overridepublic void sendNotification(String message) {System.out.println("发送邮件:" + message);}
}

3.3 高层业务类

@Service
public class OrderProcessingService {private final PaymentGateway paymentGateway;private final NotificationService notificationService;@Autowiredpublic OrderProcessingService(@Qualifier("alipay") PaymentGateway paymentGateway,NotificationService notificationService) {this.paymentGateway = paymentGateway;this.notificationService = notificationService;}public void completeOrder(Order order) {paymentGateway.processPayment(order.getTotal());notificationService.sendNotification("订单完成,金额:" + order.getTotal());}
}

3.4 配置类

@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {// 可通过@Bean配置更复杂的依赖关系
}

四、Spring实现DIP的关键技术

4.1 依赖注入方式对比

注入方式代码示例适用场景
构造器注入@Autowired OrderService(OrderRepository repo)推荐首选方式,保证不可变
Setter注入@Autowired void setRepo(OrderRepository repo)可选依赖
字段注入@Autowired private OrderRepository repo;不推荐,测试困难

4.2 自动装配注解

  • @Autowired:按类型自动装配
  • @Qualifier:指定具体实现bean
  • @Primary:设置优先注入的bean

五、DIP在Spring中的实践建议

  1. 接口先行 :先定义抽象接口,再实现具体类

    // 先定义仓库接口
    public interface UserRepository {User findById(Long id);void save(User user);
    }// 再实现JPA版本
    @Repository
    public class JpaUserRepository implements UserRepository {// 具体实现
    }
    
  2. 善用Profile配置 :不同环境切换实现类

    @Profile("dev")
    @Service
    public class MockPaymentService implements PaymentService {}@Profile("prod")
    @Service
    public class RealPaymentService implements PaymentService {}
    
  3. 循环依赖处理:使用Setter注入或@Lazy注解打破循环依赖

    @Service
    public class ServiceA {private final ServiceB serviceB;@Autowiredpublic ServiceA(@Lazy ServiceB serviceB) {this.serviceB = serviceB;}
    }
    

六、典型应用场景

6.1 数据库切换

// 通用仓库接口
public interface ProductRepository extends JpaRepository<Product, Long> {}// MySQL实现
@Profile("mysql")
@Repository
public interface MysqlProductRepository extends ProductRepository {}// MongoDB实现 
@Profile("mongodb")
@Repository
public interface MongoProductRepository extends ProductRepository {}

6.2 多支付渠道

@Component
public class PaymentGatewayRouter {private final Map<String, PaymentGateway> gateways;@Autowiredpublic PaymentGatewayRouter(List<PaymentGateway> gatewayList) {this.gateways = gatewayList.stream().collect(Collectors.toMap(g -> g.getClass().getAnnotation(GatewayType.class).value(),Function.identity()));}public PaymentGateway getGateway(String type) {return gateways.get(type);}
}

七、常见误区及规避

  1. 过度抽象:为每个类都创建接口 -> 仅在确实需要多种实现时创建接口
  2. 忽略单实现:单个实现类也要通过接口注入 -> 保持架构一致性
  3. 滥用@Autowired:在工具类中直接注入 -> 优先使用构造函数注入
  4. 循环依赖:A依赖B,B又依赖A -> 使用@Lazy或重构代码结构

八、Spring Boot中的最佳实践

// 自动配置示例
@Configuration
@ConditionalOnClass(DataSource.class)
public class DatabaseAutoConfiguration {@Bean@ConditionalOnProperty(name = "db.type", havingValue = "mysql")public DataSource mysqlDataSource() {return new MysqlDataSource();}@Bean@ConditionalOnProperty(name = "db.type", havingValue = "postgres")public DataSource postgresDataSource() {return new PostgresDataSource();}
}
  • 通过Spring Boot的条件注解,实现不同环境自动装配不同的实现类,完美体现DIP原则。

九、总结

在Spring框架中实践DIP原则的关键:
✅ 通过接口定义抽象契约
✅ 利用依赖注入解耦组件
✅ 善用Spring的自动装配机制
✅ 保持适度的抽象层级
✅ 结合设计模式增强扩展性

掌握这些技巧,Spring应用将具备:

  • 更高的可测试性
  • 更好的可维护性
  • 更强的扩展能力
  • 更清晰的架构分层

  • 记住:依赖倒转不是目标,而是实现软件高质量设计的手段。在实际开发中,要平衡原则与实践需求,避免陷入过度设计的陷阱。

相关文章:

JAVA设计模式:依赖倒转原则(DIP)在Spring框架中的实践体现

文章目录 一、DIP原则深度解析1.1 核心定义1.2 现实比喻 二、Spring中的DIP实现机制2.1 传统实现 vs Spring实现对比 三、Spring中DIP的完整示例3.1 领域模型定义3.2 具体实现3.3 高层业务类3.4 配置类 四、Spring实现DIP的关键技术4.1 依赖注入方式对比4.2 自动装配注解 五、D…...

基于微信小程序的健身管理系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…...

Spring Boot是什么及其优点

简介 Spring Boot是基于Spring框架开发的全新框架&#xff0c;其设计目的是简化Spring应用的初始化搭建和开发过程。 Spring Boot整合了许多框架和第三方库配置&#xff0c;几乎可以达到“开箱即用”。 优点 可快速构建独立的Spring应用。 直接嵌入Tomcat、Jetty和Underto…...

Docker 系列之 docker-compose 容器编排详解

文章目录 前言一、Docker-compose简介二、Docker-compose 的安装三、Docker-compose卸载四、Docker-compose常用命令4.1 Docker-compose命令格式4.2 docker-compose up4.3 docker-compose ps4.4 docker-compose stop4.5 docker-compose -h4.6 docker-compose down4.7 docker-co…...

【机器学习】深入探索SVM:支持向量机的原理与应用

目录 &#x1f354; SVM引入 1.1什么是SVM? 1.2支持向量机分类 1.3 线性可分、线性和非线性的区分 &#x1f354; 小结 学习目标 知道SVM的概念 &#x1f354; SVM引入 1.1什么是SVM? 看一个故事&#xff0c;故事是这样子的&#xff1a; 在很久以前的情人节&#xf…...

输入带空格的字符串,求单词个数

输入带空格的字符串&#xff0c;求单词个数 __ueooe_eui_sjje__ ---->3syue__jdjd____die_ ---->3shuue__dju__kk ---->3 #include <stdio.h> #include <string.h>// 自定义函数来判断字符是否为空白字符 int isSpace(char c) {return c || c \t || …...

[STM32 - 野火] - - - 固件库学习笔记 - - -十二.基本定时器

一、定时器简介 STM32 中的定时器&#xff08;TIM&#xff0c;Timer&#xff09;是其最重要的外设之一&#xff0c;广泛用于时间管理、事件计数和控制等应用。 1.1 基本功能 定时功能&#xff1a;TIM定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中…...

kaggle比赛入门 - House Prices - Advanced Regression Techniques(第二部分)

本文承接上一篇 1. 分析住宅类型&#xff08;BldgType&#xff09;的分布以及它们与销售价格&#xff08;SalePrice&#xff09;的关系 # 1. distribution of dwelling types and their relation to sale prices # BldgType: Type of dwellingdwelling_types df[BldgType].v…...

数字图像处理:实验六

uu们&#xff01;大家好&#xff0c;2025年的新年就要到来&#xff0c;咸鱼哥在这里祝大家在2025年每天开心快乐&#xff0c;天天挣大钱&#xff0c;自由自在&#xff0c;健健康康&#xff0c;万事如意&#xff01;&#xff08;要是咸鱼哥嘴笨的话&#xff0c;还望大家多多包涵…...

C++——list的了解和使用

目录 引言 forward_list与list 标准库中的list 一、list的常用接口 1.list的迭代器 2.list的初始化 3.list的容量操作 4.list的访问操作 5.list的修改操作 6.list的其他操作 二、list与vector的对比 结束语 引言 本篇博客要介绍的是STL中的list。 求点赞收藏评论…...

移动光猫怎么自己改桥接模式?

环境&#xff1a; 型号H3-8s 问题描述&#xff1a; 家里宽带用的是H3-8s 光猫&#xff0c;想改桥接模式。 解决方案&#xff1a; 1.默认管理员账号和密码&#xff1a; 账号&#xff1a;CMCCAdmin 密码&#xff1a;aDm8H%MdAWEB页面我试了登陆不了&#xff0c;显示错误 …...

jupyter配置说明

使用以下命令修改jupyter的配置文件参数&#xff1a; vim /root/.jupyter/jupyter_lab_config.py #这里填写远程访问的IP名&#xff0c;填*则默认是主机IP名 c.ServerApp.ip * # 这里的密码填写上面生成的密钥 c.ServerApp.password ************************************…...

MiniMax-01中Lightning Attention的由来(线性注意力进化史)

目录 引言原始注意力线性注意力因果模型存在的问题累加求和操作的限制Lightning AttentionLightning Attention-1Lightning Attention-2 备注 引言 MiniMax-01: Scaling Foundation Models with Lightning Attention表明自己是第一个将线性注意力应用到如此大规模的模型&#…...

Vue中的动态组件是什么?如何动态切换组件?

什么是动态组件&#xff1f; 动态组件是 Vue.js 中的一项强大功能&#xff0c;它允许开发者根据程序的状态或用户的操作&#xff0c;动态地切换组件。动态组件的优势在于&#xff0c;开发者可以根据具体需求灵活地渲染不同的组件&#xff0c;从而提高应用的通用性和可维护性。…...

Day33:字符串的切片

在 Python 中&#xff0c;**切片&#xff08;Slicing&#xff09;**是对字符串&#xff08;以及其他序列类型&#xff0c;如列表、元组等&#xff09;进行提取部分内容的强大工具。通过切片&#xff0c;你可以非常方便地提取字符串的子字符串、倒序字符串&#xff0c;甚至进行步…...

汽车网络信息安全-ISO/SAE 21434解析(中)

目录 第七章-分布式网络安全活动 1. 供应商能力评估 2. 报价 3. 网络安全职责界定 第八章-持续的网络安全活动 1. 网路安全监控 2. 网络安全事件评估 3. 漏洞分析 4. 漏洞管理 第九章-概念阶段 1. 对象定义 2. 网路安全目标 3. 网络安全概念 第十章 - 产品开发 第十…...

rust feature h和 workspace相关知识 (十一)

feature 相关作用和描述 在 Rust 中&#xff0c;features&#xff08;特性&#xff09; 是一种控制可选功能和依赖的机制。它允许你在编译时根据不同的需求启用或禁用某些功能&#xff0c;优化构建&#xff0c;甚至改变代码的行为。Rust 的特性使得你可以轻松地为库提供不同的…...

从规则到神经网络:机器翻译技术的演进与未来展望

从规则到神经网络:机器翻译技术的演进与未来展望 引言 还记得早些年用翻译软件翻译一句简单的英文句子,却发现翻译结果让人啼笑皆非的日子吗?从“我喜欢吃苹果”被翻译成“我喜欢吃苹果电脑”,到今天的神经网络机器翻译(Neural Machine Translation, NMT)能够生成语义流…...

LLaMA-Factory 微调LLaMA3

LoRA介绍 LoRA&#xff08;Low-Rank Adaptation&#xff09;是一种用于大模型微调的技术&#xff0c; 通过引入低秩矩阵来减少微调时的参数量。在预训练的模型中&#xff0c; LoRA通过添加两个小矩阵B和A来近似原始的大矩阵ΔW&#xff0c;从而减 少需要更新的参数数量。具体来…...

Debian或Ubuntu系统中重置MySQL的root密码

你提供的步骤是针对在Debian或Ubuntu系统中重置MySQL的root密码的过程。以下是对你提供的步骤的详细说明和补充&#xff1a; 步骤 1.1 - 1.3&#xff1a;进入MySQL配置目录并使用debian-sys-maint账户登录MySQL # 进入MySQL配置目录 cd /etc/mysql/ # 使用vim编辑器打开debia…...

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.17 时间魔法:处理千万级时间序列的秘籍

1.17 时间魔法&#xff1a;处理千万级时间序列的秘籍 目录 #mermaid-svg-fa6SvjKCpmJ6C2BY {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-fa6SvjKCpmJ6C2BY .error-icon{fill:#552222;}#mermaid-svg-fa6SvjKCpmJ6…...

WPS数据分析000009

一、函数与数据透视表统计数据时效率差异 函数 F4绝对引用 数据透视表 二、数据透视表基础操作 数据透视表&#xff1a;一个快速的生成报表的工具 显示详细信息 方式一; 方式二&#xff1a; 移动数据透视表 删除数据透视表 复制粘贴数据透视表 留足空间&#xff0c;否则拖动字…...

Ansible自动化运维实战--script、unarchive和shell模块(6/8)

文章目录 一、script模块1.1、功能1.2、常用参数1.3、举例 二、unarchive模块2.1、功能2.2、常用参数2.3、举例 三、shell模块3.1、功能3.2、常用参数3.3、举例 一、script模块 1.1、功能 Ansible 的 script 模块允许你在远程主机上运行本地的脚本文件&#xff0c;其提供了一…...

K8S 快速实战

K8S 核心架构原理: 我们已经知道了 K8S 的核心功能:自动化运维管理多个容器化程序。那么 K8S 怎么做到的呢?这里,我们从宏观架构上来学习 K8S 的设计思想。首先看下图: K8S 是属于主从设备模型(Master-Slave 架构),即有 Master 节点负责核心的调度、管理和运维,Slave…...

用Python和PyQt5打造一个股票涨幅统计工具

在当今的金融市场中&#xff0c;股票数据的实时获取和分析是投资者和金融从业者的核心需求之一。无论是个人投资者还是专业机构&#xff0c;都需要一个高效的工具来帮助他们快速获取股票数据并进行分析。本文将带你一步步用Python和PyQt5打造一个股票涨幅统计工具&#xff0c;不…...

linux naive代理设置

naive linux客户端 Release v132.0.6834.79-2 klzgrad/naiveproxy GitHub Client setup Run ./naive with the following config.json to get a SOCKS5 proxy at local port 1080. {"listen": "socks://127.0.0.1:1080","proxy": "htt…...

猿人学第一题 js混淆源码乱码

首先检查刷新网络可知&#xff0c;m参数被加密&#xff0c;这是一个ajax请求 那么我们直接去定位该路径 定位成功 观察堆栈之后可以分析出来这应该是一个混淆&#xff0c;我们放到解码平台去还原一下 window["url"] "/api/match/1";request function…...

【学术会议征稿】第五届能源、电力与先进热力系统学术会议(EPATS 2025)

能源、电力与先进热力系统设计是指结合物理理论、工程技术和计算机模拟&#xff0c;对能源转换、利用和传输过程进行设计的学科领域。它涵盖了从能源的生产到最终的利用整个流程&#xff0c;旨在提高能源利用效率&#xff0c;减少能源消耗和环境污染。 重要信息 官网&#xf…...

对神经网络基础的理解

目录 一、《python神经网络编程》 二、一些粗浅的认识 1&#xff09; 神经网络也是一种拟合 2&#xff09;神经网络不是真的大脑 3&#xff09;网络构建需要反复迭代 三、数字图像识别的实现思路 1&#xff09;建立一个神经网络类 2&#xff09;权重更新的具体实现 3&am…...

.strip()用法

.strip("") 是 Python 字符串方法 strip() 的一个用法&#xff0c;它会去除字符串两端指定字符集中的字符。 基本语法&#xff1a; string.strip([chars])string: 这是你要操作的字符串。chars: 可选参数&#xff0c;表示你想要去除的字符集&#xff08;默认为空格…...