本编文章是学习[晓风轻技术小站]做的总结。为防丢失发布出来,也算一次分享。
只用做学习和交流,如有侵权请留言删除。
编码规范
1. 接口定义规范中出现的问题
-
返回结果格式不统一
要有一个统一返回结果的Bean。
-
没有考虑失败的情况
-
出现和业务无关的输入参数
-
出现复杂的参数
-
没有返回应该返回的数据
新建对象应该返回新对象的Id(觉得前台没用到就不返回,那以后要用到怎么办?)
2. Controller规范
-
统一返回ResultBean对象(返回的对象结构上统一)
-
ResultBean对象不允许往后传
ResultBean是Controller专用的不能向后传递。
-
Controller只做参数格式转化
Controller做参数格式转换,不允许json,map这类对象传到Services中
Services也并不允许返回json,map
json,map 中的数据可以定义一个Bean来进行转换和传递
-
参数不允许出现Request,Response这些对象
原因和json,map不能传递一样,可读性差
-
不需要打印日志
日志在AOP中会打印,大部分日志在Services中打印
总结: Controller只做参数格式转换,如果没有参数需要转换,那么就一行代码。日志/参数校验/权限判断放到Services层中 (1. Controller 基本无法重用,Services重用较多;2. 单元测试直接测试Services,不用测试Controller)
3. AOP实现
-
ResultBean定义
package com.qian.common; import lombok.Data; import java.io.Serializable; /** * @author qian */ @Data public class ResultBean<T> implements Serializable { /**序列化对象 版本号 如果该对象被改变,则需要修改该版本号。 有助与序列化和反序列化*/ private static final long serialVersionUID = 1L; /** 成功 */ public static final int SUCCESS = 0; /** 失败 */ public static final int FAIL = -1; /** 没有权限 */ public static final int NO_PERMISSION = -2; /** 没用登录 */ public static final int NO_LOGIN = -3; private int code = SUCCESS; private String msg = "success"; private T data; public ResultBean(){ super(); } public ResultBean(T data){ super(); this.data = data; } public ResultBean(Throwable e){ super(); this.msg = e.getMessage(); this.code = FAIL; } }
-
导入AOP依赖pom
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency>
-
通过.xml配置文件配置aop
<?xml version="1.0" encoding="utf-8" ?> <beans xmlns='http://www.springframework.org/schema/beans' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/beans/spring-aop.xsd "> <aop:aspectj-autoproxy/> <bean id="controllerAop" class="com.qian.common.aop.ControllerAOP"/> <aop:config> <aop:aspect id="myAop" ref="controllerAop"> <aop:pointcut id="target" expression="execution(public com.qian.common.ResultBean *(..))"/> <aop:around method="handlerControllerMethod" pointcut-ref="target"/> </aop:aspect> </aop:config> </beans>
-
简单示例
/** * 配置对象处理器 * * @author 晓风轻 https://github.com/xwjie/PLMCodeTemplate */ @RequestMapping("/config") @RestController public class ConfigController { private final ConfigService configService; public ConfigController(ConfigService configService) { this.configService = configService; } @GetMapping("/all") public ResultBean<Collection<Config>> getAll() { return new ResultBean<Collection<Config>>(configService.getAll()); } /** * 新增数据, 返回新对象的id * * @param config * @return */ @PostMapping("/add") public ResultBean<Long> add(Config config) { return new ResultBean<Long>(configService.add(config)); } /** * 根据id删除对象 * * @param id * @return */ @PostMapping("/delete") public ResultBean<Boolean> delete(long id) { return new ResultBean<Boolean>(configService.delete(id)); } @PostMapping("/update") public ResultBean<Boolean> update(Config config) { configService.update(config); return new ResultBean<Boolean>(true); } }
4. 日志打印
-
日志要求(大方向上的需求)
a. 能找到那个服务器
**解决办法:**配置Nginx让返回头里面返回是那个机器处理的。
b. 能找到用户做了什么
**解决办法:**log4j中MDC类 打印的时候带上用户信息。
-
日志要求(具体要求)
-
修改(包括新增)操作必须打印日志
大部分问题都是增和改导致的。
-
条件分支必须打印条件值,重要参数必须打印
分支条件直接知道走了什么分支。直接帮助排除问题。
-
数据量大的时候需要打印数据量
(打印数据量,处理时间 )。
-
-
新手建议
- 不要依赖debug,多依赖日志。
- 代码开发测试完成后不要急着提交,先跑一遍看看日志是否看得懂。
5. 异常处理
-
出现异常是的可怕情况
a. 系统出现异常而开发人员不知道,客户大面积受影响才知道。
b. 出了问题无法定位问题
-
如何规范异常处理(**总纲:**绝大部分场景。不允许捕获异常,不乱加空判断)
a. 大多数情况不用try{}catch(){}结构捕获异常,将异常全都抛到Controller中进行统一处理。
b. 要空判断的情况,用户输入数据。系统传过来的,系统自己获取的。不加空判断,将异常抛出去就好了。
-
异常处理要求
- 开发组长定义好异常,异常继承RuntimeException。
- 不允许开发人员捕获异常。
- 少加空判断,加了两种情况都需要做处理。
6. 参数校验和国际化
-
编写公用的参数校验工具类(例)
package plm.common.utils; import org.springframework.context.MessageSource; import plm.common.exceptions.CheckException; public class CheckUtil { private static MessageSource resources; public static void setResources(MessageSource resources) { CheckUtil.resources = resources; } public static void check(boolean condition, String msgKey, Object... args) { if (!condition) { fail(msgKey, args); } } public static void notEmpty(String str, String msgKey, Object... args) { if (str == null || str.isEmpty()) { fail(msgKey, args); } } public static void notNull(Object obj, String msgKey, Object... args) { if (obj == null) { fail(msgKey, args); } } private static void fail(String msgKey, Object... args) { throw new CheckException(resources.getMessage(msgKey, args, UserUtil.getLocale())); } }
-
国际话信息不从Url获取,直接和用户绑定。
7. 工具类编写
-
尽量定义一个自己的工具类(自己不想去实现,在工具类中直接调用第三方的工具类)
public static boolean isEmpty(String str) { return org.apache.commons.lang3.StringUtils.isEmpty(str); }
如果直接使用第三方的,需求发生改变时,很多地方都需要去修改
-
使用父类/接口(所有父类出现的地方都可以用子类代替)
例:ArrayList判空
-
直接用ArrayList
public static boolean isEmpty(ArrayList<?> list) { return list == null || list.size() == 0; }
-
我们只用到了size方法,向上看其接口List中也有这个方法的定义
public static boolean isEmpty(List<?> list) { return list == null || list.size() == 0; }
-
List的父类Collection中也有size于是
public static boolean isEmpty(Collection<?> collection) { return collection == null || collection.size() == 0; }
-
-
使用重载编写衍生函数组(方法重载)
-
使用静态引用
解决工具类泛滥(每个组员都写一个工具类,自己忘记已经写过某个工具类,然后忘记了)(要有相应的命名规范)
-
物理上独立存放
把和业务逻辑无关的代码放到独立的工程或者目录(独立存放。专门维护(自己写的只能自己去维护))
**总结:**要充分的使用面向对象的思想(封装,继承,多态)
几乎所有人都知道面向对象的思想有抽象封装,但几个人真正能做到,其实有心的话,处处都能体现出这些思想。编写工具类的时候需要注意参数的优化,而且大型项目里面不要在业务代码里面直接调用第三方的工具类,然后就是多想一步多走一步,考虑各种类型的入参,这样你也能编写出专业灵活的工具类!
8.函数编写的建议
-
不要出现和业务逻辑无关的参数
-
避免使用Map,Json这些复杂对象作为参数或结果(在Controller中就将其解析成相应的对象)
-
有明确的输入输出和方法名(规范发命名,参数名,方法名)
-
把可能变化的地方封装成函数(抽象和封装)
要把代码的逻辑抽象出来封装成为一个函数,以应对将来可能的变化。以后代码逻辑有变更的时候,单独修改和测试这个函数即可。
-
编写能测试的函数(程序员的目标,更短,更小,更快)
- 不要出现乱七八糟的参数。如(Request,Response)
- 把函数写小一点,一个Service一个函数,单元测试就麻烦了。力求每个函数都可以进行单元测试(junit,main)
- 要有单元测试每个函数的习惯(一行行代码,一个个函数的测试)
9. 配置规范
-
书写配置的流程
1.写配置文件,2.书写配置文件对应的bean,3,实例化这个bean,4,将这个bean输出检查(单元测试)
-
配置文件编码禁忌(不要这样做)
-
读配置的代码与业务代码耦合
-
开发初期就定义配置文件(后期或频繁改动,应该先定义bean,测试时再生成配置文件)
-
手工编写配置文件
应该先写代码,根据代码生成配置文件
-
**重要思想:**不要直接和配置文件发生关系,一定要有第三者,bean就是第三者
10. 如何应对需求变更
-
把代码写到最简单(需求变更不可避免,所以把代码写简单,对付各种各样的需求变化)
-
把可能变化的都封装成函数
多思考抽象和封装,小变将无法伤害到你(主动思考将来可能的各种场景)
-
先做确定的需求
多个共功能先做不会变的功能,一个功能先做不会变的部分
**可借鉴的习惯:**难的/确定的先做。
-
解耦解耦
解耦的关键在于引入第三者,不要直接发生关系
-
数据结构上要考虑扩展
做任何一个功能都要想到将来的发展,功能现在可以不做,但对吧将来的变化做到了然于胸
关系表,现在1对1,将来会不会1对n,n对n
系统集成(jms)
**总结:**主动思考,多走一步,不要被动接受看到的需求,要对需求的将来变化做好心中有数。