springboot 详解RestControllerAdvice(ControllerAdvice)(转)

springboot 详解RestControllerAdvice(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 {};
}

作为特化@Component,允许通过类路径扫描自动检测实现类。

它通常用于定义@ExceptionHandler, @InitBinder 和 @ModelAttribute 适用于所有@RequestMapping方法的方法。

annotations(),basePackageClasses(), basePackages()或它的别名value() 可以被指定,以限定控制器,以协助的特定子集。当应用多个选择器时,应用OR逻辑 - 意味着所选的控制器应匹配至少一个选择器。

默认行为(即,如果没有任何选择器使用),带@ControllerAdvice注释的类将协助所有已知的控制器。

背景
当我们定义了一个自定义返回参数格式时,希望得到统一的返回,如果在运行时发现了异常,也希望将异常统一返回。如
期望返回格式:

{
"msg": "success",
"code": 500,
"success": false,
"message": "id不能为空!"
}

抛出异常格式:

{
"timestamp": "2019-04-01T07:17:38.619+0000",
"status": 500,
"error": "Internal Server Error",
"message": "id不能为空!",
"path": "/api/myInfo"
}

如何让我们的异常得到期望的返回格式,这里就需要用到了@ControllerAdvice或者RestControllerAdvice(如果全部异常处理返回json,那么可以使用 @RestControllerAdvice 代替 @ControllerAdvice ,这样在方法上就可以不需要添加 @ResponseBody。)。下面看一个demo。

准备工作
1.创建一个UnionExceptionHandler类,定义全局异常捕捉处理。

package com.honghh.bootfirst.exception;

import com.honghh.bootfirst.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
* ClassName: UnionExceptionHandler
* Description:
*
* @author honghh
* @date 2019/04/01 10:03
*/
@Slf4j
@RestControllerAdvice
public class UnionExceptionHandler {

/**
* 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
*
* @param binder
*/
@InitBinder
public void initBinder(WebDataBinder binder) {
log.info("binder.getFieldDefaultPrefix {}",binder.getFieldDefaultPrefix());
log.info("binder.getFieldMarkerPrefix {}",binder.getFieldMarkerPrefix());
}
/**
* 把值绑定到Model中,使全局@RequestMapping可以获取到该值
* @param model
*/
@ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("author", "harry");
}
/**
* Description : 全局异常捕捉处理
* Group :
*
* @author honghh
* @date 2019/4/1 0001 10:34
* @param ex
* @return
*/
@ExceptionHandler(RRException.class)
public R apiExceptionHandler(RRException ex) {
log.error("ApiException 异常抛出:{}", ex);
return R.fail(ex);
}

/**
* Description : 针对某个异常捕捉处理
* Group :
*
* @author honghh
* @date 2019/4/1 0001 10:34
* @param ex
* @return
*/
@ExceptionHandler(SSException.class)
public R apiExceptionHandler(SSException ex) {
log.error("ApiException 异常抛出:{}", ex);
return R.fail(ex);
}
}


启动应用后,被 @ExceptionHandler、@InitBinder、@ModelAttribute 注解的方法,都会作用在 @RequestMapping 注解的方法上。
@ModelAttribute:在Model上设置的值,对于所有被 @RequestMapping 注解的方法中,都可以通过 ModelMap 获取
2.创建一个RRException 自定义异常。
package com.honghh.bootfirst.exception;

/**
* 自定义异常
*
* @author harry
* @date 2018-07-20 16:30
*/
public class RRException extends RuntimeException {
private static final long serialVersionUID = 1L;

private String msg;
private int code = 500;

public RRException(String msg) {
super(msg);
this.msg = msg;
}

public RRException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}

public RRException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}

public RRException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}

public int getCode() {
return code;
}

public void setCode(int code) {
this.code = code;
}


}

3.写一个RequestMapping方法,抛出异常进行测试。[spring boot 默认情况下会映射到 /error 进行异常处理,但是提示并不十分友好,下面自定义异常处理,提供友好展示。

@GetMapping("myInfo")
public R myInfo(@RequestParam Integer id) {
if (id == null) {
throw new RRException("id不能为空!");
}
MyInfo myInfo = myInfoService.getById(id);
return R.ok().put("myInfo", myInfo);
}

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

{
"msg": "success",
"code": 500,
"success": false,
"message": "id不能为空!"
}

代码获取
https://gitee.com/honghh/boot-demo.git

参考文献
https://docs.spring.io/spring-framework/docs/5.0.0.M1/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html
————————————————
版权声明:本文为CSDN博主「AH_HH」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35098526/article/details/88949425

原文地址:https://www.cnblogs.com/muxi0407/p/11950475.html