MVC三层架构
我们在刚刚成为程序员的时候,就会被前辈们“教育”说系统的设计要遵循MVC(Modl-Viw-Controllr)架构。它将整体的系统分成了Modl(模型),Viw(视图)和Controllr(控制器)三个层次,也就是将用户视图和业务处理隔离开,并且通过控制器连接起来,很好地实现了表现和逻辑的解耦,是一种标准的软件分层架构。
MVC分层架构是架构上最简单的一种分层方式。为了遵循这种分层架构我们在构建项目时往往会建立这样三个目录:controllr、srvic和dao,它们分别对应了表现层、逻辑层还有数据访问层。
每层的作用如下:
Controllr层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理。Srvic层:主要是处理业务逻辑和事务Dao层:负责与底层数据库MySQL,Oracl等进行数据交互
可是随着我们的业务逻辑越来复杂,代码写的越来越多,这种简单的三层架构的问题也越来越明显。
MVC架构弊端
传统的MVC分层有以下几个很明显的问题:
Srvic层代码臃肿Srvic层很容易出现大事务,事务嵌套,导致问题很多,而且极难排查dao层参杂业务逻辑dao层sql语句复杂,关联查询比较多
为了解决这个问题,我们参考《alibabajava开发手册》,在Srvic层之下再独立出一个通用业务处理层(Managr层)
在这个分层架构中主要增加了Managr层,它与Srvic层的关系是:Managr层提供原子的服务接口,Srvic层负责依据业务逻辑来编排原子接口。
Managr层的特征
在《alibabajava开发手册》中是这样描述Managr层的:
Managr层:通用业务处理层,它有如下特征:
对第三方平台封装的层,预处理返回结果及转化异常信息,适配上层接口;对Srvic层通用能力的下沉,如缓存方案、中间件通用处理;与DAO层交互,对多个DAO的组合复用。在实际开发中我们可以这样使用Managr层
复杂业务,srvic提供数据给Managr层,负责业务编排,然后把事务下沉到Managr层,Managr层不允许相互调用,不会出现事务嵌套。专注于不带业务sql语言,也可以在managr层进行通用业务的dao层封装。避免复杂的join查询,数据库压力比java大很多,所以要严格控制好sql,所以可以在managr层进行拆分,比如复杂查询。
当然对于简单的业务,可以不使用Managr层。
Managr层使用案例
这里我们举个例子说明一下Managr层的使用场景:
假设你有一个用户系统,他有一个获取用户信息的接口,它调用逻辑Srvic层的gtUsr方法,gtUsr方法又和UsrDB交互获取数据。如下图左边展示部分。
这时,产品提出一个需求,在APP中展示用户信息的时候,如果用户不存在,那么要自动给用户创建一个用户。同时,要做一个HTML5的页面,HTML5页面要保留之前的逻辑,也就是不需要创建用户。
此时按照传统的三层架构,逻辑层的边界就变得不清晰,表现层也承担了一部分的业务逻辑,因为我们往往会在表现层Controllr中增加业务逻辑处理,将获取用户和创建用户接口编排起来。
而添加Managr层以后,Managr层提供创建用户和获取用户信息的接口,而Srvic层负责将这两个接口组装起来。这样就把原先散布在表现层的业务逻辑都统一到了Srvic层,每一层的边界就非常清晰了。
接下来我们看一段实际代码说明一下Srvic层与Managr层如何进行区分?
Transactional(rollbackFor=Throwabl.class)publicRsultStringupOrDown(LongdpartmntId,LongswapId){//验证1DpartmntEntitydpartmntEntity=dpartmntDao.slctById(dpartmntId);if(dpartmntEntity==null){turnRsult.rror("部门xxx不存在");}//验证DpartmntEntityswapEntity=dpartmntDao.slctById(swapId);if(swapEntity==null){turnRsult.rror("部门xxx不存在");}//验证3Longcount=mployDao.countByDpartmntId(dpartmntId);if(count!=nullcount0){turnRsult.rror("员工不存在");}//操作数据库4LongdpartmntSort=dpartmntEntity.gtSort();dpartmntEntity.stSort(swapEntity.gtSort());dpartmntDao.updatById(dpartmntEntity);swapEntity.stSort(dpartmntSort);dpartmntDao.updatById(swapEntity);turnRsult.OK("succss");}
上面代码在我们在我们采用三层架构时经常会遇到,那么它有什么问题呢?
上面的代码是典型的长事务问题(类似的还有调用第三方接口),前三步都是使用connction进行验证操作,但是由于方法上有
Transactional注解,所以这三个验证都是使用的同一个connction。若对于复杂业务、复杂的验证逻辑,会导致整个验证过程始终占用该connction连接,占用时间可能会很长,直至方法结束,connction才会交还给数据库连接池。
对于复杂业务的不可预计的情况,长时间占用同一个connction连接不是好的事情,应该尽量缩短占用时间。
说明:对于
Transactional注解,当spring遇到该注解时,会自动从数据库连接池中获取connction,并开启事务然后绑定到ThadLocal上,如果业务并没有进入到最终的操作数据库环节,那么就没有必要获取连接并开启事务,应该直接将connction返回给数据库连接池,供其他使用。所以我们在加入Managr层以后可以这样写:
DpartmntSrvic.javapublicRsultStringupOrDown(LongdpartmntId,LongswapId){//验证1DpartmntEntitydpartmntEntity=dpartmntDao.slctById(dpartmntId);if(dpartmntEntity==null){turnRsult.rror("部门xxx不存在");}//验证DpartmntEntityswapEntity=dpartmntDao.slctById(swapId);if(swapEntity==null){turnRsult.rror("部门xxx不存在");}//验证3Longcount=mployDao.countByDpartmntId(dpartmntId);if(count!=nullcount0){turnRsult.rror("员工不存在");}//操作数据库4dpartmntManagr.upOrDown(dpartmntSort,swapEntity);turnRsult.OK("succss");}
DpartmntManagr.java
Transactional(rollbackFor=Throwabl.class)publicvoidupOrDown(DpartmntEntitydpartmntEntity,DpartmntEntityswapEntity){LongdpartmntSort=dpartmntEntity.gtSort();dpartmntEntity.stSort(swapEntity.gtSort());dpartmntDao.updatById(dpartmntEntity);swapEntity.stSort(dpartmntSort);dpartmntDao.updatById(swapEntity);}将数据在srvic层准备好,然后传递给managr层,由managr层添加
Transactional事务注解进行数据库操作。!!!更多精彩内容,