What is `@ControllerAdvice` does?

@ControllerAdvice 是Spring 框架中的注解,多用在Spring MVC应用程序中。

用于定义全局的异常处理数据绑定等跨控制器(Controller)的增强逻辑。

@ControllerAdvice标注在类上,表明此类包含的方法 将应用于所有的@Controller@RestController及其子注解所标注的类。

在这里插入图片描述

使用样例

使用场景1:全局异常处理:

# 示例1
import org.apache.ibatis.javassist.NotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseEntity<String> handleException(Exception ex) {
        System.err.println("***GlobalExceptionHandler.handleException:" + ex.getMessage() + "***");
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occurred");
    }

    @ExceptionHandler(value = {NotFoundException.class})
    public ResponseEntity<String> handleNotFoundException(NotFoundException ex) {
        System.err.println("***GlobalExceptionHandler.handleNotFoundException***");
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Resource not found");
    }

    @ExceptionHandler(value = {NullPointerException.class, IllegalArgumentException.class})
    public ResponseEntity<String> handleException(RuntimeException ex) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage());
    }
}

在这里插入图片描述
使用场景2:数据绑定

通过实现接口ResponseBodyAdvice来修改返回值,并直接作为 ResponseBody类型处理器 的返回值。

# 示例2
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.Map;

@ControllerAdvice
public class HelloResponseBodyAdvice implements ResponseBodyAdvice<Map<String, Object>> {


    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Map<String, Object> beforeBodyWrite(Map<String, Object> body, MethodParameter returnType, MediaType selectedContentType,
                                               Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                               ServerHttpRequest request, ServerHttpResponse response) {
		// 修改返回值
        System.out.println("origin map: " + body);
        body.put("msg", "hello");
        
        return body;
    }
}
# 示例3
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import static java.util.Objects.nonNull;

@ControllerAdvice
public class HttpResponseBodyAdvice implements ResponseBodyAdvice {


    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class clazz,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        HttpHeaders headers = response.getHeaders();

        // 分页信息添加到ServerHttpResponse
        HttpHeaders headersContext = ResponseUtils.getHeaders();
        if (nonNull(headersContext) && !headersContext.isEmpty()) {
            headers.addAll(headersContext);
        }

        // 状态码添加到ServerHttpResponse
        if (nonNull(ResponseUtils.getResponseCode())) {
            response.setStatusCode(ResponseUtils.getResponseCode());
        }
        return body;
    }
}

在这里插入图片描述

验证

CASE1:只有1个 HelloResponseBodyAdvice

@RestController
@RequestMapping("/test")
public class ResponseBodyAdviceController {

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public Map<String, Object> hello() {
        Map<String, Object> map = new HashMap<>();
        map.put("a", 1);
        map.put("b", 2);
        return map;
    }
}

在这里插入图片描述

结论: 返回值被 成功修改

在这里插入图片描述

CASE2:有2个 ResponseBodyAdvice

结论:均执行,按照类名字母降序的顺序执行,比如
HelloResponseBodyAdvicePracResponseBodyAdvice 先执行,
APracResponseBodyAdviceHelloResponseBodyAdvice 先执行。

在这里插入图片描述

CASE3:发生异常时

@RestController
@RequestMapping("/test")
public class ResponseBodyAdviceController {

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public Map<String, Object> hello() {
        int a = 0;
        int b = 5;
        int i = b / a;

        return null;
    }
}

在这里插入图片描述
结论:执行顺序为:GlobalExceptionHandlerHelloResponseBodyAdviceHttpResponseBodyAdvice

结论:异常也被修改了值,所以这个修改值的操作,慎用!!!

结论:一般微服务只有 2 个 @ControllerAdvice ,一个用作修改 HttpHeaderHttpStatus (示例3),一个用作处理异常(示例1)。
在这里插入图片描述

CASE4:HandlerInterceptor VS ResponseBodyAdvice

结论:执行顺序为 ResponseBodyAdviceHandlerInterceptor

***ResponsePostInterceptor.preHandle***

***ResponsePostAdvice.supports***
***ResponsePostAdvice.beforeBodyWrite***

***ResponsePostInterceptor.postHandle***
***ResponsePostInterceptor.afterCompletion***

在这里插入图片描述

CASE5:@RestController VS @Controller

结论:@RestController 会成功返回结果, @Controller不会返回结果

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值