我记得进入实验室,开始走上java这条路就是:
- 书:Head First Java、Java核心卷…
- 视频:你是黑马派还是硅谷派呀还是小众派呀…
- 各种小项目:书城呀、商城呀这种仿XXX、XXX管理系统呀、贪吃蛇坦克大战这种…
当然啦,从javaSE总过来到数据库、JDBC,到过滤器拦截器控制器啥的,再到框架啥的,如果你想知道方向,你可以看看点点这里,大概都得了解一些做个开端,为以后工作深入学习做准备。当然啦,先学哪个后学哪个,我个人觉得,如果你想用哪个实现哪个需求然后再去学哪个,效果肯定是最好的,也就是一学A,哎呦,A这里这个地方怎么用到了B,然后你再去学B…。当然啦,你年少轻狂有时间,那你就选个派别参与进去,跟着学跟着敲,看书回顾看自己笔记回顾不断回顾…你就比我这个菜鸟强多啦。
PART1:下面选择我接触的第一个上线项目中的功能的后端代码思路,作为今天总结篇的开始:
当时就是实现这个功能:前端那位同僚在页面上写一个按钮啥的,然后选好你要删除的某条数据或者某台设备哪天到哪天的数据,一点按钮进入到后端执行代码逻辑删除页面上的数据以及数据库中的相应数据。over…
- 前端:
- 我就记得前端那个兄弟说,老胡,我这个ctx中写的,就是/xxx1/xxx2/xxx3,意思就是当我选择好日期、设备名以及其他参数时,然后然删除按钮后,会调用到ctx中的东西,而ctx中写的是此时按钮被点击,会出发xxx1包下的xxx2类中的xxx3方法,去执行删除逻辑
- 当然啦,包名是可以叠加的咯/xxx1/xxxx11/xxxxx111/xxx2/xxx3
- 我就记得前端那个兄弟说,老胡,我这个ctx中写的,就是/xxx1/xxx2/xxx3,意思就是当我选择好日期、设备名以及其他参数时,然后然删除按钮后,会调用到ctx中的东西,而ctx中写的是此时按钮被点击,会出发xxx1包下的xxx2类中的xxx3方法,去执行删除逻辑
- 后端:
- 我后端主要就是像上面ctx会先调用到controller层类名为xxx2的类中的xxx3方法,而xxx3方法会调用到XxxService类中逻辑,而XxxService类涉及到数据的增删改查就会调用XxxDao层中的方法
- xxx2这个类一般名字起为XxxController
- XxxDao层肯定是要调用或者说出发Sql语句逻辑从而执行数据的增删改查,一般中放的是抽象方法(没有方法体的方法)
- 所以咱们可以自己去实现XxxDao这些接口或者类,写一些XxxDaoImpl子实现类,里面就是咱们JDBC中那几步呗。创建连接…最后执行那一句SQL语句执行数据的增删改查逻辑
- 咱们也可以用Mybatis这种框架,人家映射文件中的SQL语句就封装住了咱们的SQL语句,然后也可以实现数据的增删改查逻辑
- 我后端主要就是像上面ctx会先调用到controller层类名为xxx2的类中的xxx3方法,而xxx3方法会调用到XxxService类中逻辑,而XxxService类涉及到数据的增删改查就会调用XxxDao层中的方法
然后呢,上面说的分层这块,就有点东西能唠唠了。说起应用分层,大部分人都会认为这个不是很简单嘛 就controller,service, mapper三层
。但是其实呢,咱们有时候并没有把他们职责划分开,在很多代码中,controller做的逻辑比service还多,service往往当成透传了,分层只是一个形式
,这其实是很多人开发代码都没有注意到的地方,反正功能也能用,至于放哪无所谓呗。这样往往造成后面代码无法复用,层级关系混乱,对后续代码的维护非常麻烦。在真正的团队开发中每个人的习惯都不同,写出来的代码必然带着自己的标签,有的人习惯controller写大量的业务逻辑,有的人习惯在service中之间调用远程服务,这样就导致了每个人的开发代码风格完全不同,后续其他人修改的时候,一看,我靠这个人写的代码和我平常的习惯完全不同,修改的时候到底是按着自己以前的习惯改,还是跟着前辈们走,这又是个艰难的选择,选择一旦有偏差,你的后辈又维护你的代码的时候,恐怕就要骂人了。【当然这种分层其实见仁见智, 团队中的所有人的分层习惯也不同,所以很难权衡出一个标准的准则,总的来说只要满足职责逻辑清晰,后续维护容易,就是好的分层
。】
- 一个好的应用分层需要具备以下几点:
方便后续代码进行维护扩展,不能前人种树后人在屎山上继续种树
。- 分层的效果需要让整个团队都接受
- 各个层职责边界清晰,虽不至于贴合单一职责啥的,但是也不能太离谱咯
- 如何进行分层:
- 阿里的编码规范中约束的分层如下:
- 开放接口层 :
- 可直接封装 Service 方法暴露成 RPC 接口;
- 通过 Web 封装成 http 接口;
- 进行 网关安全控制、流量控制等
- 终端显示层 :各个端的模板渲染并执行显示的层。当前主要是 velocity 渲染,JS 渲染, JSP 渲染,移动端展示等。
- Web 层 :主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。
- Service 层 :相对具体的业务逻辑服务层。
虽然咱们刚开始学的时候,基本上都是用MVC模式,也就是controller、service、mapper、entity
。如果未来业务扩展,你没有拆分业务结构的话,很可能就会发现,一个service包下,有上百个服务。所以正确的做法,如果服务过多,应该根据不同的业务或者说功能进行划分,比如订单、登陆、积分等等;也可以根据不同的业务划分模块,比如建一个moudles包,然后按订单、登陆等业务划分,每个业务都有自己的controller、service、mapper、entity
- Manager 层 :通用业务处理层,它有如下特征:
-
- 对第三方平台封装的层,预处理返回结果及转化异常信息;
-
- 对Service层通用能力的下沉,如缓存方案、中间件通用处理;
-
- 与DAO层交互,对多个DAO的组合复用。
-
- DAO 层 :数据访问层,与底层 MySQL、Oracle、Hbase 进行数据交互。阿里巴巴规约中的分层比较清晰简单明了,但是描述得还是过于简单了,以及service层和manager层有很多同学还是有点分不清楚之间的关系,就导致了很多项目中根本没有Manager层的存在。
- 开放接口层 :
- 看看java基基老师文章中的一个较为的理想模型,
rpc框架选用的是thrift可能会比其他的一些rpc框架例如dubbo会多出一层,作用和controller层类似
- 终端显示层&开放接口:
- Service :业务层,复用性较低,
这里推荐每一个controller方法都得对应一个service,不要把业务编排放在controller中去做
,为什么呢?如果我们把业务编排放在controller层去做的话,如果以后我们要接入thrift,我们这里又需要把业务编排在做一次,这样会导致我们每接入一个入口层这个代码都得重新复制一份如下图所示:
- 这样大量的重复工作必定会导致我们开发效率下降,所以我们需要把业务编排逻辑都得放进service中去做:
- 这样大量的重复工作必定会导致我们开发效率下降,所以我们需要把业务编排逻辑都得放进service中去做:
- Mannager :可复用逻辑层。这里的Mannager可以是单个服务的,比如我们的cache,mq等等,当然也可以是复合的,当你需要调用多个Mannager的时候,这个可以合为一个Mannager,比如逻辑上的连表查询等。如果是httpMannager或rpcMannager需要在这一层做一些数据转换
- DAO :数据库访问层。
主要负责“操作数据库的某张表,映射到某个java对象”,dao应该只允许自己的Service访问,其他Service要访问我的数据必须通过对应的Service
。
- 终端显示层&开放接口:
- 在阿里巴巴编码规约中列举了下面几个领域模型规约:分层领域模型的转换,
每一个层基本都自己对应的领域模型,这样就导致了有些人过于追求每一层都是用自己的领域模型,这样就导致了一个对象可能会出现3次甚至4次转换在一次请求中,当返回的时候同样也会出现3-4次转换,这样有可能一次完整的请求-返回会出现很多次对象转换
。如果在开发中真的按照这么来,恐怕就别写其他的了,一天就光写这个重复无用的逻辑算了吧
- 一个折中的方案:
- 允许Service/Manager可以操作数据领域模型,对于这个层级来说,本来自己做的工作也是做的是业务逻辑处理和数据组装。
- Controller/TService层的领域模型不允许传入DAO层,这样就不符合职责划分了
- 不允许DAO层的数据传入到Controller/TService
- 一个折中的方案:
- 阿里的编码规范中约束的分层如下:
PART2:除了分层解耦和,后端开发还有其他的好的经验:当然啦,关于规范呀经验呀这些,阿里巴巴开发手册咱们肯定要好好参考一下咯
- 注释尽可能全面,但也别啥注释都写,过犹不及,要写有意义的注释
- 接口方法、类、复杂的业务逻辑,都应该添加有意义的注释。一般而言好像IDEA和ECLIPSE啥的都会自动生成,可以提前配置好
- 对于接口方法的注释,应该包含详细的入参和结果说明,有异常抛出的情况也要详细叙述
- 类的注释应该包含类的功能说明、作者和修改者
如果是业务逻辑很复杂的代码,真的非常有必要写清楚注释
- 接口方法、类、复杂的业务逻辑,都应该添加有意义的注释。一般而言好像IDEA和ECLIPSE啥的都会自动生成,可以提前配置好
不在循环里远程调用、或者数据库操作,优先考虑批量进行
- 封装的能力要有哦:
- 方法参数过多,要封装一个对象出来;
- 公共的代码要想办法通过比如继承、组合、多态等形式进行抽取,成为一个工具类/工具方法/模板方法/模板类啥的。或者说人家有封装好的工具类啥的,咱们要学会调用,因为人家官方封装的时候可能会考虑到比如时间空间复杂度或者其他一些bug,所以多用用
- 封装复杂的逻辑判断条件,别吓着看你代码的人
- 时刻保持优化性能的嗅觉。比如避免创建非必要的对象、异步处理、使用缓冲流,减少IO操作等等。
- 几个杂碎知识点:
- 可变的参数,比如用户多少天没登录注销、运营活动,不同节日红包皮肤切换、订单多久没付款就删除等等。
对于这些可变的参数,不用该直接写死在代码。优秀的后端,要做配置化处理,你可以把这些可变参数,放到数据库一个配置表里面,也可以放到项目的配置文件或者apollo上
。 - 在finally块中对资源进行释放:
- 用好日志,把日志打印好。你实现转账业务,转个几百万,然后转失败了,接着客户投诉,然后你还没有打印到日志,想想那种水深火热的困境下,你却毫无办法。
一般情况,方法入参、出参需要打印日志,异常的时候,也要打印日志等等
。 - 考虑异常,处理好异常
- 考虑系统、接口的兼容性。
如果修改了对外旧接口,但是却不做兼容。这个问题可能比较严重,甚至会直接导致系统发版失败的
。
- 采取措施避免运行时错误。
应该在编写代码阶段,就采取措施,避免运行时错误,如数组边界溢出,被零整除,空指针等运行时错误
。类似代码比较常见:
- 可变的参数,比如用户多少天没登录注销、运营活动,不同节日红包皮肤切换、订单多久没付款就删除等等。
巨人的肩膀:
Head First Java
B站上各位java视频老师
Java基基老师
是捡田螺的小男孩老师