Spring的全局(统一)异常处理

异常处理的三种方式

使用 @ExceptionHandler 注解

实现 HandlerExceptionResolver 接口(SpringMVC)

使用 @RestControllerAdvice 注解(诞生于Spring3.2)

官方推荐的是使用@ExceptionHandler注解去捕获固定的异常。

使用统一异常处理,将这些重复的try-catch块抽取出来,这样使我们可以更专注于业务逻辑的处理,同时能够使得异常的处理有一个统一的控制。

使用 @ExceptionHandler 注解

对Controller局部异常处理,@ExceptionHandler注解中可以添加参数,参数是某个异常类的class,代表这个方法专门处理该类异常。

@RestController
public class TestControllerException {

    //单个controller进行异常处理
    @RequestMapping("/testError")
    public String testError() {
        int a = 10 / 0;
        return "this is testError" + a;
    }
    /**
     * 处理其他异常
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public String exceptionHandler(Exception e) {
        System.out.println(e);
        return "this is a controller exception method!";
    }
    /**
     * 处理空指针异常
     */
    @ExceptionHandler(value =NullPointerException.class)
    public String  exceptionHandler(NullPointerException e){
        System.out.println("发生空指针异常!原因是:"+e);
        return "null";
    }
}

实现 HandlerExceptionResolver 接口(SpringMVC)

使用全局异常处理器只需要两步:
1.实现HandlerExceptionResolver接口。
2.将实现类作为Spring Bean,这样Spring就能扫描到它并作为全局异常处理器加载。
实例如下:
配置applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="exceptionResolver" class="com.example.ExceptionResolver"/>
</beans>

编写ExceptionResolver

package com.example;
/**
 * 全局异常处理
 * @Order(-1000) 为了使优先级最高
 * @Component 把普通pojo实例化到spring容器中,
 * 相当于配置文件中的 <bean id="" class=""/>
 */
@Order(-1000)
@Component
public class ExceptionResolver implements HandlerExceptionResolver {

    private static Logger logger = LoggerFactory.getLogger(ExceptionResolver.class);

    @Override
    public ModelAndView resolveException(HttpServletRequest request,
                                         HttpServletResponse response, Object handler, Exception ex) {
        ResultVO result = new ResultVO();
        StringBuilder sb = new StringBuilder();
        System.out.println("执行全局异常处理-----------------------");
        //处理异常
        if(ex instanceof BussinessException) {
            resolverBussinessException(ex, sb, result);
        } else if (ex instanceof BindException) {
            resolverBindException(ex, sb, result);
        } else {
            resolverOtherException(ex, sb, result);
        }

        result.setCode(0);
        result.setResult(sb);
        result.setTime(new Date());

        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Cache-Control", "no-cache, must-revalidate");
        try {
            response.getWriter().write(JSON.toJSONString(result));
        } catch (IOException e) {
            logger.error("异常:" + e.getMessage(), e);
            e.printStackTrace();
        }

        logger.debug("异常:" + ex.getMessage(), ex);
        ex.printStackTrace();
        return new ModelAndView();
    }

    /*
     * 处理业务层异常
     */
    private void resolverBussinessException(Exception ex, StringBuilder sb, ResultVO result) {
        BussinessException businessException = (BussinessException) ex;
        sb.append(businessException.getMsg());
        addResult(result, "业务异常");
    }

    /*
     * 处理参数绑定异常
     */
    private void resolverBindException(Exception ex, StringBuilder sb, ResultVO result) {
        BindException be = (BindException) ex;
        List<FieldError> errorList = be.getBindingResult().getFieldErrors();
        for (FieldError error : errorList) {
            sb.append(error.getObjectName());
            sb.append("对象的");
            sb.append(error.getField());
            sb.append("字段");
            sb.append(error.getDefaultMessage());
        }
        addResult(result, "参数传递异常");
    }

    /*
     * 处理其他异常
     */
    private void resolverOtherException(Exception ex, StringBuilder sb, ResultVO result) {
        sb.append(ex.getMessage());
        addResult(result, "其他异常");
    }

    /*
     * 封装code和msg
     */
    private void addResult(ResultVO result, String msg) {
        result.setMsg(msg);
    }
}

使用 @RestControllerAdvice 注解

如果单使用方式1中的 @ExceptionHandler,只能在当前Controller中处理异常。
但当配合 @RestControllerAdvice 一起使用的时候,则可以全局捕获。

@RestControllerAdvice,是Spring3.2提供的新注解,它是一个Controller增强器,可对controller中定义的接口方法加一些逻辑处理,最常用的就是异常处理。

需要配合@ExceptionHandler使用。
当将异常抛到controller时,可以对异常进行统一处理,规定返回的json格式或是跳转到一个错误页面。

全局异常处理的原理

在独立的一个类中,定义一套对各种异常的处理机制,然后用@RestControllerAdvice注解该类,统一对不同位置的不同异常进行处理。

编写GlobalExceptionHandler全局异常处理类:

@RestControllerAdvice
public class GlobalExceptionHandler
{
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 权限校验异常
     */
    @ExceptionHandler(AccessDeniedException.class)
    public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
        return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权");
    }

    /**
     * 请求方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
            HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
        return AjaxResult.error(e.getMessage());
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(ServiceException.class)
    public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request)
    {
        log.error(e.getMessage(), e);
        Integer code = e.getCode();
        return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
    }

    /**
     * 拦截未知的运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生未知异常.", requestURI, e);
        return AjaxResult.error(e.getMessage());
    }

    /**
     * 系统异常
     */
    @ExceptionHandler(Exception.class)
    public AjaxResult handleException(Exception e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        return AjaxResult.error(e.getMessage());
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public AjaxResult handleBindException(BindException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getAllErrors().get(0).getDefaultMessage();
        return AjaxResult.error(message);
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return AjaxResult.error(message);
    }

    /**
     * 演示模式异常
     */
    @ExceptionHandler(DemoModeException.class)
    public AjaxResult handleDemoModeException(DemoModeException e)
    {
        return AjaxResult.error("演示模式,不允许操作");
    }
}

编写TestAdvice

@RestController
public class TestController extends BaseController
{
    @GetMapping("testException")
    public AjaxResult testException() throws Exception{
        throw new Exception("系统异常");
    }

    @GetMapping("testServiceException")
    public AjaxResult testMyException() throws ServiceException {
        throw new ServiceException("业务异常");
    }
}
posted @ 2022-09-23 17:30  盗梦笔记  阅读(1589)  评论(0编辑  收藏  举报