javaweb应用的代码分层最佳实践

     2022-04-03     732

关键词:

Java Web应用的代码分层最佳实践

技术图片

代码分层,对于任何一个Java Web开发来说应该都不陌生。一个好的层次划分不仅可以能使代码结构更加清楚,还可以使项目分工更加明确,可读性大大提升,更加有利于后期的维护和升级。

从另外一个角度来看,好的代码分层架构,应该是可以很好的匹配上单一职责原则的。这样就可以降低层与层之间的依赖,还能最大程度的复用各层的逻辑。本文就来介绍下Java Web项目的代码到底应该如何分层。

三层架构


在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。微软推荐的分层式结构一般分为三层,从下至上分别为:数据访问层、业务逻辑层(又或称为领域层)、表示层。这也是Java Web中重要的三层架构中的三个层次。区分层次的目的即为了“高内聚低耦合”的思想。
所谓三层体系结构,是在客户端与数据库之间加入了一个“中间层”,也叫组件层。这里所说的三层体系,不是指物理上的三层,不是简单地放置三台机器就是三层体系结构,也不仅仅有B/S应用才是三层体系结构,三层是指逻辑上的三层,即把这三个层放置到一台机器上。

数据访问层

主要是对非原始数据(数据库或者文本文件等存放数据的形式)的操作层,而不是指原始数据,也就是说,是对数据库的操作,而不是数据,具体为业务逻辑层或表示层提供数据服务。

业务逻辑层

主要是针对具体的问题的操作,也可以理解成对数据层的操作,对数据业务逻辑处理,如果说数据层是积木,那逻辑层就是对这些积木的搭建。

界面层

主要表示WEB方式。如果逻辑层相当强大和完善,无论表现层如何定义和更改,逻辑层都能完善地提供服务。

三层架构与MVC的区别

MVC(模型Model-视图View-控制器Controller)是一种架构模式,可以用它来创建在域对象和UI表示层对象之间的区分。
技术图片

同样是架构级别的,相同的地方在于他们都有一个表现层,但是他们不同的地方在于其他的两个层。

在三层架构中没有定义Controller的概念。这是最不同的地方。而MVC也没有把业务的逻辑访问看成两个层,这是采用三层架构或MVC搭建程序最主要的区别。
技术图片

分层的最佳实践


随着网站的用户量的不断提升,系统架构也在不断的调整。有时候,随着业务越来越复杂,有时候三层架构好像不够用了。比如,我们的应用除了要给用户提供页面访问以外,还需要提供一些开放接口,供外部系统调用。这个接口既不属于界面层,也不应该属于业务逻辑层,因为他还可能包含一些和业务逻辑无关的处理,如权限控制、流量控制等。
还有,随着微服务的盛行,我们应用中可能要依赖很多外部接口或第三方平台。这部分代码放下业务逻辑层和数据访问层也都不合适。

所以,渐渐的,在三层架构的基础上,系统架构的分层变得更加复杂了。也正是因为复杂,就非常考验架构设计能力,因为层次划分的不好,很可能会影响后面的开发,给代码维护带来很大的困难。

下图,是阿里巴巴(参考《阿里巴巴Java开发手册》)提倡的应用分层结构:
技术图片

开放接口层

可直接封装 Service 方法暴露成 RPC 接口;通过 Web 封装成 http 接口;进行网关安全控制、流量控制等。

终端显示层

各个端的模板渲染并执行显示的层。当前主要是 velocity 渲染,JS 渲染,JSP 渲染,移动端展示等。

Web 层

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

Service 层

相对具体的业务逻辑服务层。

Manager 层

通用业务处理层,它有如下特征: 1) 对第三方平台封装的层,预处理返回结果及转化异常信息; 2) 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理; 3) 与 DAO 层交互,对多个 DAO 的组合复用。

DAO 层

数据访问层,与底层 MySQL、Oracle、Hbase 等进行数据交互。

外部接口或第三方平台

包括其它部门 RPC 开放接口,基础平台,其它公司的 HTTP 接口。

事务处理


在了解了分层之后,我们再来看一下写Java Web代码的时候,大家比较关心的一个问题,那就是涉及到数据库操作的时候,事务处理应该在哪一层控制呢?
关于这个问题,仁者见仁,智者见智。作者认为,事务处理应该放在Service层和Manager层。

DAO层不应该有事务,应该只是很纯的 CRUD 等比较通用的数据访问方法。一个DAO应该只处理和自己相关的操作,不要有任何组合。组合的事情交给上层。

Service层和Manager层一般会组合多个DAO的CRUD操作,例如:在注册一个用户的时候需要往日志表里 INSERT 日志,那么就在 Service 层构造事务,在该事务中调用 Dao 层的 User.Insert () 与 Log.Insert ()。

异常处理


异常处理是Java中比较重要的一个话题,在《Effective Java》中有很多关于异常处理的最佳实践,这里不详细介绍了,本文主要简单说一下在应用代码分层之后,各个层次之间的异常应该如何处理,是自己捕获,还是向上一层抛出。
首先,每一层都是可能发生异常的。由于每一层的职责都不通,处理方式也可能有差别。

DAO层

在 DAO 层,产生的异常类型可能有很多,可能是SQL相关的异常,也可能是数据库连接相关的异常。

这一层的处理方式可以简单一点,直接try-catch(Exception),然后封装成DAOException抛给上一层。这一层一般不需要打印日志,交给Service或者Manager层来打印。


try{
   CRUD
}catch(Exception e){
   throw new DAOException(e);
}
Manager/Service

首先,对于DAO层抛上来的异常一定要捕获的,并且记录日志打印现场。

但是值得注意的是,如果是需要事务控制的方法,要注意捕获到异常之后再向上抛一个新的异常,如 TransactionRolledbackException,否则事务无法回滚。

这两层发生的异常可以根据情况决定是继续向上抛还是自己处理掉。如果是自己可以处理的异常,就捕获,打日志,然后通过ErrorCode等方式返回给上一层。如果是自己无法处理或者不知道该如何处理的异常,就直接抛给上一层来处理。

Web

首先,可以明确的一点:Web层不应该再往外抛异常,因为这一层一旦抛异常,就可能会导致用户跳转到不友好的错误页面甚至看到错误信息等。

如果意识到这个异常将导致页面无法正常渲染,那么就应该直接跳转到友好错误页面,加上用户容易理解的错误提示信息。

开放接口层

这一层和Web层一样,不可以抛出异常。一般通过ErrorCode和ErrorMessage反馈给外部调用方。

这一层,要自己处理好所有的异常,定义好ErrorCode,并记录好日志,便于日后排查问题。

总结


本文主要介绍了Java Web项目中代码分层的方案,通过分层之后可以使没一层更加专注,解除耦合。并简单介绍了一下分层之后的事务处理和异常处理的逻辑。

- MORE | 更多精彩文章 -

  • 本地缓存的原理及技术选型思考
  • 大家都在说分布式系统到底是什么
  • Java虚拟机的锁优化技术
  • 你真的了解Java中的三目运算符吗

    如果你看到了这里,说明你喜欢本文。

    那么请长按二维码,关注Hollis

    技术图片
    转发朋友圈,是对我最大的支持。

jsr-303beanvalidation介绍及springmvc服务端验证最佳实践

  任何时候,当要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情。  应用程序必须通过某种手段来确保输入参数在上下文来说是正确的。  分层的应用在很多时候,同样的数据验证逻... 查看详情

重新整理.netcore实践篇—————应用分层[二十四](代码片段)

前言简单整理一下分层。正文应用程序分层,分为:1.领域模型层2.基础设施层3.应用层4.共享层共享层共享层一般包括下面几个类库。有一个Core的类库,比如说BLog.Core.这个类库用来,主要用来承载一些基础简单的类型,比如说一... 查看详情

XML 集合最佳实践

...布时间】:2010-09-0614:07:14【问题描述】:我正在创建一个应用程序,它将在XML文件中存储项目的分层集合,我想知道在XML中存储集合的行业标准。以下两种格式中的哪一种更受欢迎?(如果我没有看到其他选项,请告知。)选项... 查看详情

最佳实践 - 您自己的项目/应用程序的 NSError 域和代码

】最佳实践-您自己的项目/应用程序的NSError域和代码【英文标题】:BestPractice-NSErrordomainsandcodesforyourownproject/app【发布时间】:2010-07-1815:34:56【问题描述】:有一个previousSOpost是关于为您自己的框架设置错误域,但是关于为您自... 查看详情

在应用程序中使用 jQuery 表单绑定代码的最佳实践

】在应用程序中使用jQuery表单绑定代码的最佳实践【英文标题】:BestpracticeswithjQueryformbindingcodeinanapplication【发布时间】:2010-09-0706:36:43【问题描述】:我们有一个应用程序,其中包含大量对服务器端代码的jQueryJSON调用。因此,... 查看详情

javaweb学习笔记--分层设计

在早期的JavaWeb应用中,JSP文件负责处理业务逻辑,控制网页流程并创建HTML页面,JSP文件是一个独立的,能自主完成所有任务的模块,这带来了一系列问题:HTML代码和Java程序代码强耦合在一起内嵌的流程控制逻辑调试困难可维... 查看详情

访问 Spring 的安全上下文的最佳实践

...2015-06-2205:53:45【问题描述】:我正在开发具有分层架构的应用程序:演示-服务-数据访问这个服务层的许多模块需要访问当前登录的用户。让这些模块直接访问安全上下文以获取UserDetails对象是个好主意吗?我在想,如果将来来... 查看详情

springcloud应用在kubernetes上的最佳实践—部署篇(工具部署)(代码片段)

...开发和用户体验优化工作。导读:上一篇文章《SpringCloud应用在Kubernetes上的最佳实践—部署篇(开发部署)》我们介绍了从IDE插件内介绍了如何进行应用部署的方式,除此之外,目前EDAS还支持了额外的工具对其他场景进行覆盖,... 查看详情

最佳实践——在应用之间共享 Spring-boot Service 和 Repo 层代码

】最佳实践——在应用之间共享Spring-bootService和Repo层代码【英文标题】:Bestpractice-shareSpring-bootServiceandRepolayercodebetweenapplications【发布时间】:2022-01-0814:50:20【问题描述】:需要一些关于基于层模块化Springboot应用程序的经典需... 查看详情

flask应用最佳实践

一个好的应用目录结构可以方便代码的管理和维护,一个好的应用管理维护方式也可以强化程序的可扩展性应用目录结构假定我们的应用主目录是”flask-demo”,首先我们建议每个应用都放在一个独立的包下,假设包名是”myapp”... 查看详情

javaweb之:分层设计

1、早期Web系统是没有使用分层思想进行设计的,大量的代码会出现在一个servlet或者jsp中,这样就不可避免的造成代码质量低劣。2、MVC分层模式  MVC分层模式是指模型(Model)、视图(View)、控制(Controller)的缩写,使用分... 查看详情

在 JDBC 中编写 SQL 查询的最佳实践是啥

...使用JSP和带有JDBC的Servlet开发电子商务Web应用程序来学习JavaWeb开发。我正在检查GitHub、GoogleCode等中的一些项目,并且遇到了一些我发现不寻 查看详情

临时维护页面的最佳实践方法和状态代码是啥?

...2010-09-1503:56:18【问题描述】:对于不应暂时显示的网站/应用程序页面,最佳实践方法和最理想的HTTP状态代码是什么?概述可能的替代方案-将用户留在当前URL,但返回 查看详情

Flutter:从 UI 调用异步代码的最佳实践

...】:2018-08-1414:15:31【问题描述】:我正在开发一个Flutter应用程序,但想知道从UI调用异步代码时应该怎么做-比如说UI小部件的构建方法。例如,我的应用通过服务类连接Firebase,该服务类使用Async-Await样式从Firebase获取记录。使用... 查看详情

Spark:单个应用程序中的两个 SparkContext 最佳实践

】Spark:单个应用程序中的两个SparkContext最佳实践【英文标题】:Spark:TwoSparkContextsinasingleApplicationBestPractice【发布时间】:2016-01-3107:06:52【问题描述】:我想我今天有一个有趣的问题要问大家。在下面的代码中,您会注意到我有... 查看详情

在类似 Web 应用程序之间共享 Web 层代码(控制器和 JSP)的最佳实践

】在类似Web应用程序之间共享Web层代码(控制器和JSP)的最佳实践【英文标题】:Bestpracticesforsharingweb-tiercode(ControllersandJSPs)betweensimilarwebapps【发布时间】:2012-03-2416:54:53【问题描述】:我正在重写一些老化的Web应用程序。特别... 查看详情

谷歌应用引擎:隐藏 Rails 密钥的最佳实践?

】谷歌应用引擎:隐藏Rails密钥的最佳实践?【英文标题】:Googleappengine:BestpracticeforhidingRailssecretkeys?【发布时间】:2017-04-1911:56:28【问题描述】:我正在将我的Rails应用程序部署到GAE,其代码存储在github中。显然,我需要隐藏我... 查看详情

编码最佳实践——单一职责原则(代码片段)

...LLiskov(里式)替换原则I接口分离原则D依赖注入原则同时应用这些最佳实践,可以提升代码适应变更的能力。但是凡事要有度,过度使用虽然可以让代码有很高的自适应能力,但是会导致层次粒度过小而难以理解或使用,还会影... 查看详情