文档章节

spring boot / cloud (十二) 异常统一处理进阶

wangkang80
 wangkang80
发布于 2017/08/23 13:26
字数 1183
阅读 2423
收藏 4

spring boot / cloud (十二) 异常统一处理进阶

前言

spring boot / cloud (二) 规范响应格式以及统一异常处理这篇博客中已经提到了使用@ExceptionHandler来处理各种类型的异常,这种方式也是互联网上广泛的方式

今天这篇博客,将介绍一种spring boot官方文档上的统一处理异常的方式.大家可以在spring boot 官方文档查看介绍

在开始介绍新的方法之前 , 我们先来分析一下 , 以前的做法有那些地方是需要优化的

场景分析

通常我们需要做统一异常处理的需求,大概都是要规范异常输出,以及处理,通同一套抽象出来的逻辑来处理所有异常.

但是在当前流行RestFul风格接口的环境下,对异常的输出还做了额外的一个要求,就是针对不同的错误需要输出对应的http状态.

在前面的实现中,我们大可以指定一个处理Exception的@ExceptionHandler,这样所有异常都能囊括了,但是却无法很好的将http状态区分开来.

如果要实现不同的异常输出不同的http状态,在原来的做法里就要将每个异常都穷举出来,然后做不同的设定.

显然,我们是不希望这样做的,显得太不聪明,不过还好,spring已经帮我们把这一步已经做掉了,我们只需处理自己关心的异常即可

@ExceptionHandler(value = 要拦截的异常.class)
@ResponseStatus(响应状态)
@ResponseBody
public RestResponse<String> exception(要拦截的异常 exception) {
  return new RestResponse<>(ErrorCode.ERROR, buildError(exception));
}

@ExceptionHandler(value = Exception.class)
@ResponseStatus(500)
@ResponseBody
public RestResponse<String> exception(Exception exception) {
  return new RestResponse<>(ErrorCode.ERROR, buildError(exception));
}

源码解读

官方文档中指出,你需要实现一个类,使用@ControllerAdvice标注,然后继承至ResponseEntityExceptionHandler类.

这个ResponseEntityExceptionHandler类是一个抽象类,如下是它的核心方法

@ExceptionHandler({
        NoSuchRequestHandlingMethodException.class,
        HttpRequestMethodNotSupportedException.class,
        .....省略
    })
public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
        HttpHeaders headers = new HttpHeaders();
        if (ex instanceof NoSuchRequestHandlingMethodException) {
            HttpStatus status = HttpStatus.NOT_FOUND;
            return handleNoSuchRequestHandlingMethod(
            (NoSuchRequestHandlingMethodException) ex, 
            headers, status, request);
        }
        else if (ex instanceof HttpRequestMethodNotSupportedException) {
            HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED;
            return handleHttpRequestMethodNotSupported(
            (HttpRequestMethodNotSupportedException) ex, headers, status, request);
        } else if (..............){
            .....省略
        } else {
            .....省略
            return handleExceptionInternal(ex, null, headers, status, request);
        }
}

在以上的代码片段中,我们可以看到handleException方法已经把常见的异常都拦截掉了,并且做出了适当的处理,并且在最后,else分支里,调用了handleExceptionInternal方法,

这个方法就是处理没有被拦截到的异常,然后这也是我们要进行扩展的地方

实现

实现ExceptionHandle类,继承至ResponseEntityExceptionHandler,并且注解@ControllerAdvice

@ControllerAdvice
@Slf4j
public class ExceptionHandle extends ResponseEntityExceptionHandler {
    .....
}

实现exception方法,使用@ExceptionHandler拦截Exception,那么在这里,所有的异常都会进入这个方法进行处理.

然后调用父类的handleException方法(上面提到的),让spring默认的异常处理先处理一遍,如果当前的异常恰巧是被spring拦截的,那么就用spring的默认实现处理,就无需在写额外的代码了,http状态码也一并的会设置好.

最后在调用我们即将要重写的方法handleExceptionInternal,来处理自定义异常以及规范异常输出

@ExceptionHandler(value = Exception.class)
public ResponseEntity<Object> exception(Exception ex, WebRequest request) {
    ResponseEntity<Object> objectResponseEntity = this.handleException(ex, request);
    return this.handleExceptionInternal(ex, null, objectResponseEntity.getHeaders(), objectResponseEntity.getStatusCode(), request);
}

重写handleExceptionInternal方法,

在这个方法里面,可以向如下实现一样,去处理项目中自定义的异常,将其规范为想要的输出格式,

最后再调用父类的handleExceptionInternal方法,将控制权交还给spring,

这样就完成了整个异常处理的流程

@Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
    HttpStatus localHttpStatus = status;
    ErrorResult errorResult = buildError(applicationConfig, ex);
    if (ex instanceof PermissionException) { //权限异常
        localHttpStatus = HttpStatus.FORBIDDEN;
    } else if (ex instanceof AuthException) { //认证异常
        localHttpStatus = HttpStatus.UNAUTHORIZED;
    } else if (ex instanceof ParameterValidException) { //参数校验异常
        localHttpStatus = HttpStatus.BAD_REQUEST;
    } else if (ex instanceof RestClientResponseException) { //rest请求异常
        try {
            RestClientResponseException restClientResponseException = (RestClientResponseException) ex;
            String data = restClientResponseException.getResponseBodyAsString();
            if (StringUtils.isNotBlank(data)) {
                RestResponse<String> child = objectMapper.readValue(data, objectMapper.getTypeFactory().constructParametricType(RestResponse.class, String.class));
                errorResult.setChild(child);
            }
        } catch (IOException e) {
            throw new SystemRuntimeException(e);
        }
    }
    log.error(ex.getClass().getName(), ex);
    return super.handleExceptionInternal(ex, new RestResponse<>(localHttpStatus, errorResult), headers, localHttpStatus, request);
}

结束

在上面我们优化了统一异常处理的代码,做到了只关心系统自定义异常的处理,框架和容器的异常处理,交由spring处理,简化了代码,避免了重复造轮子,同时代码也更加健壮了.

在下一篇文章中,我会介绍另外一个更合里的处理404错误的方式,敬请期待

代码仓库 (博客配套代码)


想获得最快更新,请关注公众号

想获得最快更新,请关注公众号

© 著作权归作者所有

wangkang80
粉丝 369
博文 22
码字总数 34117
作品 3
浦东
高级程序员
私信 提问
UDF 集成样例--udf-sample

UDF 基于spring boot / spring cloud 的基础项目,脚手架,主要用于学习和实践 按照spring boot的思想,将各个不同的功能按照starter的形式拆分开来,做到灵活组合 物理架构示意 CI & CD 示意 代...

wangkang80
2017/08/27
989
0
spring boot 1.5.4 统一异常处理(九)

上一篇:springboot 1.5.4 配置文件详解(八) 1 Spring Boot统一异常处理 Spring Boot中实现了默认的error映射,但是在实际应用中,上面你的错误页面对用户来说并不够友好,我们通常需要去实...

wyait
2017/09/27
0
0
Spring Cloud(十二):分布式链路跟踪 Sleuth 与 Zipkin【Finchley 版】

随着业务发展,系统拆分导致系统调用链路愈发复杂一个前端请求可能最终需要调用很多次后端服务才能完成,当整个请求变慢或不可用时,我们是无法得知该请求是由某个或某些后端服务引起的,这时...

吴伟祥
03/21
82
0
Spring Cloud Spring Boot mybatis分布式微服务云架构 返回JSON格式

在上述例子中,通过统一定义不同Exception映射到不同错误处理页面。而当我们要实现RESTful API时,返回的错误是JSON格式的数据,而不是HTML页面,这时候我们也能轻松支持。 本质上,只需在之...

itcloud
2018/06/21
27
0
spring boot 1.5.4 从入门到实践

Spring Boot四个重要核心: 自动配置:针对很多Sping应用程序常见的应用功能,Spring Boot能自动提供相关配置; 起步依赖:告诉Spring Boot需要什么功能,它就能引入需要的库; 命令行界面:...

wyait
2017/10/31
0
0

没有更多内容

加载失败,请刷新页面

加载更多

java通过ServerSocket与Socket实现通信

首先说一下ServerSocket与Socket. 1.ServerSocket ServerSocket是用来监听客户端Socket连接的类,如果没有连接会一直处于等待状态. ServetSocket有三个构造方法: (1) ServerSocket(int port);...

Blueeeeeee
今天
6
0
用 Sphinx 搭建博客时,如何自定义插件?

之前有不少同学看过我的个人博客(http://python-online.cn),也根据我写的教程完成了自己个人站点的搭建。 点此:使用 Python 30分钟 教你快速搭建一个博客 为防有的同学不清楚 Sphinx ,这...

王炳明
昨天
5
0
黑客之道-40本书籍助你快速入门黑客技术免费下载

场景 黑客是一个中文词语,皆源自英文hacker,随着灰鸽子的出现,灰鸽子成为了很多假借黑客名义控制他人电脑的黑客技术,于是出现了“骇客”与"黑客"分家。2012年电影频道节目中心出品的电影...

badaoliumang
昨天
15
0
很遗憾,没有一篇文章能讲清楚线程的生命周期!

(手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本。 简介 大家都知道线程是有生命周期,但是彤哥可以认真负责地告诉你网上几乎没有一篇文章讲得是完全正确的。 ...

彤哥读源码
昨天
17
0
jquery--DOM操作基础

本文转载于:专业的前端网站➭jquery--DOM操作基础 元素的访问 元素属性操作 获取:attr(name);$("#my").attr("src"); 设置:attr(name,value);$("#myImg").attr("src","images/1.jpg"); ......

前端老手
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部