Spring 全局异常处理三种方式ControllerAdvice+ExceptionHandler

Spring 全局异常处理三种方式ControllerAdvice+ExceptionHandler

1.简介与代码

异常处理方式一. @ExceptionHandler
异常处理方式二. 实现HandlerExceptionResolver接口
异常处理方式三. @ControllerAdvice+@ExceptionHandler

package com.example.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;

/**
 * 异常处理器demo
 */
@RestController
public class ControllerAdviceDemo {
    /**
     * demo1,用于测试当前类异常处理器
     */
    @RequestMapping("/demo1")
    @ResponseBody
    public Object demo1() {
        int i = 1 / 0;
        return new Date();
    }

    /**
     * 方式1:controller中使用ExceptionHandler注解处理异常
     * 注意事项: 1. 一个Controller下多个@ExceptionHandler上的异常类型不能出现一样的,否则运行时抛异常.
     * Ambiguous @ExceptionHandler method mapped for;
     * 2. @ExceptionHandler下方法返回值类型支持多种,常见的ModelAndView,@ResponseBody注解标注,ResponseEntity等类型都OK.
     */
    @ExceptionHandler({RuntimeException.class})
    public String fix(Exception ex) {
        System.out.println("方式1,do This");
        return ex.getMessage();
    }


    /**
     * demo2,用于测试全局异常处理器
     */
    @RequestMapping("/demo2")
    @ResponseBody
    public Object demo2(String msg) throws Exception {
        System.out.println("进入demo2");
        if (StringUtils.isEmpty(msg)) {
            throw new Exception("msg为空");
        }
        return new Date();
    }


}

/**
 * 方式2:全局异常处理,HandlerExceptionResolver方式
 * 只能返回ModelAndView,不能返回json,不常用。
 */
@Component
class MyHandlerExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("方式2,发生全局异常!");
        ModelMap mmp = new ModelMap();
        mmp.addAttribute("ex", ex.getMessage());
        response.addHeader("Content-Type", "application/json;charset=UTF-8");
        try {
            new ObjectMapper().writeValue(response.getWriter(), ex.getMessage());
            response.getWriter().flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new ModelAndView();
    }
}

/**
 * 方式,3:全局异常处理器,所有controller,抛出方法对应的异常将被捕获
 * 用法说明: 这种情况下 @ExceptionHandler 与第一种方式用法相同,
 * 返回值支持ModelAndView,@ResponseBody等多种形式.
 */
@ControllerAdvice
class GlobalController {

    @ResponseBody
    @ExceptionHandler(Exception.class)
    public String fix1(Exception e) {
        System.out.println("方式3,全局的异常处理器");
        return e.getMessage();
    }
}

2.调用demo1

请求:
在这里插入图片描述
日志:

2020-11-26 12:40:04.827  INFO 58496 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-11-26 12:40:04.827  INFO 58496 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-11-26 12:40:04.845  INFO 58496 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 17 ms
方式1,do This
2020-11-26 12:40:04.962  WARN 58496 --- [nio-8081-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [java.lang.ArithmeticException: / by zero]

3.调用demo2

请求:
在这里插入图片描述

日志:

进入demo2
方式3,全局的异常处理器
2020-11-26 12:43:26.406  WARN 58496 --- [nio-8081-exec-3] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [java.lang.Exception: msg为空]

注意:因为方式3的优先级高于方式2,所以这里被方式3捕获到。若去掉方式3的代码,则被方式2捕获。

4. 比较说明

方式1,@Controller+@ExceptionHandler
方式2,HandlerExceptionResolver接口形式
方式3(推荐),@ControllerAdvice+@ExceptionHandler优缺点说明:

在Spring4.3.0版本下,

  1. 优先级来说,@Controller+@ExceptionHandler优先级最高,其次是@ControllerAdvice+@ExceptionHandler,最后才是HandlerExceptionResolver,说明假设三种方式并存的情况 优先级越高的越先选择,而且被一个捕获处理了就不去执行其他的.

  2. 三种方式都支持多种返回类型,@Controller+@ExceptionHandler、@ControllerAdvice+@ExceptionHandler可以使用Spring支持的@ResponseBody、ResponseEntity,而HandlerExceptionResolver方法声明返回值类型只能是 ModelAndView,如果需要返回JSON、xml等需要自己实现.

  3. 缓存利用,@Controller+@ExceptionHandler的缓存信息在ExceptionHandlerExceptionResolver的exceptionHandlerCache,@ControllerAdvice+@ExceptionHandler的缓存信息在ExceptionHandlerExceptionResolver的exceptionHandlerAdviceCache中, 而HandlerExceptionResolver接口是不做缓存的,在前面两种方式都fail的情况下才会走自己的HandlerExceptionResolver实现类,多少有点性能损耗.

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值