Spring中的@ControllerAdvice和ResponseBodyAdvice详解
作者:开源必胜
@ControllerAdvice
@ControllerAdvice作用于@Controller修饰的类里面的所有方法。
它主要有两种作用:
对Controller的入参进行预处理
对Controller中的异常进行全局统一处理
ResponseBodyAdvice
ResponseBodyAdvice作用于@ResponseBody注解修饰的方法,它可以对这些方法的返回值进行修改。
它是一个接口,这个接口有两个函数:
/** * @param returnType 可以得到方法和参数的相关信息(注解呀,类型呀) * @param converterType HttpMessageConverter的实现类 * @return 是否对某个接口(被@ResponseBody修饰)的返回值进行修改。如果为true就会调用 * beforeBodyWrite方法 */ boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType); /** * @param body 被@ResponseBody修饰方法的返回值(区别于returnType) * @param returnType 可以得到方法和参数的相关信息(注解呀,类型呀) * @param selectedContentType 选中的媒体类型,即以什么格式写出数据(json、xml、text...) * @param selectedConverterType HttpMessageConverter的实现类的具体类型 * @param request 请求对象 * @param response 响应对象 * @return */ T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response);
类上注释如下:
Allows customizing the response after the execution of an @ResponseBody or a ResponseEntity controller method but before the body is written with an HttpMessageConverter.
根据类上的注释可以得知如果满足以下两个条件,则返回值会被ResponseBodyAdvice的beforeBodyWrite方法修改,修改之后的值被HttpMessageConverter写出,返回给前端浏览器。
Controller里的方法被@ResponseBody修饰或者Controller里的方法返回ResponseEntityResponseBodyAdvice的supports方法返回true 注意两者的执行顺序
如果针对异常情况和正常情况我们都做了统一处理,要留意不要重复处理。
示例
新建一个Advice类,它被@ControllerAdvice修饰,又实现了ResponseBodyAdvice接口。我们希望在这个类里面既能实现全局异常处理,又能对后端返回的数据统一封装。
返回结果封装类
在Controller里可以返回任意类型,他们都会被封装到Result的data属性中。
public class Result { private int code; private String data; // getter and seter }
全局处理异常,全局封装返回值
@ControllerAdvice public class Advice implements ResponseBodyAdvice { //因为这里也加了@ResponseBody注解,所以它的返回值也会被ResponseBodyAdvice 处理一遍 @ExceptionHandler(Exception.class) @ResponseBody public Result he(Exception e) { Result res = new Result(); res.setCode(500); res.setData(e.getMessage()); return res; } @Override public boolean supports(MethodParameter returnType, Class converterType) { //返回任意类型都要封装 return true; } ObjectMapper objectMapper = new ObjectMapper(); @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { Result res = new Result(); res.setCode(200); //如果返回值是String,直接放到Result里 if (body instanceof String) { res.setData((String) body); return res; } //如果返回值是标准返回格式,就不需要再次封装了 //如果不加这个判断,异常的结果会被封装两次 else if (body instanceof Result) { return body; } String dataStr = null; try { dataStr = objectMapper.writeValueAsString(body); res.setData(dataStr); } catch (JsonProcessingException e) { e.printStackTrace(); } return res; } }
注意:Advice类加了@ControllerAdvice注解,并实现了ResponseBodyAdvice
接口
简单起见,直接在启动类里面写RESTful接口。一个抛出异常,一个返回一个实体类。
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @Controller public class Demo1Application { public static void main(String[] args) { SpringApplication.run(Demo1Application.class, args); } @GetMapping("ex") @ResponseBody public String ex() throws Exception { throw new Exception("异常信息"); } @GetMapping("stu") @ResponseBody public Stu s() { final Stu stu = new Stu(); stu.setName("zcx"); stu.setAge(22); return stu; } } class Stu { private String name; private int age; //getter and setter }
原理
因为被@ResponseBody注解注释的返回值都会被RequestResponseBodyMethodProcessor处理,它里面的 HttpMessageConverter在写出数据时,会先拿到一个RequestResponseBodyAdviceChain,先用RequestResponseBodyAdviceChain里面的responseBodyAdvice对Controller返回值进行处理,再写到浏览器。
拓展
与ResponseBodyAdvice类似的有RequestBodyAdvice,它可以对@RequestBody注释的参数进行额外处理,在使用时注意不要在beforeBodyRead里面把inputMessage的body读出来,否则会有I/O异常。可以使用afterBodyRead对参数进行修改。
到此这篇关于Spring中的@ControllerAdvice和ResponseBodyAdvice详解的文章就介绍到这了,更多相关@ControllerAdvice和ResponseBodyAdvice内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!