【Spring】Spring启示录
目录
前言
一、示例程序
二、OCP开闭原则
三、依赖倒置原则DIP
四、控制反转IOC
总结
前言
在软件开发的世界里,随着项目的增长和需求的变化,如何保持代码的灵活性、可维护性和扩展性成为了每个开发者必须面对的问题。传统的面向过程或基于类的设计往往导致代码高度耦合,使得任何小的改动都需要对整个系统进行大规模的测试和调整。这不仅耗费时间,也增加了出错的风险。因此,理解并应用一些基本的设计原则,如开闭原则(OCP)和依赖倒置原则(DIP),对于构建高效、灵活的应用程序至关重要。本文将通过一个简单的用户登录示例,探讨如何从传统设计模式过渡到使用Spring框架实现控制反转(IoC)来解决这些问题,帮助开发者更好地理解和实践这些重要的编程理念。
一、示例程序
当没有Spring时,我们是怎么开发的呢?我们先阅读下面的程序:
// 控制层
public class UserController {private UserService userService = new UserServiceImpl();public void login(){String username = "admin";String password = "123456";boolean success = userService.login(username, password);if (success) {// 登录成功} else {// 登录失败}}
}// 业务层
public class UserServiceImpl implements UserService {private UserDao userDao = new UserDaoImplForMySQL();public boolean login(String username, String password) {User user = userDao.selectByUsernameAndPassword(username, password);if (user != null) {return true;}return false;}
}// 数据库层
public class UserDaoImplForMySQL implements UserDao {public User selectByUsernameAndPassword(String username, String password) {// 连接MySQL数据库,根据用户名和密码查询用户信息return null;}
}
可以看出,UserDaoImplForMySQL中主要是连接MySQL数据库进行操作。如果更换到Oracle数据库上,则需要再提供一个UserDaoImplForOracle。很明显,以上的操作正在进行功能的扩展,添加了一个新的类UserDaoImplForOracle来应付数据库的变化,这里的变化会引起连锁反应吗?当然会,如果想要切换到Oracle数据库上,UserServiceImpl类代码就需要修改。
二、OCP开闭原则
上面的代码以及修改违背了OCP开闭原则,开闭原则是这样说的:在软件开发过程中应当对扩展开放,对修改关闭。也就是说,如果在进行功能扩展的时候,添加额外的类是没问题的,但因为功能扩展而修改之前运行正常的程序,这是忌讳的,不被允许的。因为一旦修改之前运行正常的程序,就会导致项目整体要进行全方位的重新测试。这是相当麻烦的过程。导致以上问题的主要原因是:代码和代码之间的耦合度太高。如下图所示:
更多软件设计原则:【设计模式】软件设计原则-CSDN博客
可以很明显的看出,上层是依赖下层的。UserController依赖UserServiceImpl,而UserServiceImpl依赖UserDaoImplForMySQL,这样就会导致下面只要改动,上面必然会受牵连(跟着也会改),所谓牵一发而动全身。这样也就同时违背了另一个开发原则:依赖倒置原则。
三、依赖倒置原则DIP
依赖倒置原则(Dependence Inversion Principle),简称DIP,主要倡导面向抽象编程,面向接口编程,不要面向具体编程,让上层不再依赖下层,下面改动了,上面的代码不会受到牵连。这样可以大大降低程序的耦合度,耦合度低了,扩展力就强了,同时代码复用性也会增强(软件七大开发原则都是在为解耦合服务)。你可能会说,上面的代码已经面向接口编程了呀:
确实已经面向接口编程了,但对象的创建是:new UserDaoImplForOracle()显然并没有完全面向接口编程,还是使用到了具体的接口实现类。什么叫做完全面向接口编程?请看以下代码:
如果代码是这样编写的,才算是完全面向接口编程。那你可能会问,这样userDao是null,在执行的时候就会出现空指针异常呀。你说的有道理,确实是这样的,所以我们要解决这个问题。解决空指针异常的问题,其实就是解决两个核心的问题:
- 谁来负责对象的创建。【也就是说谁来:new UserDaoImplForOracle()/new UserDaoImplForMySQL()】
- 谁来负责把创建的对象赋到这个属性上。【也就是说谁来把上面创建的对象赋给userDao属性】
如果我们把以上两个核心问题解决了,就可以做到既符合OCP开闭原则,又符合依赖倒置原则。很荣幸的通知你:Spring框架可以做到。在Spring框架中,它可以帮助我们new对象,并且它还可以将new出来的对象赋到属性上。换句话说,Spring框架可以帮助我们创建对象,并且可以帮助我们维护对象和对象之间的关系。比如:
Spring可以new出来UserDaoImplForMySQL对象,也可以new出来UserDaoImplForOracle对象,并且还可以让new出来的dao对象和service对象产生关系(产生关系其实本质上就是给属性赋值)。很显然,这种方式是将对象的创建权/管理权交出去了,不再使用硬编码的方式了。同时也把对象关系的管理权交出去了,也不再使用硬编码的方式了。像这种把对象的创建权交出去,把对象关系的管理权交出去,被称为控制反转。
四、控制反转IOC
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计思想,可以用来降低代码之间的耦合度,符合依赖倒置原则。控制反转的核心是:将对象的创建权交出去,将对象和对象之间关系的管理权交出去,由第三方容器来负责创建与维护。控制反转常见的实现方式:依赖注入(Dependency Injection,简称DI)。通常,依赖注入的实现又包括两种方式:
- set方法注入
- 构造方法注入
Spring框架就是一个实现了IoC思想的框架。IoC可以认为是一种全新的设计模式,但是理论和时间成熟相对较晚,并没有包含在GoF中(GoF指的是23种设计模式)。
总结
通过对一个简单用户登录系统的分析,我们了解到了在没有采用现代框架时开发可能面临的挑战:高耦合度的代码难以维护和扩展。遵循开闭原则(OCP)和依赖倒置原则(DIP),可以有效地降低这种耦合,提高代码的复用性和可扩展性。然而,手动管理对象创建和依赖关系仍然复杂且容易出错。Spring框架通过引入控制反转(IoC)概念,提供了一种优雅的解决方案。它不仅简化了对象的创建和依赖注入过程,还促进了更清晰、模块化的代码结构,极大地提升了开发效率和软件质量。掌握这些设计原则与技术框架的应用,可以帮助开发者构建更加健壮、灵活和易于维护的软件系统。
相关文章:

【Spring】Spring启示录
目录 前言 一、示例程序 二、OCP开闭原则 三、依赖倒置原则DIP 四、控制反转IOC 总结 前言 在软件开发的世界里,随着项目的增长和需求的变化,如何保持代码的灵活性、可维护性和扩展性成为了每个开发者必须面对的问题。传统的面向过程或基于类的设计…...

ospf动态路由配置,cost路径调整,ospf认证实验
一、实验拓扑如图: 接口ip配置网络 :10.17.12.* 10.17.13.* ,10.17.23.* 回环接口配置分别为 10.0.1.1 ,10.0.1.2,10.0.1.3对应三台路由器 ar1配置接口ip interface GigabitEthernet0/0/0 ip address 10.17.12.1…...
在Rust应用中访问.ini格式的配置文件
在Rust应用中访问.ini格式的配置文件,你可以使用第三方库,比如 ini 或 config. 下面是一个使用 ini 库的示例,该库允许你读取和解析.ini文件。 使用 ini 库 添加依赖 首先,你需要在你的 Cargo.toml 文件中添加 ini 库的依赖&am…...
批量处理多个模型的预测任务
#!/bin/bash# 检查是否传入必要的参数,若未传入参数则打印用法并退出 if [ "$#" -lt 1 ]; thenecho "用法: $0 <file_path>"echo "示例: $0 /home/aistudio/work/PaddleSeg/city/cityscapes_urls_extracted.txt"exit 1 fi# 读取…...
Java 编程初体验
Java学习资料 Java学习资料 Java学习资料 一、引言 在当今数字化的时代,编程已然成为一项极具价值的技能。而 Java 作为一门广泛应用于企业级开发、移动应用、大数据等众多领域的编程语言,吸引着无数初学者投身其中。当我们初次踏入 Java 编程的世界&…...
element-plus 的table section如何实现单选
如果是单选那么全新的按钮应该隐藏或者不可编辑的状态。但是我没找到改变成不可编辑的方法,只能采取隐藏 <template><!-- 注意要包一层div根元素,否则css样式可能会不生效,原因不详 --><div><el-table ref"proTab…...

【JavaEE进阶】图书管理系统 - 壹
目录 🌲序言 🌴前端代码的引入 🎋约定前后端交互接口 🚩接口定义 🍃后端服务器代码实现 🚩登录接口 🚩图书列表接口 🎄前端代码实现 🚩登录页面 🚩…...
牛客周赛 Round 77 题解
文章目录 A-时间表B-数独数组D-隐匿社交网络E-1or0 A-时间表 签到题 #include <bits/stdc.h> using namespace std;int main() {int a[6] {20250121,20250123,20250126,20250206,20250208,20250211};int n; cin >> n;cout << a[n - 1];return 0; }B-数独数…...

Mybatis配置文件详解
MyBatis通过XML或注解的方式将Java对象与数据库中的记录进行映射,极大地简化了数据访问层的开发。而在MyBatis的核心组成部分中,配置文件扮演着举足轻重的角色。它不仅定义了MyBatis的运行环境,还配置了数据源、事务管理、映射器等关键元素&a…...
《深度揭秘:TPU张量计算架构如何重塑深度学习运算》
在深度学习领域,计算性能始终是推动技术发展的关键因素。从传统CPU到GPU,再到如今大放异彩的TPU(张量处理单元),每一次硬件架构的革新都为深度学习带来了质的飞跃。今天,就让我们深入探讨TPU的张量计算架构…...
Java基础知识总结(二十二)--List接口
List本身是Collection接口的子接口,具备了Collection的所有方法。现在学习List体系特有的共性方法,查阅方法发现List的特有方法都有索引,这是该集合最大的特点。 List:有序(元素存入集合的顺序和取出的顺序一致),元素都…...

[STM32 - 野火] - - - 固件库学习笔记 - - -十二.基本定时器
一、定时器简介 STM32 中的定时器(TIM,Timer)是其最重要的外设之一,广泛用于时间管理、事件计数和控制等应用。 1.1 基本功能 定时功能:TIM定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中…...
算法随笔_27:最大宽度坡
上一篇:算法随笔_26: 按奇偶排序数组-CSDN博客 题目描述如下: 给定一个整数数组 nums,坡是元组 (i, j),其中 i < j 且 nums[i] < nums[j]。这样的坡的宽度为 j - i。 找出 nums 中的坡的最大宽度,如果不存在,返回 0 。 …...

无公网IP 外网访问本地部署 llamafile 大语言模型
llamafile 是一种AI大模型部署(或者说运行)的方案,它的特点就是可以将模型和运行环境打包成一个独立的可执行文件,这样就简化了部署流程。用户只需要下载并执行该文件,无需安装运行环境或依赖库,这大大提高…...

使用PC版本剪映制作照片MV
目录 制作MV模板时长调整拖动边缘缩短法分割删除法变速法整体调整法 制作MV 导入音乐 导入歌词 点击歌词 和片头可以修改字体: 还可以给字幕添加动画效果: 导入照片,自动创建照片轨: 修改片头字幕:增加两条字幕轨&…...

搭建 docxify 静态博客教程
首先,安装 node 环境安装 docxify ,参考官网:https://docsify.js.org/#/zh-cn/ npm i docsify-cli -g新建docs文件夹专门用来放文章,初始化命令 docsify init ./docs就会生成如下两个文件,index.html 入口文件&#…...

汽车OEMs一般出于什么目的来自定义Autosar CP一些内容
汽车OEMs在使用AUTOSAR CP(Classic Platform)协议时,可能会根据自身的特定需求对标准协议进行修改,形成自己的企业标准(企标)。这种修改通常是为了满足特定的硬件平台、功能需求、安全要求或优化性能。以下是一些常见的修改场景和例子: 1. 硬件平台适配 企业可能会根据…...
Vue.js Vuex 模块化管理
Vue.js Vuex 模块化管理 今天咱们来聊聊如何在 Vuex 中进行模块化管理。当你的 Vue.js 应用变得越来越庞大时,单一的状态管理可能会让人头疼。这时候,Vuex 的模块化功能就派上用场了。 为什么需要模块化? 想象一下,如果把所有的…...

分布式光纤应变监测是一种高精度、分布式的监测技术
一、土木工程领域 桥梁结构健康监测 主跨应变监测:在大跨度桥梁的主跨部分,如悬索桥的主缆、斜拉桥的斜拉索和主梁,分布式光纤应变传感器可以沿着这些关键结构部件进行铺设。通过实时监测应变情况,能够精确捕捉到车辆荷载、风荷…...
用Devc++与easyx一步一步做游戏[启动界面部分]-解决hover闪烁问题及优化
在之前的博文中《用Devc与easyx一步一步做游戏[启动界面部分]-之按钮制作》,我们利用Devc和easyx完成了游戏启动界面按钮的基本制作,实现了按钮的绘制以及鼠标悬停时的信息提示功能。然而,目前还存在一个问题,即鼠标移动时&#x…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向
在人工智能技术呈指数级发展的当下,大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性,吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型,成为释放其巨大潜力的关键所在&…...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...