博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Boot 全局异常处理
阅读量:6941 次
发布时间:2019-06-27

本文共 4441 字,大约阅读时间需要 14 分钟。

hot3.png

       当我们在开发一个项目时,往往需要对异常进行捕获处理,以提供友好的信息展示给用户。但随着业务的增长,项目越来越复杂,需要捕获异常的地方就会越来越多,如果每个地方都进行try catch,那代码将会变得非常冗余且不好维护。

       我们知道Spring Boot默认情况下会映射到 /error 进行异常处理,但是提示并不十分友好。那有没有一种统一的处理机制?幸好从Spring 3.2以后新增了@ControllerAdvice 注解,使用AOP对Controller控制器进行增强(前置增强、后置增强、环绕增强),那么我们就可以对控制器的方法进行调用前(前置增强)和调用后(后置增强)的处理。

       Spring还提供了@ExceptionHandler异常增强注解,一般需要配合@RequestBody注解使用。程序如果在执行控制器方法前或执行时抛出异常,会被@ExceptionHandler注解了的方法处理。如果全部异常处理返回json格式,那么可以使用@RestControllerAdvice 代替@ControllerAdvice,这样在方法上就可以不需要添加@ResponseBody。

自定义异常类

定义一个AgException作为全局的自定义异常。继承RuntimeException(运行时异常)对代码无可侵入性,不需要方法中强制捕获或者抛出。

@Getter@Slf4jpublic class AgException extends RuntimeException {    /**     * 响应状态码枚举     */    private StatusResultEnum statusResult;    private Object[] args;    /**     * 构造指定异常代码与消息参数的业务异常。     *     * @param statusResult 异常代码     * @param args 消息参数,该参数将用于格式化异常代码中的消息字符串     */    public AgException(StatusResultEnum statusResult, Object... args) {        this(statusResult, null, args);    }    /**     * 构造指定异常代码、异常原因与消息参数的业务异常。     *     * @param statusResult 异常代码     * @param cause 异常消息     * @param args 消息参数,该参数将用于格式化异常代码中的消息字符串     */    public AgException(StatusResultEnum statusResult, Throwable cause, Object... args) {        super(statusResult.getCodeMsg(args), cause);        log.error("系统异常:{} ", cause.getMessage(), cause);        this.args = args;        this.statusResult = statusResult;    }}

添加自定义信息枚举类

public enum StatusResultEnum {    SUCCESS("2000", "success", "请求成功"),    /**     * 可预知异常     */    NOT_LOGIN_IN("4001", "未登录", "未登录"),    /**     * 不可预知异常,但有明确错误码     */    UN_AUTHORIZED("4002", "权限不足", "权限不足"),    /**     * 不可预知异常,默认错误     */    INTERNAL_SERVER_ERROR("5000", "%1s", "内部服务器错误,请联系客服人员。");    /**     * 响应返回码     */    @Getter    private String code;    /**     * 响应描述,面向开发者     */    @Setter    private String codeMsg;    /**     * 响应描述,面向用户     */    @Setter    private String statusMsg;    StatusResultEnum(String code, String codeMsg, String statusMsg) {        this.code = code;        this.codeMsg = codeMsg;        this.statusMsg = statusMsg;    }    /**     * 根据指定的占位符参数格式化异常消息。     *     * @param args 占位符参数     * @return 格式化后的异常消息     */    public String getCodeMsg(Object... args) {        return ErrorCodeUtils.formatMessage(codeMsg, args);    }    /**     * 根据指定的占位符参数格式化异常消息。     *     * @param args 占位符参数     * @return 格式化后的异常消息     */    public String getStatusMsg(Object... args) {        return ErrorCodeUtils.formatMessage(statusMsg, args);    }}

全局异常处理类

  • 对异常进行归类:可预知异常(即自定义异常AgException);不可预知异常,但需要明确定义错误码;不可预知异常,不需特殊处理错误码。
  • 使用Spring MVC控制器增强,捕获全局异常。
  • 捕获AgException业务异常,取出错误码和信息构造响应。
  • 使用一个线程安全、并且不可更改的map存储不可预知异常自定义的错误信息。
  • 捕获AgException以外的异常(Exception),判断map是否定义了该异常错误信息,若有定义取出错误信息构造响应,否则返回默认错误。
@RestControllerAdvice@Slf4jpublic class AgExceptionHandler {    /**     * 线程安全     */    private static final ImmutableMap
, StatusResultEnum> EXCEPTIONS; static { final ImmutableMap.Builder
, StatusResultEnum> builder = ImmutableMap.builder(); builder.put(LockedAccountException.class, StatusResultEnum.IDENTITY_AUTH_FAIL); builder.put(UnknownAccountException.class, StatusResultEnum.IDENTITY_AUTH_FAIL); builder.put(IncorrectCredentialsException.class, StatusResultEnum.IDENTITY_AUTH_FAIL); builder.put(DisabledAccountException.class, StatusResultEnum.IDENTITY_AUTH_FAIL); builder.put(UnauthorizedException.class, StatusResultEnum.UN_AUTHORIZED); builder.put(MissingServletRequestParameterException.class, StatusResultEnum.REQUIRE_ARGUMENT); // 其他未被发现的异常 builder.put(Exception.class, StatusResultEnum.INTERNAL_SERVER_ERROR); EXCEPTIONS = builder.build(); } @ExceptionHandler(AgException.class) public BaseResponse handleAgException(Throwable e) { AgException agException = (AgException) e; return new ResultResponse(agException.getStatusResult(), agException.getArgs()); } @ExceptionHandler(Exception.class) public BaseResponse handleException(Exception e) { log.error("系统异常:{} ", e.getMessage(), e); StatusResultEnum statusResultEnum = EXCEPTIONS.get(e.getClass()); return new ResultResponse(statusResultEnum, e.getMessage()); }}

 

 c454eb6d88900b351ae00015e38f0ae0c41.jpg

总结

异常抛出的顺序为Dao—Service—Controller—AgExceptionHandler,SpringMVC增强的即是在Controller层进行拦截,实现全局异常统捕获,异常在AgExceptionHandler 统一处理后,就无需再代码中单独对每个服务进行try catch,此种实现方式代码不仅重用性高,而易于扩展。

参考

转载于:https://my.oschina.net/lienson/blog/3054971

你可能感兴趣的文章
关于用VS实现开机自启动功能(win7/winXp)
查看>>
重拾Python 笔记五
查看>>
Yii 日志
查看>>
我的友情链接
查看>>
3.2 双向链表
查看>>
MySQL for Mac 安装和基本操作
查看>>
MFC does not support WINVER less than 0x0501.
查看>>
如何用一年时间学完MIT四年的计算机科学课程
查看>>
网络之二
查看>>
Go应用程序使用dockerfile multi-stage的问题
查看>>
吐槽:iOS要获得联系人所属分组好麻烦
查看>>
中文环境下的Weblogic
查看>>
[Android]开源中国源码分析——UI设计XML文件分析
查看>>
Angular CLI 常用命令
查看>>
Nginx的安装和配置
查看>>
mysql 5.6.27 rpm 安装
查看>>
Android Studio 创建项目常见问题
查看>>
在主机用ssh连接vitualbox虚拟机 nat端口转发 ssh连接
查看>>
Google Map For Android
查看>>
【转载】Hibernate3注解
查看>>