zoukankan      html  css  js  c++  java
  • 精通Spring Boot---使用@ControllerAdvice处理异常

    在Spring 3.2中,新增了@ControllerAdvice、@RestControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping、@PostMapping, @GetMapping注解中。
    接下来我将通过代码展示如何使用这些注解,以及处理异常。

    1.注解的介绍

    先定义一个ControllerAdvice。代码如下

    /**
     * @author Lensen
     * @desc
     * @since 2018/10/5 11:01
     */
    @ControllerAdvice
    public class MyExceptionHandler {
    
        /**
         * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
         * @param binder
         */
        @InitBinder
        public void initWebBinder(WebDataBinder binder){
            //对日期的统一处理
            binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
            //添加对数据的校验
            //binder.setValidator();
        }
    
        /**
         * 把值绑定到Model中,使全局@RequestMapping可以获取到该值
         * @param model
         */
        @ModelAttribute
        public void addAttribute(Model model) {
            model.addAttribute("attribute",  "The Attribute");
        }
    
        /**
         * 捕获CustomException
         * @param e
         * @return json格式类型
         */
        @ResponseBody
        @ExceptionHandler({CustomException.class}) //指定拦截异常的类型
        @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) //自定义浏览器返回状态码
        public Map<String, Object> customExceptionHandler(CustomException e) {
            Map<String, Object> map = new HashMap<>();
            map.put("code", e.getCode());
            map.put("msg", e.getMsg());
            return map;
        }
    
        /**
         * 捕获CustomException
         * @param e
         * @return 视图
         */
    //    @ExceptionHandler({CustomException.class})
    //    public ModelAndView customModelAndViewExceptionHandler(CustomException e) {
    //        Map<String, Object> map = new HashMap<>();
    //        map.put("code", e.getCode());
    //        map.put("msg", e.getMsg());
    //        ModelAndView modelAndView = new ModelAndView();
    //        modelAndView.setViewName("error");
    //        modelAndView.addObject(map);
    //        return modelAndView;
    //    }
    }

    需要注意的是使用@ExceptionHandler注解传入的参数可以一个数组,且使用该注解时,传入的参数不能相同,也就是不能使用两个@ExceptionHandler去处理同一个异常。如果传入参数相同,则初始化ExceptionHandler时会失败。
    对于@ControllerAdvice注解,我们来看看源码的定义:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface ControllerAdvice {
        @AliasFor("basePackages")
        String[] value() default {};
    
        @AliasFor("value")
        String[] basePackages() default {};
    
        Class<?>[] basePackageClasses() default {};
    
        Class<?>[] assignableTypes() default {};
    
        Class<? extends Annotation>[] annotations() default {};
    }

    我们可以传递basePackage,声明的类(是一个数组)指定的Annotation参数,具体参考:spring framework doc

    2.异常的处理

    编写自定义异常类

    package com.developlee.errorhandle.exception;
    
    /**
     * @author Lensen
     * @desc 自定义异常类
     * @since 2018/10/5 11:04
     */
    public class CustomException extends RuntimeException {
    
        private long code;
        private String msg;
    
        public CustomException(Long code, String msg){
            this.code = code;
            this.msg = msg;
        }
    
        public long getCode() {
            return code;
        }
    
        public void setCode(long code) {
            this.code = code;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    }

    Spring 对于 RuntimeException类的异常才会进行事务回滚,所以我们一般自定义异常都继承该异常类。

    编写全局异常处理类

    /**
     * @author Lensen
     * @desc
     * @since 2018/10/5 11:01
     */
    @ControllerAdvice("com.developlee.errorhandle")
    public class MyExceptionHandler {
    
        /**
         * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
         * @param binder
         */
        @InitBinder
        public void initWebBinder(WebDataBinder binder){
    
        }
    
        /**
         * 把值绑定到Model中,使全局@RequestMapping可以获取到该值
         * @param model
         */
        @ModelAttribute
        public void addAttribute(Model model) {
            model.addAttribute("attribute",  "The Attribute");
        }
    
        /**
         * 捕获CustomException
         * @param e
         * @return json格式类型
         */
        @ResponseBody
        @ExceptionHandler({CustomException.class}) //指定拦截异常的类型
        @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) //自定义浏览器返回状态码
        public Map<String, Object> customExceptionHandler(CustomException e) {
            Map<String, Object> map = new HashMap<>();
            map.put("code", e.getCode());
            map.put("msg", e.getMsg());
            return map;
        }
    
        /**
         * 捕获CustomException
         * @param e
         * @return 视图
         */
    //    @ExceptionHandler({CustomException.class})
    //    public ModelAndView customModelAndViewExceptionHandler(CustomException e) {
    //        Map<String, Object> map = new HashMap<>();
    //        map.put("code", e.getCode());
    //        map.put("msg", e.getMsg());
    //        ModelAndView modelAndView = new ModelAndView();
    //        modelAndView.setViewName("error");
    //        modelAndView.addObject(map);
    //        return modelAndView;
    //    }
    }

    测试

    在controller中抛出自定义异常

    /**
     * @author Lensen
     * @desc
     * @since 2018/10/5 11:00
     */
    @Controller
    public class DemoController {
      
        /**
       * 关于@ModelAttribute,
       * 可以使用ModelMap以及@ModelAttribute()来获取参数值。
       */    
        @GetMapping("/one")
        public String testError(ModelMap modelMap ) {
            throw new CustomException(500L, "系统发生500异常!" + modelMap.get("attribute"));
        }
    
        @GetMapping("/two")
        public String testTwo(@ModelAttribute("attribute") String attribute) {
            throw new CustomException(500L, "系统发生500异常!" + attribute);
        }
    }

    启动应用,范围localhost:8080/one.返回报文为:

    {"msg":"系统发生500异常!The Attribute","code":500}

    可见我们的@InitBinder和@ModelAttribute注解生效。且自定义异常被成功拦截。如果全部异常处理都返回json,那么可以使用 @RestControllerAdvice 代替 @ControllerAdvice ,这样在方法上就可以不需要添加 @ResponseBody。@RestControllerAdvice在注解上已经添加了@ResponseBody。

  • 相关阅读:
    再论使用Oracle Instant Client连接Oracle
    再谈业务逻辑架构模式(事务脚本,表模块,活动记录,领域模型)
    业务逻辑架构模式(事务脚本,表模块,活动记录,领域模型)
    .net程序集组成与内存布局
    多参的实现原理
    起点
    Windows1[头文件]
    C++中构造函数、析构函数、拷贝构造函数详解
    PHP 开发工具
    写点东西顺便吐槽(很弱很弱的技术文)
  • 原文地址:https://www.cnblogs.com/huangwentian/p/10375274.html
Copyright © 2011-2022 走看看