异常问题的思考:
今天遇到一个问题:NullPointerException: temporal
看见这个报错懵逼的,temporal是个啥,首先想起了百度,但一番折腾下来,并没有得到想要的答案,不过找到一些关于temporal的蛛丝马迹,这是一个关键词与java中的Data日期格式转换有关。于是我又查看了下具体的log,果然有个log中清清楚楚的写出了某一行报错。
血崩的一天从早上开始- -,具体的错误是某个数据的日期我使用sdf(simpleDateFormat)转换日期格式时,没有对该处进行判空。苦逼的我还吭哧吭哧百度半天没找到原因。于是又开始了提交代码->打包->发布环境->验证的老套路。
仔细反思了一下异常处理机制:
1.异常代码设计(异常出现前):
首先第一点很重要,每个系统都需要一套完整的异常捕获机制来应对系统出现各种问题时,帮助我们更快的找到出错的地点,也可以将错误信息通过接口传递给前端,提示给用户,这就涉及到全局异常捕获机制。
(1)首先需要定义一个Exception类,专门存放这些异常,如上图我本人建了几个异常类
(2)在异常类里规定了一些异常代码和异常信息,如下图:
public class EasiControllerException extends RuntimeException {
/**
* <p>
* Field code: 异常代码
* </p>
*/
private String status;//状态0=正常1=异常
private String message;//状态信息
public EasiControllerException() {
}
/**
* 默认构造函数
*
* @param code 异常代码
* @param message 异常信息
*/
public EasiControllerException(String code, String message) {
super(message);
this.status = code;
}
public EasiControllerException(String message) {
super(message);
this.message = message;
}
public EasiControllerException(String code, String message, Throwable throwble) {
super(message, throwble);
this.status = code;
}
public String getStatus() {
return status;
}
public void setStatus(String code) {
this.status = code;
}
public String getMessage() {
return null == message ? super.getMessage() : message;
}
public void setMessage(String message) {
this.message = message;
}
}
(3)配置一个全局捕获异常类,用于每次抛出自定义异常时捕获并且抛出:
@ControllerAdvice(annotations = Controller.class)
@ResponseBody
public class GlobalExceptionHandler {
protected final Logger log = Logger.getLogger(this.getClass());
@ExceptionHandler
@ResponseBody
public ApiResult exceptionHandler(Exception e) {
ApiResult apiResult;
if(e instanceof EasiAopException
|| e instanceof EasiControllerException
|| e instanceof EasiServiceException){
apiResult = ApiResult.newInstance(Constants.FLAG_F,
e.getMessage(),
Constants.showInfo(e.getMessage()),
null);
}
else{
apiResult = ApiResult.newInstance(Constants.FLAG_F,
Constants.ERROR_900.ERROR_CODE901,
e.toString(),
null);
}
log.error("response Exception String:" + JsonUtil.beanToJson(apiResult));
return apiResult;
}
}
(4)使用自定义的异常类:
throw new EasiControllerException(Constants.ERROR_000.ERROR_CODE001);
这样就能使用自己定义的异常类,被捕获后通过接口传输给前端,将具体的错误显示出来。
(4)使用catch(XXXException)时,范围由小到大:
很多萌新在写代码时遇见需要抛异常的全篇就是这样玩,咔咔咔套路一般写的贼6
try{
xxx
}catch(Exception e){
log.info(e.getMessage);
throw new Exception();
}
当每次写异常时,多思考一步,除了Exception这个大家伙意外,我们能不能精确的遇见其他类异常,比如我在和其他供应商对接接口时,异常可不仅仅只是一个Exception这么简单,需要考虑网络连接的ConnectException、SocketTimeoutException、IOException。。。每种异常都需要不通的处理方式,而不是简简单单的抛出即可完事。
try{
xxx
}catch(ConnectException e){
throw new XXXException("0","网络连接异常");
}catch(IOException e){
throw new XXXException("0","IO异常,请检查xxx");
}
if(resultCode.equals"0"){
throw new XXXException("0","调用xx接口返回信息异常");
}
...
2.追踪异常,捕获有用可靠消息(出现异常信息):
刚刚说到的是在发生异常之前,我们要做好防范及时抛出异常,使我们在系统“冒烟时”,能通过异常信息准确定位问题,接下来就要说说如何看log日志文件。
相信大部分人看见异常首先就是拷下报错信息,打开百度,搜一波到底这个异常代表啥意思,到底是空指针呢,还是类型转换错误呢还是其他等等等,然后定位了错误信息再看自己的代码哪块跟这个错误信息相符合,然后再改。
这样其实是错误的。log日志给予了我们很大的帮助,有用的可不单单就那一行java.lang.NullPointerException: temporal 耐着性子往下找,通常都是有小到大一层层的将错误代码的行号给展示出来,只需要找代码层面的信息,如某个类的某一行,看日志基本就是看自己认识的部分,自己写的代码总归认识吧,是controller层?service层?还是Dao层等等,这时根据行号精确定位导致报错的代码行,再回过头去看最上头的那行,再百度,这样效率会大大提升。(大佬都这么干)
3.修改代码(找到错误以后):
最后,历经千辛万苦,修改完代码-预编译成功-测试没问题-准备发布?
No No No 还有最重要的一步,就是要总结经验教训嘛,不然同样的坑再摔就没脸了,至少以后在训诫菜鸟的时候,还能吹牛逼,本帅当年可是就经历过一次这种事情,多学着点!