补习系列-springboot 实现拦截的五种姿势 (3)

上述代码实现中,针对前面提到的 MsgBody对象类型进行了拦截处理。
在beforeBodyRead 中,返回一个BodyInputMessage对象,而这个对象便负责源数据流解析转换

public static class BodyInputMessage implements HttpInputMessage { private HttpHeaders headers; private InputStream body; public BodyInputMessage(HttpInputMessage inputMessage) throws IOException { this.headers = inputMessage.getHeaders(); // 读取原字符串 String content = IOUtils.toString(inputMessage.getBody(), "UTF-8"); MsgBody msg = new MsgBody(); msg.setContent(content); this.body = new ByteArrayInputStream(JsonUtil.toJson(msg).getBytes()); } @Override public InputStream getBody() throws IOException { return body; } @Override public HttpHeaders getHeaders() { return headers; } }

代码说明
完成数据流的转换,包括以下步骤:

获取请求内容字符串;

构建 MsgBody 对象,将内容字符串作为其 content 字段;

将 MsgBody 对象 Json 序列化,再次转成字节流供后续环节使用。

ResponseBodyAdvice 用法

ResponseBodyAdvice 的用途在于对返回内容做拦截处理,如下面的示例:

@ControllerAdvice(assignableTypes = InterceptController.class) public static class CustomResponseAdvice implements ResponseBodyAdvice<String> { private static final Logger logger = LoggerFactory.getLogger(CustomRequestAdvice.class); @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { // 返回true,表示启动拦截 return true; } @Override public String beforeBodyWrite(String body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { logger.info("CustomResponseAdvice beforeBodyWrite"); // 添加前缀 String raw = String.valueOf(body); return "PREFIX:" + raw; } }

看,还是容易理解的,我们在返回的字符串中添加了一个前缀!

推荐指数
2 颗星,这是两个非常冷门的接口,目前的使用场景也相对有限;
一般在需要对输入输出流进行特殊处理(比如加解密)的场景下使用。

姿势五、@Aspect 注解

这是目前最灵活的做法,直接利用注解可实现任意对象、方法的拦截。
在某个Bean的类上面** @Aspect** 注解便可以将一个Bean 声明为具有AOP能力的对象。

@Aspect @Component public class InterceptControllerAspect { private static final Logger logger = LoggerFactory.getLogger(InterceptControllerAspect.class); @Pointcut("target(org.zales.dmo.boot.controllers.InterceptController)") public void interceptController() { } @Around("interceptController()") public Object handle(ProceedingJoinPoint joinPoint) throws Throwable { logger.info("aspect before."); try { return joinPoint.proceed(); } finally { logger.info("aspect after."); } } }

简单说明

@Pointcut 用于定义切面点,而使用target关键字可以定位到具体的类。
@Around 定义了一个切面处理方法,通过注入ProceedingJoinPoint对象达到控制的目的。

一些常用的切面注解:

注解 说明
@Before   方法执行之前  
@After   方法执行之后  
@Around   方法执行前后  
@AfterThrowing   抛出异常后  
@AfterReturing   正常返回后  

深入一点
aop的能力来自于spring-boot-starter-aop,进一步依赖于aspectjweaver组件。
有兴趣可以进一步了解。

推荐指数
5颗星,aspectj 与 SpringBoot 可以无缝集成,这是一个经典的AOP框架,
可以实现任何你想要的功能,笔者之前曾在多个项目中使用,效果是十分不错的。
注解的支持及自动包扫描大大简化了开发,然而,你仍然需要先对 Pointcut 的定义有充分的了解。

思考

到这里,读者可能想知道,这些实现拦截器的接口之间有什么关系呢?
答案是,没有什么关系! 每一种接口都会在不同的时机被调用,我们基于上面的代码示例做了日志输出:

- Filter customFilter handle before - Filter annotateFilter handle before - CustomerHandlerInterceptor preHandle, body - CustomRequestAdvice beforeBodyRead - CustomRequestAdvice afterBodyRead - aspect before. - aspect after. - CustomResponseAdvice beforeBodyWrite - CustomerHandlerInterceptor postHandle, body - CustomerHandlerInterceptor afterCompletion, body - Filter annotateFilter handle after - Filter customFilter handle after

可以看到,各种拦截器接口的执行顺序如下图:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpgyfj.html