加入收藏 | 设为首页 | 会员中心 | 我要投稿 广州站长网 (https://www.020zz.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

如何搭建合适的Web框架?

发布时间:2019-10-12 10:17:14 所属栏目:优化 来源:架构思维
导读:副标题#e# 之前在Web开发框架推导一文中我们一步步的搭建了一个开发框架。 在当时的情况下,还算满足需求。但是随着项目的逐渐完善,需求变更的频度逐渐变得比新增需求的频度高,原来框架的弊端越来越明显,所以需要对框架进行升级改进。 我们先来看原来框
副标题[/!--empirenews.page--]

 如何搭建合适的Web框架?

之前在Web开发框架推导一文中我们一步步的搭建了一个开发框架。

如何搭建合适的Web框架?

在当时的情况下,还算满足需求。但是随着项目的逐渐完善,需求变更的频度逐渐变得比新增需求的频度高,原来框架的弊端越来越明显,所以需要对框架进行升级改进。

我们先来看原来框架的问题,然后基于这些问题,来对框架进行改进。

原框架的问题

  • 代码生成问题
  • 参数传递问题
  • Service层问题
  • 测试依赖问题
  • Mapper.xml的问题

代码生成问题

在原框架中,我们基于各种约束,编写了一个代码生成组件,通过这个组件,我们可以针对选中的表来生成Controller,Service,Model,Mapper等一系列的类,也就是说,只要建完表,就可以直接生成一套CRUD,直接就可以启动并测试。这在项目初期看起来很美,但是在需求变动时,还是有很多的局限性。

首先,生成的代码逻辑是固化的。如果稍微有些调整,就需要调整生成代码的组件,然后重新打包,上传到jar仓库,项目修改组件版本,再进行代码生成,整个流程过于繁琐。

其次,为了方便代码的生成,其实是做了不少妥协的:

  • 为了方便在修改表字段以后,能够重新生成,很多类都抽象了一个基类用于操作Model字段。这些基类不能够手动修改,因为每次生成都会覆盖。这实际导致了类的数量的增多。
  • 生成的CRUD固化了,不能手动调整。如果生成的CRUD不满足需求,不能直接在代码上修改。只能拷贝一份进行修改,因为再次生成时会覆盖。这导致了代码的冗余。
  • Param和Result委托了Model,这在Model发生改变时,能在编译期就能知道对应字段的调整。但是也引入了不少问题,我们在「参数传递问题」一节单独讨论。

参数传递问题

当初为了便于代码的生成,决定Param和Result都继承Model,这导致了如下的一些问题:

  • 使得Param和Result都依赖了Model。但是Param和Result是视图层模型,而Model是持久层模型,两者的进化度并不是一致的。但是现在的继承关系导致了在默认情况下视图层模型的进化需要和持久层同步,当然你也可以手动调整Param和Result,但是这又导致了代码生成的优势没有了。
  • Param和Result通过委托的方式来设置字段,也就是说,它们实际是没有字段的,通过getter和setter将值设置到了Model中。这就没法使用lombok来简化getter和setter,使得Param和Result代码行数较多
  • 同时,对于swagger来说,有些注解需要基于字段,导致某些功能无法实现(例如:ModelAttribute),只能基于额外手段来处理(例如:需要通过ApiImplicitParams来实现字段文档)。
  • CRUD都是基于同一个Param和Result,导致前端的接口会显示很多无用的字段,加大前端理解接口的难度

Service层问题

Service层有如下问题:

  • Service层的职责过重,包括了事务处理、参数设置、业务逻辑
  • 导致Service中的代码是面条代码,不利于业务逻辑的理解
  • 同时事务注解是直接加在类上的,Spring的默认事务机制会导致类似如下代码的逻辑调用不会抛出期望的异常
  1. // PostService 
  2. public String savePost(Post post) { 
  3.  postRepository.save(post); 
  4.  for(PostDiscuss discuss : post.getDiscuss()) { 
  5.  // 这里是抓不到RuntimeException异常的,会是一个TransactionRollBack的异常 
  6.  discussService.save(discuss); 
  7.  } 
  8. // discussService 
  9. public String savePost(PostDiscuss discuss) {  
  10.  throw new RuntimeException("保存失败"); 

测试依赖问题

核心的业务逻辑在Service中,测试还是需要依赖于Spring,当项目越来越大时,启动项目的时间越来越长,可能要1分钟甚至更长。这就导致单元测试效率越来越低。

Mapper.xml的问题

在面试的时候,我经常会问下面的一些问题:

  • Java里面接口的作用是什么?
  • Service、DAO为什么要编写接口,再去实现这个接口?
  • 接口和实现在相同的模块下,反正都要重新打包的。多写个接口不是多写了好几行代码吗?
  • 和上面类似的问题,Mybatis里面,声称将sql独立到了Mapper.xml文件中,使得可以不需要编译直接修改sql。但Mapper.xml都是和Class放在一起的,改了还是需要重新打包,而且Mybatis是不能动态加载Mapper.xml的,那把sql独立到XML里,到底有什么优势?

对于最后一个问题,我的答案是,对于大部分项目来说,没什么优势!项目易不易于部署、扩展,不在于你使用的框架,而在于你的设计。

就以Mapper.xml来说,Mybatis将sql与代码分离了,但是你在项目里还是将Mapper.xml和代码放在同一个模块下,那这个优势就没有了。既然没有这个优势,我们还有必要单独写Mapper.xml文件吗?我的选择是,那就不写了,直接使用Mybatis提供的注解。

同时为了解决Service层对DAO层(这里也就是对Mybatis)的强依赖,对框架进行了一些改进,解耦Service和DAO层。具体见下面的改进方案。

框架改进方案

为了解决上面这些问题,对框架进行了如下调整:

  • 分离Param、Result和Model
  • 替换代码生成
  • 独立业务逻辑
  • Model层优化

分离Param、Result和Model

(编辑:广州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读