java开发MVC三层架构上再加一层Manager层原理详解

 更新时间:2021年10月27日 15:50:41   作者:飘渺Jam  
这篇文章主要为大家介绍了MVC三层架构中再加一层Manager层原理的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步

MVC三层架构

我们在刚刚成为程序员的时候,就会被前辈们 “教育” 说系统的设计要遵循 MVC(Model-View-Controller)架构。它将整体的系统分成了 Model(模型),View(视图)和 Controller(控制器)三个层次,也就是将用户视图和业务处理隔离开,并且通过控制器连接起来,很好地实现了表现和逻辑的解耦,是一种标准的软件分层架构。

MVC分层架构

MVC分层架构是架构上最简单的一种分层方式。为了遵循这种分层架构我们在构建项目时往往会建立这样三个目录:controller、service 和 dao,它们分别对应了表现层、逻辑层还有数据访问层。

三层架构示意图

每层的作用如下:

Controller层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理。

Service层:主要是处理业务逻辑和事务

Dao层:负责与底层数据库MySQL,Oracle等进行数据交互

可是随着我们的业务逻辑越来复杂,代码写的越来越多,这种简单的三层架构的问题也越来越明显。

MVC架构弊端

传统的MVC分层有以下几个很明显的问题:

Service层代码臃肿

Service层很容易出现大事务,事务嵌套,导致问题很多,而且极难排查

dao层参杂业务逻辑

dao层sql语句复杂,关联查询比较多

为了解决这个问题,我们参考《alibaba java开发手册》,在Service层之下再独立出一个通用业务处理层(Manager层)

manager层

在这个分层架构中主要增加了 Manager 层,它与 Service 层的关系是:Manager 层提供原子的服务接口,Service 层负责依据业务逻辑来编排原子接口。

Manager层的特征

在《alibaba java开发手册》中是这样描述Manager层的:

Manager 层:通用业务处理层,它有如下特征:

对第三方平台封装的层,预处理返回结果及转化异常信息,适配上层接口;对 Service 层通用能力的下沉,如缓存方案、中间件通用处理;与 DAO 层交互,对多个 DAO 的组合复用。

在实际开发中我们可以这样使用Manager层

复杂业务,service提供数据给Manager层,负责业务编排,然后把事务下沉到Manager层,Manager层不允许相互调用,不会出现事务嵌套。

专注于不带业务sql语言,也可以在manager层进行通用业务的dao层封装。

避免复杂的join查询,数据库压力比java大很多,所以要严格控制好sql,所以可以在manager层进行拆分,比如复杂查询。

当然对于简单的业务,可以不使用Manager层。

Manager层使用案例

这里我们举个例子说明一下Manager层的使用场景:

假设你有一个用户系统,他有一个获取用户信息的接口,它调用逻辑Service层的 getUser 方法,getUser方法又和 User DB 交互获取数据。如下图左边展示部分。

这时,产品提出一个需求,在 APP 中展示用户信息的时候,如果用户不存在,那么要自动给用户创建一个用户。同时,要做一个 HTML5 的页面,HTML5 页面要保留之前的逻辑,也就是不需要创建用户。

图片

此时按照传统的三层架构,逻辑层的边界就变得不清晰,表现层也承担了一部分的业务逻辑,因为我们往往会在表现层Controller中增加业务逻辑处理,将获取用户和创建用户接口编排起来。

而添加Manager层以后,Manager 层提供创建用户和获取用户信息的接口,而 Service 层负责将这两个接口组装起来。这样就把原先散布在表现层的业务逻辑都统一到了 Service 层,每一层的边界就非常清晰了。

接下来我们看一段实际代码说明一下Service层与Manager层如何进行区分?

@Transactional(rollbackFor = Throwable.class)
public Result<String> upOrDown(Long departmentId, Long swapId) {
  // 验证 1
  DepartmentEntity departmentEntity = departmentDao.selectById(departmentId);
  if (departmentEntity == null) {
    return Result.error("部门xxx不存在");
  }
  // 验证 2
  DepartmentEntity swapEntity = departmentDao.selectById(swapId);
  if (swapEntity == null) {
    return Result.error("部门xxx不存在");
  }
  // 验证 3
  Long count = employeeDao.countByDepartmentId(departmentId);
  if (count != null && count > 0) {
    return Result.error("员工不存在");
  }
  // 操作数据库 4
  Long departmentSort = departmentEntity.getSort();
  departmentEntity.setSort(swapEntity.getSort());
  departmentDao.updateById(departmentEntity);
  swapEntity.setSort(departmentSort);
  departmentDao.updateById(swapEntity);
  return Result.OK("success");
}

上面代码在我们在我们采用三层架构时经常会遇到,那么它有什么问题呢?

上面的代码是典型的长事务问题(类似的还有调用第三方接口),前三步都是使用 connection 进行验证操作,但是由于方法上有@Transactional 注解,所以这三个验证都是使用的同一个 connection。

若对于复杂业务、复杂的验证逻辑,会导致整个验证过程始终占用该 connection 连接,占用时间可能会很长,直至方法结束,connection 才会交还给数据库连接池。

对于复杂业务的不可预计的情况,长时间占用同一个 connection 连接不是好的事情,应该尽量缩短占用时间。

说明:对于@Transactional 注解,当 spring 遇到该注解时,会自动从数据库连接池中获取 connection,并开启事务然后绑定到 ThreadLocal 上,如果业务并没有进入到最终的 操作数据库环节,那么就没有必要获取连接并开启事务,应该直接将 connection 返回给数据库连接池,供其他使用。

所以我们在加入Manager层以后可以这样写:

DepartmentService.java 
public Result<String> upOrDown(Long departmentId, Long swapId) {
  // 验证 1
  DepartmentEntity departmentEntity = departmentDao.selectById(departmentId);
  if (departmentEntity == null) {
    return Result.error("部门xxx不存在");
  }
  // 验证 2
  DepartmentEntity swapEntity = departmentDao.selectById(swapId);
  if (swapEntity == null) {
    return Result.error("部门xxx不存在");
  }
  // 验证 3
  Long count = employeeDao.countByDepartmentId(departmentId);
  if (count != null && count > 0) {
    return Result.error("员工不存在");
  }
  // 操作数据库 4
  departmentManager.upOrDown(departmentSort,swapEntity);
  return Result.OK("success");
}
DepartmentManager.java
  
@Transactional(rollbackFor = Throwable.class)
public void upOrDown(DepartmentEntity departmentEntity ,DepartmentEntity swapEntity){
  Long departmentSort = departmentEntity.getSort();
  departmentEntity.setSort(swapEntity.getSort());
  departmentDao.updateById(departmentEntity);
  swapEntity.setSort(departmentSort);
  departmentDao.updateById(swapEntity);
}

将数据在 service 层准备好,然后传递给 manager 层,由 manager 层添加 @Transactional事务注解进行数据库操作。

以上就是MVC三层架构上再加一层Manager层原理详解的详细内容,更多关于MVC架构Manager层原理的资料请关注脚本之家其它相关文章!

相关文章

  • 程序包org.springframework.boot不存在的问题解决

    程序包org.springframework.boot不存在的问题解决

    本文主要介绍了程序包org.springframework.boot不存在的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-09-09
  • SpringBoot+Redis实现后端接口防重复提交校验的示例

    SpringBoot+Redis实现后端接口防重复提交校验的示例

    本文将结合实例代码,介绍SpringBoot+Redis实现后端接口防重复提交校验的示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • Spring Boot中使用Spring MVC的示例解析

    Spring Boot中使用Spring MVC的示例解析

    MVC 是一种常见的软件设计模式,用于分离应用程序的不同部分以实现松散耦合和高内聚性,这篇文章主要介绍了如何在Spring Boot中使用Spring MVC,需要的朋友可以参考下
    2023-04-04
  • springboot-curd基于mybatis项目搭建

    springboot-curd基于mybatis项目搭建

    这篇文章主要介绍了springboot-curd基于mybatis项目搭建,围绕相关资料展开详细内容,希望对正在学习的你有所帮助,需要的小伙伴也可以参考一下
    2022-01-01
  • SpringBoot整合Web开发之Json数据返回的实现

    SpringBoot整合Web开发之Json数据返回的实现

    这篇文章主要介绍了SpringBoot整合Web开发其中Json数据返回的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • fastjson全局日期序列化设置导致JSONField失效问题解决方案

    fastjson全局日期序列化设置导致JSONField失效问题解决方案

    这篇文章主要介绍了fastjson通过代码指定全局序列化返回时间格式,导致使用JSONField注解标注属性的特殊日期返回格式失效问题的解决方案
    2023-01-01
  • jmeter中beanshell的用法小结

    jmeter中beanshell的用法小结

    本文主要介绍了jmeter中beanshell的用法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • IDEA2022搭建Spring Cloud多模块项目的详细过程

    IDEA2022搭建Spring Cloud多模块项目的详细过程

    这篇文章主要介绍了IDEA2022搭建Spring Cloud多模块项目,网上有很多教程父模块都是通过maven的方式创建的,然后子模块是通过Spring Initalizr方式创建,这种方式父模块无法管理子模块的依赖仲裁,需要每个子模块自行管理,就失去了父模块的用处了
    2022-10-10
  • Jmeter如何获取jtl文件中所有的请求报文详解

    Jmeter如何获取jtl文件中所有的请求报文详解

    JMeter的可以创建一个包含测试运行结果的文本文件,这些通常称为JTL文件,因为这是默认扩展名,但可以使用任何扩展名,这篇文章主要给大家介绍了关于Jmeter如何获取jtl文件中所有的请求报文的相关资料,需要的朋友可以参考下
    2021-09-09
  • hadoop运行java程序(jar包)并运行时动态指定参数

    hadoop运行java程序(jar包)并运行时动态指定参数

    这篇文章主要介绍了hadoop如何运行java程序(jar包)并运行时动态指定参数,使用hadoop 运行 java jar包,Main函数一定要加上全限定类名,需要的朋友可以参考下
    2021-06-06

最新评论