方式一
这种方式线上高并发看链路追踪发现new RequestWrapper耗时3秒 后来改为第二种方式
装饰requset
@Component @WebFilter(filterName = "wrapperFilter", urlPatterns = {"/**"}) public class WrapperFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ServletRequest requestWrapper = new RequestWrapper((HttpServletRequest) request); chain.doFilter(requestWrapper, response); } }
package cn.wine.ms.promotion.configuartion; import lombok.extern.slf4j.Slf4j; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; import java.util.Objects; /** * @author lau * 解决因为ServletInputStream 读取body流 读取后指针不能还原其他地方不能读取多次 */ @Slf4j public class RequestWrapper extends HttpServletRequestWrapper { private byte[] body; private Reader reader; public RequestWrapper(HttpServletRequest request) throws IOException { super(request); try { StringBuffer stringBuffer = new StringBuffer(); request.getReader().lines().forEach(stringBuffer::append); body = stringBuffer.toString().getBytes(); } catch (IllegalStateException e) { body = new byte[]{}; log.error("{}接口body为空或丢失",request.getRequestURI()); //部分接口,文件上传,body为空,会报IllegalStateException } } public RequestWrapper(HttpServletRequest request, Reader reader) { super(request); this.reader = reader; StringBuffer stringBuffer = new StringBuffer(); new BufferedReader(reader).lines().forEach(stringBuffer::append); body = stringBuffer.toString().getBytes(); } @Override public BufferedReader getReader() throws IOException { if (Objects.isNull(reader)){ return new BufferedReader(new InputStreamReader(getInputStream())); }else { return new BufferedReader(reader); } } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener listener) { } @Override public int read() throws IOException { return bais.read(); } }; } }
方式二
package cn.wine.ms.promotion.configuartion; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.util.CollectionUtils; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; /** * @Project 商品uaa * @PackageName cn.wine.ms.promotion.configuartion * @ClassName RequestMappingHandlerAdapterCusotmer * @Author qiang.li * @Date 2021/2/23 11:34 上午 * @Description 用于对springRequestMappingHandlerAdapter做一些定制化配置 */ @Configuration public class RequestMappingHandlerAdapterCustomizeConfig implements InitializingBean { @Autowired private RequestMappingHandlerAdapter requestMappingHandlerAdapter; @Override public void afterPropertiesSet() throws Exception { //兼容判断 防止使用非注解方式 如xml配置 if(requestMappingHandlerAdapter==null){ return; } //定制化配置@RequestBody 入参解析器 replaceRequestResponseBodyMethodProcessor(); } /** * 使用RequestResponseBodyMethodProcessorWrapper 替换默认的使用RequestResponseBodyMethodProcessor * 解决因为Request.getInputStream()只能读取一次 不能多次读取的问题 * 1.在原有的基础上增加将解析结果存入Request attributes 供后续使用 * HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); * Object data = request.getAttribute(RequestResponseBodyMethodProcessorWrapper.resolveArgumentKey); */ public void replaceRequestResponseBodyMethodProcessor(){ //获得解析器处理集合 List<HandlerMethodArgumentResolver> handlerMethodArgumentResolver= requestMappingHandlerAdapter.getArgumentResolvers(); if(!CollectionUtils.isEmpty(handlerMethodArgumentResolver)){ Optional<HandlerMethodArgumentResolver> resolverOptional=handlerMethodArgumentResolver.stream().filter(c->c instanceof RequestResponseBodyMethodProcessor).findAny(); if(resolverOptional.isPresent()){ //因为框架是不可变的 改为可变类型 List<HandlerMethodArgumentResolver> newHandlerMethodArgumentResolvers=new ArrayList<>(handlerMethodArgumentResolver); //替换为RequestResponseBodyMethodProcessorWrapper Collections.replaceAll(newHandlerMethodArgumentResolvers, resolverOptional.get(), new RequestResponseBodyMethodProcessorWrapper(resolverOptional.get())); //替换list requestMappingHandlerAdapter.setArgumentResolvers(newHandlerMethodArgumentResolvers); } } } }
package cn.wine.ms.promotion.configuartion; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; /** * @Project 商品uaa * @PackageName cn.wine.ms.promotion.configuartion * @ClassName RequestResponseBodyMethodProcessorWrapper * @Author qiang.li * @Date 2021/2/23 1:20 下午 * @Description RequestResponseBodyMethodProcessor装饰器,在原有功能上做将结果保存到request attribute的增加 */ public class RequestResponseBodyMethodProcessorWrapper implements HandlerMethodArgumentResolver { public final static String resolveArgumentKey="resolveArgumentObject"; private HandlerMethodArgumentResolver handlerMethodReturnValueHandler; public RequestResponseBodyMethodProcessorWrapper(HandlerMethodArgumentResolver handlerMethodReturnValueHandler){ this.handlerMethodReturnValueHandler=handlerMethodReturnValueHandler; } @Override public boolean supportsParameter(MethodParameter methodParameter) { //委托 return handlerMethodReturnValueHandler.supportsParameter(methodParameter); } @Override public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception { //委托 Object argumentObject= handlerMethodReturnValueHandler.resolveArgument(methodParameter,modelAndViewContainer,nativeWebRequest,webDataBinderFactory); //功能增强 将解析结果存入request attribute供后续使用 if(argumentObject!=null){ HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); request.setAttribute(resolveArgumentKey,argumentObject); } return argumentObject; } }
使用 我的场景是异常拦截器打印入参
/** * 获取body * @param httpServletRequest * @return */ public static String readBody(HttpServletRequest httpServletRequest) { try { //request body不能重复读取 处理方案请看cn.wine.ms.promotion.configuartion.RequestMappingHandlerAdapterCustomizeConfig HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); Object data = request.getAttribute(RequestResponseBodyMethodProcessorWrapper.resolveArgumentKey); return JSON.toJSONString(data); }catch (Exception e){ log.error("获取body数据异常:{}",e); return "getError"; } }