当前位置: 首页 > news >正文

Spring Boot 3.x Web MVC实战:实现流缓存的request

上一节《Spring Boot 3.x Filter实战:记录请求日志》实践最后遇到了request对象的流不可重复读的问题,本小节我们将通过流数据缓存以及流的装饰器模式来解决这个问题。如果觉得对你有帮助,记得点赞收藏,关注小卷,后续更精彩!

在这里插入图片描述

文章目录

    • 装饰流操作对象
    • 缓存流数据的请求包装器
    • RequestLogFilter应用

装饰流操作对象

我们知道,在Java语言的流模块设计中大量采用了装饰器模式,来扩展流的特性。这里我们同样会对接收字节数组流数据的ByteArrayInputStream对象进行包装,将其作为一个从ServletInputStream扩展的CachedServletInputStream类型对象的成员变量,进行缓存。看下代码实现:

package com.juan.demo.common.web.support.servlet;import ...@Slf4j
public class CachedServletInputStream extends ServletInputStream {private final InputStream cachedInputStream;public CachedServletInputStream(byte[] cachedByteArray) {this.cachedInputStream = new ByteArrayInputStream(cachedByteArray);}@Overridepublic boolean isFinished() {try {return cachedInputStream.available() == 0;} catch (IOException e) {log.error(e.getMessage());}return false;}@Overridepublic boolean isReady() {return true;}@Overridepublic void setReadListener(ReadListener listener) {throw new UnsupportedOperationException();}@Overridepublic int read() throws IOException {return cachedInputStream.read();}
}

该流操作对象扩展自ServletInputStream,可以提供给外部作为请求的流对象使用,在调用ServletInputStream相关流操作方法时,实际调用的是该包装类重写的相关方法。这里主要关注isFinished()read()方法,它们的实现其实就是调用被缓存和包装的流对象的相关方法。

缓存流数据的请求包装器

对于http servlet请求,Java EE提供了HttpServletRequestWrapper包装器类对原始的请求对象进行包装,这对应用层的开发人员来说是透明的,咱们仅关注面向HttpServletRequest接口的编程,实际运行时的调用会对包装器对象调用内部被包装对象来完成相应的功能。

这里我们仅仅要做的就是扩展HttpServletRequestWrapper,并缓存最原始的字节输出流数据。而在调用目标请求对象相关方法(这里是getInputStream()getReader()方法)时,进行重写,实现每次都返回一个携带原始流数据的ServletInputStreamBufferedReader对象即可。

而要返回的ServletInputStream类型,前面我们已经自定义了一个接收原始字节数据完成构造的CachedServletInputStream类型。

看完成的代码实现:

package com.juan.demo.common.web.support.servlet;import ...public class CachedHttpServletRequestWrapper extends HttpServletRequestWrapper {private byte[] cachedByteArray;public CachedHttpServletRequestWrapper(HttpServletRequest request) {super(request);}@Overridepublic ServletInputStream getInputStream() throws IOException {copyInputStreamIfNecessary();return new CachedServletInputStream(this.cachedByteArray);}@Overridepublic BufferedReader getReader() throws IOException {copyInputStreamIfNecessary();return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(this.cachedByteArray)));}private void copyInputStreamIfNecessary() throws IOException {if (this.cachedByteArray == null) {this.cachedByteArray = StreamUtils.copyToByteArray(getRequest().getInputStream());}}
}

延迟拷贝流数据

注意我们这里的设计,并不是在构造CachedHttpServletRequestWrapper时就进行流数据的拷贝,而是推迟到需要使用时才调用copyInputStreamIfNecessary()去做拷贝。这种方式更加的安全,也考虑到一般一个请求的处理在一个请求线程上,不会有线程安全问题。

RequestLogFilter应用

最后,我们在RequestLogFilterdoFilterInternal方法中应用前面对请求包装器的实现:

@Override
protected void doFilterInternal(...) throws ... {...CachedHttpServletRequestWrapper requestWrapper = new CachedHttpServletRequestWrapper(request);logParams(requestWrapper);logRequestBody(requestWrapper);filterChain.doFilter(requestWrapper, response);
}

注意,这里记录日志以及放行请求的后续处理操作的都是包装过的requestWrapper对象。

最后,测试一下之前的添加购物车API,ok!

在这里插入图片描述

在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 速盾:高防ip和cdn哪个好?
  • 按钮(Buttons)-Qt-思维导图-学习笔记
  • Flink开发(一):概述与基础
  • SpringCloud 微服务nacos和eureka
  • 深入探针:PHP与DTrace的动态追踪艺术
  • 【Python快速入门和实践004】Python控制流
  • 机器学习中的距离概念
  • Java8_关于list集合的操作Stream
  • 安科瑞Acrel-2000ES储能能量管理系统在新型电力系统下分布式储能的研究
  • Condition使用
  • 安全测试中参数校验测试
  • 2024最全最新VMWare以及Linux配置(含yum失效解决方案)
  • 算法解析——双指针算法(3)数据匹配
  • 开启IDEA打开新项目时窗口提示
  • 【C#】 接口 继承
  • 【翻译】babel对TC39装饰器草案的实现
  • 【干货分享】SpringCloud微服务架构分布式组件如何共享session对象
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • C++11: atomic 头文件
  • es的写入过程
  • git 常用命令
  • JAVA SE 6 GC调优笔记
  • swift基础之_对象 实例方法 对象方法。
  • Vue 2.3、2.4 知识点小结
  • 对超线程几个不同角度的解释
  • 分布式熔断降级平台aegis
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 欢迎参加第二届中国游戏开发者大会
  • 技术发展面试
  • 快速构建spring-cloud+sleuth+rabbit+ zipkin+es+kibana+grafana日志跟踪平台
  • 离散点最小(凸)包围边界查找
  • 三栏布局总结
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • ​Distil-Whisper:比Whisper快6倍,体积小50%的语音识别模型
  • ​比特币大跌的 2 个原因
  • ​用户画像从0到100的构建思路
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • #FPGA(基础知识)
  • #vue3 实现前端下载excel文件模板功能
  • $.ajax,axios,fetch三种ajax请求的区别
  • (007)XHTML文档之标题——h1~h6
  • (11)工业界推荐系统-小红书推荐场景及内部实践【粗排三塔模型】
  • (Oracle)SQL优化技巧(一):分页查询
  • (PADS学习)第二章:原理图绘制 第一部分
  • (超详细)语音信号处理之特征提取
  • (二)延时任务篇——通过redis的key监听,实现延迟任务实战
  • (附源码)ssm教材管理系统 毕业设计 011229
  • (每日持续更新)jdk api之StringBufferInputStream基础、应用、实战
  • (三)docker:Dockerfile构建容器运行jar包
  • (正则)提取页面里的img标签
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • .net 重复调用webservice_Java RMI 远程调用详解,优劣势说明
  • .NET性能优化(文摘)
  • .vollhavhelp-V-XXXXXXXX勒索病毒的最新威胁:如何恢复您的数据?
  • @reference注解_Dubbo配置参考手册之dubbo:reference