美文网首页
@ControllerAdvice的使用

@ControllerAdvice的使用

作者: 东南枝下 | 来源:发表于2022-02-21 17:45 被阅读0次

@ControllerAdvice是在类上声明的注解,其用法主要有三点:

  1. @ExceptionHandler注解标注的方法:用于捕获Controller中抛出的不同类型的异常,从而达到异常全局处理的目的;
  2. @InitBinder注解标注的方法:用于请求中注册自定义参数的解析,从而达到自定义请求参数格式的目的;
  3. @ModelAttribute注解标注的方法:表示此方法会在执行目标Controller方法之前执行 。

@ExceptionHandler

使用@ExceptionHandler注解来拦截一个异常,并进行处理

创建一个报错的接口

@RestController
@RequestMapping("/v1/test")
@Slf4j
public class TestController {

    @GetMapping("/err")
    public ResponseEntity<String> errTest() {
        int a = 1 / 0;
        return new ResponseEntity<>("OK", HttpStatus.OK);
    }
}

该接口报的错为 java.lang.ArithmeticException: / by zero,一个算术异常

图片.png

捕获处理该异常

/**
 * @author Jenson
 */
@Slf4j
@ControllerAdvice
public class BaseControllerAdvice {


    @ExceptionHandler(ArithmeticException.class)
    public ResponseEntity<ExceptionResponse> processArithmeticException(HttpServletRequest request, HandlerMethod method, ArithmeticException exception) {
        if (log.isWarnEnabled()) {
            log.warn("ArithmeticException :", exception);
        }
        ExceptionResponse er = new ExceptionResponse();
        er.setFailed(Boolean.TRUE);
        er.setCode("err.arithmetic");
        er.setType("error");
        er.setMessage("算术异常: " + exception.getMessage());
        return new ResponseEntity<>(er, HttpStatus.OK);
    }
}

创建了一个ExceptionResponse来组装异常响应

/**
 * @author Jenson
 */
@Data
public class ExceptionResponse {

    public static final String FILED_FAILED = "failed";
    public static final String FILED_CODE = "code";
    public static final String FILED_MESSAGE = "message";
    public static final String FILED_TYPE = "type";

    private Boolean failed;
    private String code;
    private String message;
    private String type;
}

结果,返回自定义异常

图片.png

如果有两个ControllerAdvice

测试

有两个ControllerAdvice,是两个都生效还是只生效一个?先后顺序是怎样的?

增加一个自定义异常类

/**
 * @author Jenson
 */
public class CommonException extends RuntimeException {

    private final String code;

    public CommonException(String code) {
        super(code);
        this.code = code;
    }

    public String getCode() {
        return code;
    }
}

调整BaseControllerAdvice,增加一个CuxControllerAdvice,在两个中都处理了CommonException.class,如下

/**
 * @author Jenson
 */
@Slf4j
@ControllerAdvice
public class BaseControllerAdvice {


    @ExceptionHandler(ArithmeticException.class)
    public ResponseEntity<ExceptionResponse> processArithmeticException(HttpServletRequest request, HandlerMethod method, ArithmeticException exception) {
        if (log.isWarnEnabled()) {
            log.warn("ArithmeticException :", exception);
        }
        ExceptionResponse er = new ExceptionResponse();
        er.setFailed(Boolean.TRUE);
        er.setCode("err.arithmetic");
        er.setType("error");
        er.setMessage("算术异常: " + exception.getMessage());
        return new ResponseEntity<>(er, HttpStatus.OK);
    }

    @ExceptionHandler(CommonException.class)
    public ResponseEntity<ExceptionResponse> processCommonException(HttpServletRequest request, HandlerMethod method, CommonException exception) {
        log.warn("CommonException22222 :", exception);
        ExceptionResponse er = new ExceptionResponse();
        er.setFailed(Boolean.TRUE);
        er.setCode(exception.getCode());
        er.setType("error");
        er.setMessage("通用异常222222: " + exception.getMessage());
        return new ResponseEntity<>(er, HttpStatus.OK);
    }
}
/**
 * @author Jenson
 */
@Slf4j
@ControllerAdvice
public class CuxControllerAdvice {

    @ExceptionHandler(CommonException.class)
    public ResponseEntity<ExceptionResponse> processCommonException(HttpServletRequest request, HandlerMethod method, CommonException exception) {
        log.warn("CommonException111111 :", exception);
        ExceptionResponse er = new ExceptionResponse();
        er.setFailed(Boolean.TRUE);
        er.setCode(exception.getCode());
        er.setType("error");
        er.setMessage("通用异常11111: " + exception.getMessage());
        return new ResponseEntity<>(er, HttpStatus.OK);
    }
}

调整controller接口调用,报出自定义异常

/**
 * @author Jenson
 */
@RestController
@RequestMapping("/v1/test")
@Slf4j
public class TestController {

    @GetMapping("/err")
    public ResponseEntity<String> errTest(@RequestParam String code) {
        if("zero".equals(code)){
            int a = 1 / 0;
        }
        else if("common".equals(code)){
            throw new CommonException("err.initiative");
        }
        return new ResponseEntity<>("OK", HttpStatus.OK);
    }

}
  • 调用接口测试
图片.png

日志

2022-02-21 16:02:43.926  WARN 21704 --- [nio-8010-exec-5] com.jenson.infra.BaseControllerAdvice    : CommonException22222 :

com.jenson.exception.CommonException: err.initiative
    at com.jenson.controller.v1.TestController.errTest(TestController.java:31) ~[classes/:na]

...

从日志中可以看出,只进入了BaseControllerAdvice中的异常捕获

通过@Order来更改加载顺序

修改BaseControllerAdviceCuxControllerAdvice如下,增加@Order注解让BaseControllerAdvice先加载

/**
 * @author Jenson
 */
@Slf4j
@ControllerAdvice
@Order(2)
public class BaseControllerAdvice {

...
/**
 * @author Jenson
 */
@Slf4j
@ControllerAdvice
@Order(1)
public class CuxControllerAdvice {

...
  • 调用接口结果如下:

自定义异常:

图片.png

日志

2022-02-21 16:10:03.109  WARN 22061 --- [nio-8010-exec-2] com.jenson.infra.CuxControllerAdvice     : CommonException111111 :

com.jenson.exception.CommonException: err.initiative
    at com.jenson.controller.v1.TestController.errTest(TestController.java:31) ~[classes/:na]
...

算术异常:
算术异常的捕获只写在了BaseControllerAdvice中,依然被执行到了

图片.png

日志

2022-02-21 16:11:28.065  WARN 22061 --- [nio-8010-exec-5] com.jenson.infra.BaseControllerAdvice    : ArithmeticException :

java.lang.ArithmeticException: / by zero
    at com.jenson.controller.v1.TestController.errTest(TestController.java:28) ~[classes/:na]
...
  • 结论
    当有多个@ControllerAdvice时,如果某个异常的捕获在多个类中,只会进入先加载的类的异常捕获方法中,如果先加载的类中没有某个异常的捕获但后加载的有,则会进入后加载的异常捕获方法中。

@ModelAttribute

/**
 * @author Jenson
 */
@ControllerAdvice
public class BaseModelAttribute {

    @ModelAttribute("randomUUID")
    public String generateRandomUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }
}
/**
 * @author Jenson
 */
@RestController
@RequestMapping("/v1/test")
@Slf4j
public class TestController {

    @GetMapping("/hello")
    public ResponseEntity<String> hello(@ModelAttribute("randomUUID") String randomStr) {
        return ResponseEntity.ok("hello fool : "+randomStr);
    }

...

测试结果:


图片.png

注:调用参数中未使用@ModelAttribute注解的Controller前也会进入标记了@ModelAttribute的函数中。

@InitBinder

创建一个接口参数对象

/**
 * @author Jenson
 */
@Data
public class ThingDTO {

    private List<String> things;
}

创建一个接口来测试下

/**
 * @author Jenson
 */
@RestController
@RequestMapping("/v1/test")
@Slf4j
public class TestController {
    @GetMapping("/split2")
    public ResponseEntity<ThingDTO> splitTest2(@RequestParam ThingDTO thing) {
        return new ResponseEntity<>(thing, HttpStatus.OK);
    }

...

参考org.springframework.beans.propertyeditors.CustomDateEditor写一个自己的Editor
将接口传入的文本转为ThingDTO类型

/**
 * @author Jenson
 */
public class CustomThingsEditor extends PropertyEditorSupport {

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        ThingDTO thingDTO = new ThingDTO();
        List<String> list = new ArrayList<>(Arrays.asList(text.split("-")));
        thingDTO.setThings(list);
        this.setValue(thingDTO);
    }
}

initBinder

/**
 * @author Jenson
 */
@Slf4j
@ControllerAdvice
public class BaseInitBinder {

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(ThingDTO.class, new CustomThingsEditor());
    }
}

测试接口

图片.png

代码地址:https://gitee.com/jenson343/hotchpotch/tree/master/controller-advice-test

参考:
作者:YLiuY
链接:https://www.jianshu.com/p/acf7fdf20326
来源:简书

相关文章

网友评论

      本文标题:@ControllerAdvice的使用

      本文链接:https://www.haomeiwen.com/subject/ercilrtx.html