文章目录
为什么需要统一异常处理?
不用的现状(必要性)
问题代码一:(返回值不统一,混乱)
$.ajax({
type: "GET",
url: "/goods/add",
dataType: "json",
success: function(data) {
if (data.flag) {
alert("添加成功");
} else {
alert(data.message);
}
},
error: function(data){
alert("添加失败");
}
});
问题代码二:
@RequestMapping("/goods/add")
@ResponseBody
public Map add(Goods goods) {
Map map = new HashMap();
try {
// do something
map.put(flag, true);
} catch (Exception e) {
e.printStackTrace();
map.put("flag", false);
map.put("message", e.getMessage());
}
reutrn map;
}
这种方式捕获异常后, 返回了错误信息, 且前台做了一定的处理, 看起来很完善? 但用 HashMap 中的 flag 和 message 这种字符串来当键很容易处理, 例如你这里叫 message, 别人起名叫 msg, 甚至有时手抖打错了, 怎么办? 前台再改成 msg 或其他的字符?, 前端后端这样一直来回改?
更有甚者在情况 A 的情况下, 返回 json, 在情况 B 的情况下, 重定向到某个页面, 这就更乱了. 对于这种不统一的结构处理起来非常麻烦.
小结:
- 一大堆的try catch语句
- 前端要做各种没有意义的适配
- 导致前后端严重耦合
使用的好处(意义)
- 代码更加干净清爽
- 异常统一由项目经理leader使用AOP来进行处理和定义,不需要每个开发自己定义维护
- 统一的接口返回值,前后端交互更简单。
- 充分解耦前后端,面向接口编程(符合契约式编程原则)
如何做统一的异常处理?
原则和约定
- 不要捕获任何异常
不要在业务代码中进行捕获异常, 即 dao、service、controller 层的所以异常都全部抛出到上层. 这样不会导致业务代码中的一堆 try-catch 会混乱业务代码. - 统一返回结果集
不要使用 Map 来返回结果, Map 不易控制且容易犯错, 应该定义一个 Java 实体类. 来表示统一结果来返回 - 前端统一处理提示信息
- 后端统一处理异常(使用AOP)
在这里统一配置需要处理的异常, 同样, 对于未知的异常, 一定要及时发现, 并进行处理. 推荐出现未知异常后发送邮件, 提示技术人员.
如何保证新人和代码的维护者保持统一异常处理的好习惯?
岗前培训考核机制
1、新人上手文档手册和文档
2、建立岗前培训以及上岗前的考核机制
3、岗前培训,标准示例代码的学习。工具的学习、配置,最佳实践等。
4、岗前,完成根据题库设定的试题、实战型项目的考核。到此完成验收。
定期codeReview机制
1、工具检查,静态代码扫描工具:sonarqube或者阿里的规约插件等
2、线上使用gitlib加gitflow,进行在线review
3、线下定期举行,meeting式、三俩结组式的codeReview
建立良性的长效激励机制
要让定期review和岗前培训得到长期的支持和落实。
需要高层领导在激励机制上,做出一定的设计和支持。
传帮带机制
主动发现并帮助团队成员,共同进步和成长。定期组织主题技术的分享。
这层,主要是项目leader和项目经理的考核范围,应纳入其绩效考核。
在spring-boot中如何做异常的统一处理?
1、统一在AOP中进行处理(使用@ControllerAdvice,@ExceptionHandler)
package com.dynamic.springbootbvexcetionhandler.exception;
import com.dynamic.springbootbvexcetionhandler.util.ResultBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* a global execption handler use aop
*
* @author Administrator
* @date 2019/1/6
* commpany: yonyou
* version:v1.0.0
*/
@ControllerAdvice
@ResponseBody
public class WebExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(WebExceptionHandler.class);
@ExceptionHandler
public ResultBean methodArgumentNotValid(BindException e) {
LOGGER.error("参数校验失败",e);
StringBuilder errorMessage = new StringBuilder();
e.getAllErrors().forEach(objectError ->
errorMessage.append(objectError.getDefaultMessage()).append(",")
);
return ResultBean.error(1, errorMessage.toString());
}
@ExceptionHandler
public ResultBean methodArgumentNotValidForJsonArgumentResolver(MethodArgumentNotValidException e) {
LOGGER.error("json参数绑定到对象失败",e);
StringBuilder errorMessage = new StringBuilder();
e.getBindingResult().getAllErrors().forEach( errors-> errorMessage.append(errors.getDefaultMessage()).append(",") );
return ResultBean.error(1, errorMessage.toString());
}
@ExceptionHandler
public ResultBean unKnowException(Exception e) {
LOGGER.error("未知异常", e);
//发送邮件,或者短信通知
return ResultBean.error(-999, "发生了未知异常,请联系系统管理员");
}
@ExceptionHandler
public ResultBean ageDeleteException(AgeDeleteException e) {
LOGGER.error("不能删除18岁以下的用户",e);
return ResultBean.error(-1, "不能删除18岁以下的用户");
}
}
2、定义统一的返回对象
private int code;
private String message;
private Collection<T> data;
private ResultBean() {
}
private ResultBean(int code, String message) {
this.code = code;
this.message = message;
}
private ResultBean(int code, String message, Collection<T> data) {
this.code = code;
this.message = message;
this.data = data;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Collection<T> getData() {
return data;
}
public void setData(Collection<T> data) {
this.data = data;
}
public static ResultBean error(int code, String message) {
ResultBean resultBean = new ResultBean(code, message);
return resultBean;
}
public static ResultBean success() {
ResultBean resultBean = new ResultBean();
resultBean.setCode(0);
resultBean.setMessage("success");
return resultBean;
}
public static <V> ResultBean<V> success(Collection<V> data) {
ResultBean resultBean = new ResultBean();
resultBean.setMessage("success");
resultBean.setData(data);
resultBean.setCode(0);
return resultBean;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("com.dynamic.springbootbvexcetionhandler.util.ResultBean{");
sb.append("code=").append(code);
sb.append(", message='").append(message).append('\'');
sb.append(", data=").append(data);
sb.append('}');
return sb.toString();
}
附
源码下载:https://github.com/bill4j/spring-boot-course/tree/develop/spring-boot-exceptionHandler