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

基于Spring框架的分层解耦详解

  • 博客主页:誓则盟约
  • 系列专栏:Java Web
  • 关注博主,后期持续更新系列文章
  • 如果有错误感谢请大家批评指出,及时修改
  • 感谢大家点赞👍收藏⭐评论✍ 

Java Web 三层架构:

Java Web可以大致被分为三层架构:

  • controller:控制层
  • service:业务逻辑层
  • dao:数据访问层

Controller层:

        controller层也称为控制层,只要功能是接收前端发送的请求,对请求进行处理,并响应数据。

        作为应用程序的入口点之一,Controller 层负责接收来自客户端(如浏览器、移动设备等)的 HTTP 请求。这些请求可以是 GET、POST、PUT、DELETE 等不同类型的请求,例如,在 Web 应用中,当用户在浏览器中输入一个 URL 或者提交一个表单时,请求会被发送到相应的 Controller。

        它会解析请求中的参数,如查询字符串参数(对于 GET 请求)或者表单数据(对于 POST 请求)。例如,在一个登录功能中,Controller 会获取用户输入的用户名和密码参数。

注:Controller 层并不直接实现业务逻辑,而是调用 Service 层(业务逻辑层)的方法来处理业务。

Service层:

        service层也称为业务逻辑层,在 Java 应用架构(特别是遵循 MVC 或类似分层架构的应用)中,Service 层(业务逻辑层)扮演着核心的角色,其中主要是处理具体的业务逻辑。

        Service 层包含了应用的复杂且核心的业务逻辑。例如,在一个银行系统中,转账业务逻辑就会在 Service 层实现。它需要考虑诸如账户余额检查、转账金额的合法性、更新相关账户余额等操作。

        除此之外,Service通常还负责管理事务。在涉及多个数据库操作的业务场景中,如订单处理(包括创建订单、扣减库存、更新用户积分等多个数据库操作),Service 层确保这些操作要么全部成功(提交事务),要么全部失败(回滚事务)。

        在架构中Service 层为 Controller 层提供业务处理方法。Controller 层调用 Service 层的方法来完成具体的业务操作。

Dao层:

        Dao(Data Access Object)层也称为数据访问层,主要负责数据访问操作,包括数据的增删改查。

        Dao 层的主要目的是将数据库操作抽象出来,为上层(通常是 Service 层)提供统一的访问数据库的接口。这样,上层业务逻辑层不需要关心具体的数据库类型(如 MySQL、Oracle 等)以及数据库的底层操作细节(如 SQL 语句的编写、数据库连接的管理等)。例如,在一个企业级应用中,无论是使用关系型数据库还是非关系型数据库,Service 层只需要调用 Dao 层提供的方法(如findUserByIdsaveUser等)就可以实现对数据的操作。

        除此之外,Dao层还负责执行数据的持久化操作,包括将数据保存到数据库(插入操作)、从数据库中查询数据、更新数据库中的数据以及删除数据库中的数据等操作。因此,Dao层也被称为持久层。


分层解耦:

首先要知道内聚和耦合两个概念:

  • 内聚:软件中各个功能模块内部的功能联系。
  • 耦合:衡量软件中各个层/模块之间的依赖、关联的程度。

         在开发过程中,我们要朝着高内聚低耦合的方向实施,最好可以让层与层之间解除耦合,让他们不产生依赖,这样我们程序的灵活性和可扩展性会更佳。

        下面我们以Spring框架为例讲解如何实现解耦操作。在 Spring 框架中,解耦主要通过控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)的机制来实现。

控制反转(IOC):

        在没有使用 Spring 等框架进行解耦之前,对象之间的依赖关系通常是由对象自己创建和管理的。例如,在一个业务逻辑类中,如果需要调用数据访问层的方法来获取数据,它可能会直接实例化数据访问层的对象。

   public class BusinessLogicClass {public void doSomeBusinessLogic() {DataAccessClass dataAccess = new DataAccessClass();// 使用 dataAccess 对象进行数据操作}}

        这种方式存在的问题是,业务逻辑类与数据访问类紧密耦合,当数据访问层的实现发生变化时,业务逻辑类也需要进行相应的修改。

        而Spring 框架引入了控制反转的概念,即对象的创建和依赖关系的管理不再由对象自己负责,而是交给一个外部的容器(通常是 Spring 容器)来管理。而在IOC容器中创建、管理的对象,称之为bean

        这样在 Spring 中,对象只需要声明自己所需要的依赖,而不需要关心这些依赖是如何创建和初始化的。例如:

   public class BusinessLogicClass {private DataAccessInterface dataAccess;public BusinessLogicClass(DataAccessInterface dataAccess) {this.dataAccess = dataAccess;}public void doSomeBusinessLogic() {// 使用 dataAccess 对象进行数据操作}}

        这里,BusinessLogicClass不再自己创建DataAccessClass的实例,而是通过构造函数接收一个实现了DataAccessInterface接口的对象。这样,BusinessLogicClass只依赖于接口,而不依赖于具体的实现类,实现了解耦。

依赖注入(DI):

        容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。这些资源一般取自Spring容器中的bean对象。

        Spring 框架通过依赖注入的方式来实现控制反转。依赖注入有多种方式,常见的有构造函数注入、Setter 注入和字段注入。

        构造函数注入:在对象创建时,通过构造函数将依赖对象传递进去。例如上面的例子中,BusinessLogicClass通过构造函数接收DataAccessInterface的实现对象。

        Setter 注入:通过设置方法将依赖对象注入到对象中。

        字段注入:使用注解(如@Autowired)直接在字段上进行依赖注入。例如:

   import org.springframework.beans.factory.annotation.Autowired;public class BusinessLogicClass {@Autowiredprivate DataAccessInterface dataAccess;public void doSomeBusinessLogic() {// 使用 dataAccess 对象进行数据操作}}

解耦的好处:

1.可维护性

        由于对象之间的依赖关系通过接口进行解耦,当一个模块的实现发生变化时,只需要修改对应的实现类,而不会影响到其他模块。例如,如果数据访问层的实现从使用一种数据库切换到另一种数据库,只需要修改数据访问层的实现类和 Spring 配置文件,业务逻辑层不需要做任何修改。

2.可测试性

        解耦后的代码更容易进行单元测试。在测试业务逻辑类时,可以通过注入模拟的依赖对象(如使用模拟框架创建的模拟数据访问对象)来隔离其他模块的影响,只专注于测试业务逻辑本身。

3.可扩展性

        当需要添加新的功能模块时,可以很容易地将新模块集成到系统中,只需要在 Spring 配置文件中定义新模块的 bean,并将其注入到需要的地方即可。例如,添加一个新的业务逻辑模块,只需要在配置文件中定义新的 bean,并将其注入到现有的业务逻辑类中,实现功能的扩展。


欲买桂花同载酒,终不似、少年游。—— 《唐多令·芦叶满汀洲》

相关文章:

  • 状态模式原理剖析
  • HTML基础用法介绍二
  • 计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23
  • 单体到微服务架构服务演化过程
  • 《深度学习》【项目】OpenCV 发票识别 透视变换、轮廓检测解析及案例解析
  • Cocos 3.8.3 实现外描边效果(逃课玩法)
  • Apache Iceberg 概述
  • MobaXterm基本使用 -- 服务器状态、批量操作、显示/切换中文字体、修复zsh按键失灵
  • 精通推荐算法32:行为序列建模总结
  • 亚马逊爆款三明治封口器发明专利维权,恐涉及大量卖家,速查
  • 探索 Python CacheControl 库:AI 领域的新利器
  • springboot引入netty
  • NLP:BERT的介绍
  • Android Studio Dolphin 中Gradle下载慢的解决方法
  • Python中的机器学习:从入门到实战
  • #Java异常处理
  • 【面试系列】之二:关于js原型
  • Cumulo 的 ClojureScript 模块已经成型
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • JavaScript实现分页效果
  • JS函数式编程 数组部分风格 ES6版
  • js作用域和this的理解
  • React-redux的原理以及使用
  • Redash本地开发环境搭建
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • windows-nginx-https-本地配置
  • 成为一名优秀的Developer的书单
  • 码农张的Bug人生 - 见面之礼
  • 普通函数和构造函数的区别
  • 如何使用 JavaScript 解析 URL
  • 温故知新之javascript面向对象
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • 用Python写一份独特的元宵节祝福
  • Java性能优化之JVM GC(垃圾回收机制)
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • #我与Java虚拟机的故事#连载06:收获颇多的经典之作
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (C语言)fread与fwrite详解
  • (八十八)VFL语言初步 - 实现布局
  • (办公)springboot配置aop处理请求.
  • (接上一篇)前端弄一个变量实现点击次数在前端页面实时更新
  • (转)Oracle 9i 数据库设计指引全集(1)
  • (转)视频码率,帧率和分辨率的联系与区别
  • (转)淘淘商城系列——使用Spring来管理Redis单机版和集群版
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .mysql secret在哪_MYSQL基本操作(上)
  • .Net 6.0--通用帮助类--FileHelper
  • .NET 简介:跨平台、开源、高性能的开发平台
  • .NET 药厂业务系统 CPU爆高分析
  • .NET 中让 Task 支持带超时的异步等待
  • .NET开源项目介绍及资源推荐:数据持久层
  • .NET是什么
  • @Async注解的坑,小心
  • [ 隧道技术 ] cpolar 工具详解之将内网端口映射到公网