@ControllerAdvice 和 @ExceptionHandler注解处理全局异常

@ControllerAdvice 和 @ExceptionHandler注解处理全局异常,第1张

@ControllerAdvice 和 @ExceptionHandler注解处理全局异常

前言:@ControllerAdvice ,很多初学者可能都没有听说过这个注解,实际上,这是一个非常有用的解,顾名思义,这是一个增强的 Controller。一般配合@ExceptionHandler使用来处理全局异常。

一、@ControllerAdvice

@ControllerAdvice 是Spring 3.2提供的新注解,他是一个controller增强器,可以对controller中使用到@RequestMapping注解的方法做逻辑处理。

使用这个注解 ,可以实现三个方面的功能:

  • 全局异常处理

  • 全局数据绑定

  • 全局数据预处理

灵活使用这三个功能,可以帮助我们简化很多工作,需要注意的是,这是 SpringMVC 提供的功能,在 Spring Boot 中可以直接使用,这里只介绍全局异常处理,需要其他功能可以访问参考链接。

全局异常处理

使用 @ControllerAdvice结合@ExceptionHandler 实现全局异常处理,只需要定义类,添加该注解即可定义方式如下:

@ControllerAdvice
public class MyGlobalExceptionHandler 
{
    @ExceptionHandler(Exception.class)
    public ModelAndView customException(Exception e) 
    {
        ModelAndView mv = new ModelAndView();
        mv.addObject("message", e.getMessage());
        mv.setViewName("myerror");
        return mv;
    }
}

在该类中,可以定义多个方法,不同的方法处理不同的异常,例如专门处理空指针的方法、专门处理数组越界的方法...,也可以直接向上面代码一样,在一个方法中处理所有的异常信息。

@ExceptionHandler 注解用来指明异常的处理类型,即如果这里指定为 NullpointerException,则数组越界异常就不会进到这个方法中来。


二、为什么要做Controller层的异常统一处理及返回

不知道你平时在写Controller层接口的时候,有没有注意过抛出异常该怎么处理,是否第一反应是想着用个try-catch来捕获异常?

但是这样地处理只适合那种编译器主动提示的检查时异常,因为你不用try-catch就过不了编译检查,所以你能主动地抓获异常并进行处理。但是,如果存在运行时异常且你没有来得及想到去处理它的时候会发生什么呢?我们可以来先看看下面的这个没有处理运行时异常的例子:

@RestController
public class ExceptionRest {
    @GetMapping("getNullPointerException")
    public Map getNullPointerException(){
        throw new NullPointerException("出现了空指针异常");
    }
}

以上代码在基于maven的SpringMVC项目中,使用tomcat启动后,浏览器端发起如下请求

http://localhost:8080/zxtest/getNullPointerException

访问后得到的结果是这样的,浏览器收到的报错信息:

 可以看到,我们在Controller接口层抛出了一个空指针异常,然后没有捕获,结果异常堆栈就会返回给前端浏览器,给用户造成了非常不好的体验。

除此之外,前端从报错信息中能看到后台系统使用的服务器及中间件类型、所采用的框架信息及类信息,甚至如果后端抛出的是SQL异常,那么还可以看到SQL异常的具体查询的参数信息,这是一个中危安全漏洞,是必须要修复的。


三、使用@ExceptionHandler和@ControllerAdvice做到统一处理

当出现这种运行时异常的时候,我们想到的最简单的方法也许就是给可能会抛出异常的代码加上异常处理,如下所示:

@RestController
public class ExceptionRest {
    private Logger log = LoggerFactory.getLogger(ExceptionRest.class);
    @GetMapping("getNullPointerException")
    public Map getNullPointerException(){
        Map returnMap = new HashMap();
        try{
            throw new NullPointerException("出现了空指针异常");
        }catch(NullPointerException e){
            log.error("出现了空指针异常",e);
            returnMap.put("success",false);
            returnMap.put("mesg","请求发生异常,请稍后再试");
        }
        return returnMap;
    }
}

因为我们手动地在抛出异常的地方加上了处理,并妥善地返回发生异常时该返回给前端的内容,因此,当我们再次在浏览器发起相同的请求时得到就是以下内容:

{
success: false,
mesg: "请求发生异常,请稍后再试"
}

貌似问题得到了解决,但是你能确保你可以在所有可能会发生异常的地方都正好捕获了异常并处理吗?你能确保团队的其他人也这么做?

很明显,你需要一个统一的异常捕获与处理方案。

使用@ExceptionHandler

Spring3.2以后,SpringMVC引入了ExceptionHandler的处理方法,使得对异常的处理变得更加简单和精确,你唯一需要做的就是新建一个Controller,然后再里面加上两个注解即可完成Controller层所有异常的捕获与处理。

3.1、基本使用

新建一个Controller如下:

@ControllerAdvice
public class ExceptionConfigController {
    @ExceptionHandler
    public ModelAndView exceptionHandler(Exception e){
        ModelAndView mv = new ModelAndView(new MappingJackson2JsonView());
        mv.addObject("success",false);
        mv.addObject("mesg","请求发生了异常,请稍后再试");
        return mv;
    }
}

我们在如上的代码中,类上加了@ControllerAdvice注解,表示它是一个增强版的controller,然后在里面创建了一个返回ModelAndView对象的exceptionHandler方法,其上加上@ExceptionHandler注解,表示这是一个异常处理方法,然后在方法里面写上具体的异常处理及返回参数逻辑即可,如此就完成了所有的工作,真的是太方便了。

我们在浏览器发起调用后就返回了如下的结果:

{
success: false,
mesg: "请求发生了异常,请稍后再试"
}
3.2、 具体异常的处理

相比与HandlerExceptionResolver而言,使用@ExceptionHandler更能灵活地对不同的异常进行分别的处理。并且,当抛出的异常是指定异常的子类,那么照样能够被捕获和处理。

我们改变下controller层的代码如下:

@RestController
public class ExceptionController {

    @GetMapping("getNullPointerException")
    public Map getNullPointerException() {
        throw new NullPointerException("出现了空指针异常");
    }

    @GetMapping("getClassCastException")
    public Map getClassCastException() {
        throw new ClassCastException("出现了类型转换异常");
    }

    @GetMapping("getIOException")
    public Map getIOException() throws IOException {
        throw new IOException("出现了IO异常");
    }
}

已知NullPointerException和ClassCastException都继承RuntimeException,而RuntimeException和IOException都继承Exception。

我们在ExceptionConfigController做这样的处理:

@ControllerAdvice
public class ExceptionConfigController {
    // 专门用来捕获和处理Controller层的空指针异常
    @ExceptionHandler(NullPointerException.class)
    public ModelAndView nullPointerExceptionHandler(NullPointerException e){
        ModelAndView mv = new ModelAndView(new MappingJackson2JsonView());
        mv.addObject("success",false);
        mv.addObject("mesg","请求发生了空指针异常,请稍后再试");
        return mv;
    }

    // 专门用来捕获和处理Controller层的运行时异常
    @ExceptionHandler(RuntimeException.class)
    public ModelAndView runtimeExceptionHandler(RuntimeException e){
        ModelAndView mv = new ModelAndView(new MappingJackson2JsonView());
        mv.addObject("success",false);
        mv.addObject("mesg","请求发生了运行时异常,请稍后再试");
        return mv;
    }

    // 专门用来捕获和处理Controller层的异常
    @ExceptionHandler(Exception.class)
    public ModelAndView exceptionHandler(Exception e){
        ModelAndView mv = new ModelAndView(new MappingJackson2JsonView());
        mv.addObject("success",false);
        mv.addObject("mesg","请求发生了异常,请稍后再试");
        return mv;
    }
}

那么

  • 当我们在Controller层抛出NullPointerException时,就会被nullPointerExceptionHandler进行处理,然后拦截。

    {
    success: false,
    mesg: "请求发生了空指针异常,请稍后再试"
    }
    
  • 当我们在Controller层抛出ClassCastException时,就会被runtimeExceptionHandler进行处理,然后拦截。

    {
    success: false,
    mesg: "请求发生了运行时异常,请稍后再试"
    }
    
  • 当我们在Controller层抛出IOException时,就会被exceptionHandler进行处理,然后拦截。

    {
    success: false,
    mesg: "请求发生了异常,请稍后再试"
    }
总结

SpringMVC为我们提供的Controller层异常处理真的是太方便了,尤其是@ExceptionHandler,推荐大家使用。


参考链接:

Controller层的异常统一处理及返回

SpringMVC 中 @ControllerAdvice 注解的三种使用场景!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5609226.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-15

发表评论

登录后才能评论

评论列表(0条)

保存