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

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

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

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

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

java 复制代码
   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)直接在字段上进行依赖注入。例如:

java 复制代码
   import org.springframework.beans.factory.annotation.Autowired;

   public class BusinessLogicClass {
       @Autowired
       private DataAccessInterface dataAccess;

       public void doSomeBusinessLogic() {
           // 使用 dataAccess 对象进行数据操作
       }
   }

解耦的好处:

1.可维护性

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

2.可测试性

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

3.可扩展性

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


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

相关推荐
AI人工智能+电脑小能手16 分钟前
【大白话说Java面试题】【Java基础篇】第30题:JDK动态代理和CGLIB动态代理有什么区别
java·开发语言·后端·面试·代理模式
swipe27 分钟前
别再把 AI 聊天做成纯文本:从 agui 这个前后端项目,拆解“可感知工具调用”的流式 AI UI
后端·langchain·llm
GetcharZp28 分钟前
GitHub 爆火!纯 Go 编写的文件同步神器 Syncthing,凭什么成为程序员的标配?
后端
hERS EOUS32 分钟前
SpringBoot 使用 spring.profiles.active 来区分不同环境配置
spring boot·后端·spring
DFT计算杂谈38 分钟前
wannier90 参数详解大全
java·前端·css·html·css3
LucianaiB43 分钟前
我用飞书多维表做了一个 AI 活动推荐智能体:每天自动催我别错过截止日期!
后端
marsh02061 小时前
43 openclaw熔断与降级:保障系统在异常情况下的可用性
java·运维·网络·ai·编程·技术
张健11564096481 小时前
临界区和同一线程上锁
java·开发语言·jvm
铁皮饭盒2 小时前
第2课:5分钟!用 Trae AI 生成你的第一个后端服务(Bunjs + Elysia)
前端·后端·全栈
超梦dasgg2 小时前
智慧充电系统设备管理服务对外接口实现方案
java·spring·微服务