Spring异常处理 ExceptionHandler的使用

通常一个web程序在运行过程中,由于用户的操作不当,或者程序的bug,有大量需要处理的异常。其中有些异常是需要暴露给用户的,比如登陆超时,权限不足等等。可以通过弹出提示信息的方式告诉用户出了什么错误。

而这就表示在程序中需要一个机制,去处理这些异常,将程序的异常转换为用户可读的异常。而且最重要的,是要将这个机制统一,提供统一的异常处理。因为我设计这个结构的主要目的是为了简化代码。

在探寻spring的异常处理机制的时候,我分别使用了三种方式。三种方式都是使用的@ExceptionHandler注解。
当一个Controller中有方法加了@ExceptionHandler之后,这个Controller其他方法中没有捕获的异常就会以参数的形式传入加了@ExceptionHandler注解的那个方法中。
首先需要为自己的系统设计一个自定义的异常类,通过它来传递状态码。

/** * Created by liuruijie.

  • 自定义异常
    */
    public class SystemException extends RuntimeException{
    private String code;//状态码
    public SystemException(String message, String code) {
    super(message);
    this.code = code;
    }
    public String getCode() {
    return code;
    }
    }
    第一种思路,设计一个基类。

/**

  • Created by liuruijie.
  • 处理异常的类,需要处理异常的Controller直接继承这个类
    /
    public class BaseController {
    /
    *
    • 处理Controller抛出的异常
    • @param e 异常实例
    • @return Controller层的返回值
      */
      @ExceptionHandler
      @ResponseBody
      public Object expHandler(Exception e){
      if(e instanceof SystemException){
      SystemException ex= (SystemException) e;
      return WebResult.buildResult().status(ex.getCode())
      .msg(ex.getMessage());
      }else{
      e.printStackTrace();
      return WebResult.buildResult().status(Config.FAIL)
      .msg(“系统错误”);
      }
      }
      }
      之后所有需要异常处理的Controller都继承这个类,从而获取到异常处理的方法。
      虽然这种方式可以解决问题,但是极其不灵活,因为动用了继承机制就只为获取一个默认的方法,这显然是不好的。

第二种思路,将这个基类变为接口,提供此方法的默认实现(也就是接口中的default方法,java8开始支持接口方法的默认实现)

/**

  • Created by liuruijie.
  • 接口形式的异常处理
    */
    public interface DataExceptionSolver {
    @ExceptionHandler
    @ResponseBody
    default Object exceptionHandler(Exception e){
    try {
    throw e;
    } catch (SystemException systemException) {
    systemException.printStackTrace();
    return WebResult.buildResult().status(systemException.getCode())
    .msg(systemException.getMessage());
    } catch (Exception e1){
    e1.printStackTrace();
    return WebResult.buildResult().status(Config.FAIL)
    .msg(“系统错误”);
    }
    }
    }
    这种方式虽然没有占用继承,但是也不是很优雅,因为几乎所有的Controller都需要进行异常处理,于是我每个Controller都需要去写implement DataExceptionSolver,这显然不是我真正想要的。况且这种方式依赖java8才有的语法,这是一个很大的局限。

第三种思路,使用加强Controller做全局异常处理。
所谓加强Controller就是@ControllerAdvice注解,有这个注解的类中的方法的某些注解会应用到所有的Controller里,其中就包括@ExceptionHandler注解。
于是可以写一个全局的异常处理类:

/**

  • Created by liuruijie on 2016/12/28.
  • 全局异常处理,捕获所有Controller中抛出的异常。
    */
    @ControllerAdvice
    public class GlobalExceptionHandler {
    //处理自定义的异常
    @ExceptionHandler(SystemException.class)
    @ResponseBody
    public Object customHandler(SystemException e){
    e.printStackTrace();
    return WebResult.buildResult().status(e.getCode()).msg(e.getMessage());
    }
    //其他未处理的异常
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Object exceptionHandler(Exception e){
    e.printStackTrace();
    return WebResult.buildResult().status(Config.FAIL).msg(“系统错误”);
    }
    }
    这个类中只处理了两个异常,但是已经满足了大部分需要,如果还有需要特殊处理的地方,可以再加上处理的方法就行了。第三种实现方式是目前我知道的最优雅的方式了。
    如此,我们现在的Controller中的方法就可以很简洁了,比如处理登陆的逻辑就可以这样简单的写:

/**

  • Created by liuruijie on 2016/12/28.
  • 账号
    */
    @RestController
    @RequestMapping(“passport”)
    public class PassportController {
    PassportService passportService;
    @RequestMapping(“login”)
    public Object doLogin(HttpSession session, String username, String password){
    User user = passportService.doLogin(username, password);
    session.setAttribute(“user”, user);
    return WebResult.buildResult().redirectUrl("/student/index");
    }
    }
    而在passprotService的doLogin方法中,可能会抛出用户名或密码错误等异常,然后就会交由GlobalExceptionHandler去处理,直接返回异常信息给前端,然后前端也不需要关心是否返回了异常,因为这些都已经定义好了。
    前端js代码只需要这样写:

//登陆
AJAX.POST("/passport/login", {
username:name,
password:psw
})
一个异常在其中流转的过程为:
比如doLogin方法抛出了自定义异常,其code为:FAIL,message为:用户名或密码错误,由于在controller的方法中没有捕获这个异常,所以会将异常抛给GlobalExceptionHandler,然后GlobalExceptionHandler通过WebResult将状态码和提示信息返回给前端,前端通过默认的处理函数,弹框提示用户“用户名或密码错误”。而对于这样的一次交互,我们根本不用编写异常处理部分的逻辑。

到这里,代码已经简洁了很多,而且重用性大大提高。

demo: https://github.com/DangerousPrayer/webUtil

作者:JerryL_
链接:https://www.jianshu.com/p/12e1a752974d
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值