Spring流重复消费

Response流复制

背景

前段时间有个需求, 需要是要业务在web层增加操作日志。 实现基于 aop 实现的。但是有几个接口返回 void, 实际的数据流通过 response.getWriter().write 这样返回的。对于业务结果是分成功和失败的, 但是aop中拿不到返回结果, 所以才有了以下的实现。

实现

分析: 由于spring中的流只能被消费一次, 所以本质上还是通过流复制来解决。实际解决如下

1
2
3
4
5
6
7
8
9
public class LogRecordFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
filterChain.doFilter(request, responseWrapper);
responseWrapper.copyBodyToResponse();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Around("@annotation(xxx.web.logrecord.annotation.LogRecord)")
public Object logPointCut(ProceedingJoinPoint joinPoint) throws Throwable {
Object ret = null;

MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Object[] args = joinPoint.getArgs();

// 省略...

try {
// 执行主逻辑
ret = joinPoint.proceed();
} catch (Throwable e) {
throw e;
}

// 如果执行失败, 不记录操作日志
if(Objects.nonNull(ret) && ret instanceof RespResult) {
RespResult respResult = (RespResult)ret;
if (respResult.getType() == 0) {
return ret;
}
}

// 如果返回值未void, 数据通过response写入, 有执行失败业务码, 不记录操作日志
if (Objects.isNull(ret)) {
try {
HttpServletResponse response = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();
if (Objects.nonNull(response)) {
byte[] contentAsByteArray = ((ContentCachingResponseWrapper) response).getContentAsByteArray();
RespResult respResult = objectMapper.readValue(contentAsByteArray, RespResult.class);
if (respResult.getType() == 0) {
LogRecordContext.clear();
return ret;
}
}
} catch (Exception e) {
// 不符合预期, 程序正常执行结束, 不做干预
log.error("[xxx] void response err", e);
}
}

return ret;
}

最后

本文到此结束,感谢阅读。如果您觉得不错,请关注公众号【当我遇上你】,您的支持是我写作的最大动力。