@ControllerAdvice 拦截异常并统一处理

在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中。参考:@ControllerAdvice 文档

一、介绍

创建 MyControllerAdvice,并添加 @ControllerAdvice注解。

  1.  
    package com.sam.demo.controller;
  2.  
     
  3.  
    import org.springframework.ui.Model;
  4.  
    import org.springframework.web.bind.WebDataBinder;
  5.  
    import org.springframework.web.bind.annotation.*;
  6.  
    import java.util.HashMap;
  7.  
    import java.util.Map;
  8.  
     
  9.  
    /**
  10.  
    * controller 增强器
  11.  
    * @author sam
  12.  
    * @since 2017/7/17
  13.  
    */
  14.  
    @ControllerAdvice
  15.  
    public class MyControllerAdvice {
  16.  
     
  17.  
    /**
  18.  
    * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
  19.  
    * @param binder
  20.  
    */
  21.  
    @InitBinder
  22.  
    public void initBinder(WebDataBinder binder) {}
  23.  
     
  24.  
    /**
  25.  
    * 把值绑定到Model中,使全局@RequestMapping可以获取到该值
  26.  
    * @param model
  27.  
    */
  28.  
    @ModelAttribute
  29.  
    public void addAttributes(Model model) {
  30.  
    model.addAttribute("author", "Magical Sam");
  31.  
    }
  32.  
     
  33.  
    /**
  34.  
    * 全局异常捕捉处理
  35.  
    * @param ex
  36.  
    * @return
  37.  
    */
  38.  
    @ResponseBody
  39.  
    @ExceptionHandler(value = Exception.class)
  40.  
    public Map errorHandler(Exception ex) {
  41.  
    Map map = new HashMap();
  42.  
    map.put("code", 100);
  43.  
    map.put("msg", ex.getMessage());
  44.  
    return map;
  45.  
    }
  46.  
     
  47.  
    }

启动应用后,被 @ExceptionHandler、@InitBinder、@ModelAttribute 注解的方法,都会作用在 被 @RequestMapping 注解的方法上。

@ModelAttribute:在Model上设置的值,对于所有被 @RequestMapping 注解的方法中,都可以通过 ModelMap 获取,如下:

  1.  
    @RequestMapping("/home")
  2.  
    public String home(ModelMap modelMap) {
  3.  
    System.out.println(modelMap.get("author"));
  4.  
    }
  5.  
     
  6.  
    //或者 通过@ModelAttribute获取
  7.  
     
  8.  
    @RequestMapping("/home")
  9.  
    public String home(@ModelAttribute("author") String author) {
  10.  
    System.out.println(author);
  11.  
    }

@ExceptionHandler 拦截了异常,我们可以通过该注解实现自定义异常处理。其中,@ExceptionHandler 配置的 value 指定需要拦截的异常类型,上面拦截了 Exception.class 这种异常。

二、自定义异常处理(全局异常处理)

spring boot 默认情况下会映射到 /error 进行异常处理,但是提示并不十分友好,下面自定义异常处理,提供友好展示。

1、编写自定义异常类:

  1.  
    package com.sam.demo.custom;
  2.  
     
  3.  
    /**
  4.  
    * @author sam
  5.  
    * @since 2017/7/17
  6.  
    */
  7.  
    public class MyException extends RuntimeException {
  8.  
     
  9.  
    public MyException(String code, String msg) {
  10.  
    this.code = code;
  11.  
    this.msg = msg;
  12.  
    }
  13.  
     
  14.  
    private String code;
  15.  
    private String msg;
  16.  
     
  17.  
    // getter & setter
  18.  
    }
注:spring 对于 RuntimeException 异常才会进行事务回滚。

2、编写全局异常处理类

创建 MyControllerAdvice.java,如下:

  1.  
    package com.sam.demo.controller;
  2.  
     
  3.  
    import org.springframework.ui.Model;
  4.  
    import org.springframework.web.bind.WebDataBinder;
  5.  
    import org.springframework.web.bind.annotation.*;
  6.  
     
  7.  
    import java.util.HashMap;
  8.  
    import java.util.Map;
  9.  
     
  10.  
    /**
  11.  
    * controller 增强器
  12.  
    *
  13.  
    * @author sam
  14.  
    * @since 2017/7/17
  15.  
    */
  16.  
    @ControllerAdvice
  17.  
    public class MyControllerAdvice {
  18.  
     
  19.  
    /**
  20.  
    * 全局异常捕捉处理
  21.  
    * @param ex
  22.  
    * @return
  23.  
    */
  24.  
    @ResponseBody
  25.  
    @ExceptionHandler(value = Exception.class)
  26.  
    public Map errorHandler(Exception ex) {
  27.  
    Map map = new HashMap();
  28.  
    map.put("code", 100);
  29.  
    map.put("msg", ex.getMessage());
  30.  
    return map;
  31.  
    }
  32.  
     
  33.  
    /**
  34.  
    * 拦截捕捉自定义异常 MyException.class
  35.  
    * @param ex
  36.  
    * @return
  37.  
    */
  38.  
    @ResponseBody
  39.  
    @ExceptionHandler(value = MyException.class)
  40.  
    public Map myErrorHandler(MyException ex) {
  41.  
    Map map = new HashMap();
  42.  
    map.put("code", ex.getCode());
  43.  
    map.put("msg", ex.getMsg());
  44.  
    return map;
  45.  
    }
  46.  
     
  47.  
    }

3、controller中抛出异常进行测试。

  1.  
    @RequestMapping("/home")
  2.  
    public String home() throws Exception {
  3.  
     
  4.  
    // throw new Exception("Sam 错误");
  5.  
    throw new MyException("101", "Sam 错误");
  6.  
     
  7.  
    }

启动应用,访问:http://localhost:8080/home ,正常显示以下json内容,证明自定义异常已经成功被拦截。

{"msg":"Sam 错误","code":"101"}

* 如果不需要返回json数据,而要渲染某个页面模板返回给浏览器,那么MyControllerAdvice中可以这么实现:

  1.  
    @ExceptionHandler(value = MyException.class)
  2.  
    public ModelAndView myErrorHandler(MyException ex) {
  3.  
    ModelAndView modelAndView = new ModelAndView();
  4.  
    modelAndView.setViewName("error");
  5.  
    modelAndView.addObject("code", ex.getCode());
  6.  
    modelAndView.addObject("msg", ex.getMsg());
  7.  
    return modelAndView;
  8.  
    }

在 templates 目录下,添加 error.ftl(这里使用freemarker) 进行渲染:

  1.  
    <!DOCTYPE html>
  2.  
    <html lang="en">
  3.  
    <head>
  4.  
    <meta charset="UTF-8">
  5.  
    <title>错误页面</title>
  6.  
    </head>
  7.  
    <body>
  8.  
    <h1>${code}</h1>
  9.  
    <h1>${msg}</h1>
  10.  
    </body>
  11.  
    </html>

重启应用,http://localhost:8080/home 显示自定的错误页面内容。

补充:如果全部异常处理返回json,那么可以使用 @RestControllerAdvice 代替 @ControllerAdvice ,这样在方法上就可以不需要添加 @ResponseBody。

posted @ 2021-02-08 10:04  上台阶  阅读(162)  评论(0编辑  收藏  举报