SpringMVC DispatcherServlet源码(5) HttpMessageConverter扩展
前文通过阅读源码,深入分析了DispatcherServlet及相关组件的工作流程,本文不再阅读源码,介绍一下扩展HttpMessageConverter的方式。
HttpMessageConverter工作方式及扩展方式
前文介绍过,HttpMessageConverter是读写请求体和响应体的组件。
RequestResponseBodyMethodProcessor(用于解析请求参数、处理返回值)从内置的HttpMessageConverter查找支持当前请求体、响应体的实例,然后调用read、write来读写数据。
Spring内置的HttpMessageConverter在装配RequestResponseBodyMethodProcessor的时候创建,具体代码在WebMvcConfigurationSupport类addDefaultHttpMessageConverters方法中。
开发者如果要扩展使用自己的HttpMessageConverter实现,可以编写组件实现WebMvcConfigurer接口,在extendMessageConverters方法中注入自己的HttpMessageConverter实现类对象。
自定义HttpMessageConverter
需求描述
系统需要对响应体进行加密、对请求体解密操作。
思路:
- 编写类实现HttpMessageConverter接口,read方法中先对请求体解密,之后在做json反序列化
- write方法先做json序列化,之后再加密
- 通过WebMvcConfigurer注册
编写HttpMessageConverter实现类
public class MyMappingJackson2HttpMessageConverter implements GenericHttpMessageConverter<Object> {private static final String S_KEY = "1234567890123456";private static final String IV_PARAMETER = "abcdefghijklmnop";// 用来做json序列化和反序列化,自己编写代码也可以,此处直接使用MappingJackson2HttpMessageConverter来做private final MappingJackson2HttpMessageConverter jackson2HttpMessageConverter;// 读写字符串private final StringHttpMessageConverter stringHttpMessageConverter;public MyMappingJackson2HttpMessageConverter(MappingJackson2HttpMessageConverter jackson2HttpMessageConverter) {this.jackson2HttpMessageConverter = jackson2HttpMessageConverter;this.stringHttpMessageConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);}@Overridepublic boolean canRead(Class<?> clazz, MediaType mediaType) {return jackson2HttpMessageConverter.canRead(clazz, mediaType);}@Overridepublic boolean canWrite(Class<?> clazz, MediaType mediaType) {return jackson2HttpMessageConverter.canWrite(clazz, mediaType);}@Overridepublic List<MediaType> getSupportedMediaTypes() {return jackson2HttpMessageConverter.getSupportedMediaTypes();}@Overridepublic Object read(Class<?> clazz, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException {// 读取请求原始字节byte[] bytes = readBytes(inputMessage);// 解密byte[] decryptBytes = AesUtil.decrypt(bytes, S_KEY, IV_PARAMETER);// 封装HttpInputMessage供下面反序列化使用HttpInputMessage in = new MyHttpInputMessage(inputMessage, decryptBytes);// json反序列化return jackson2HttpMessageConverter.read(clazz, in);}@Overridepublic void write(Object o, MediaType contentType, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException {ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();// json序列化jackson2HttpMessageConverter.write(o, contentType, new MyHttpOutputMessage(outputMessage, byteArrayOutputStream));byte[] bytes = byteArrayOutputStream.toByteArray();// 加密byte[] encryptStr = AesUtil.encrypt(bytes, S_KEY, IV_PARAMETER);// 将响应写出去this.stringHttpMessageConverter.write(new String(encryptStr, StandardCharsets.UTF_8), contentType, outputMessage);}@Overridepublic boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {return jackson2HttpMessageConverter.canRead(type, contextClass, mediaType);}@Overridepublic Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException {byte[] bytes = readBytes(inputMessage);byte[] decryptBytes = AesUtil.decrypt(bytes, S_KEY, IV_PARAMETER);HttpInputMessage in = new MyHttpInputMessage(inputMessage, decryptBytes);return jackson2HttpMessageConverter.read(type, contextClass, in);}@Overridepublic boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {return jackson2HttpMessageConverter.canWrite(type, clazz, mediaType);}@Overridepublic void write(Object o, Type type, MediaType contentType, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException {ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();jackson2HttpMessageConverter.write(o, type, contentType, new MyHttpOutputMessage(outputMessage, byteArrayOutputStream));byte[] bytes = byteArrayOutputStream.toByteArray();byte[] encryptStr = AesUtil.encrypt(bytes, S_KEY, IV_PARAMETER);this.stringHttpMessageConverter.write(new String(encryptStr, StandardCharsets.UTF_8), contentType, outputMessage);}private byte[] readBytes(HttpInputMessage inputMessage) throws IOException {long contentLength = inputMessage.getHeaders().getContentLength();ByteArrayOutputStream bos =new ByteArrayOutputStream(contentLength >= 0 ? (int) contentLength : StreamUtils.BUFFER_SIZE);StreamUtils.copy(inputMessage.getBody(), bos);return bos.toByteArray();}private static class MyHttpInputMessage implements HttpInputMessage {private final HttpInputMessage originalHttpInputMessage;private final byte[] buf;public MyHttpInputMessage(HttpInputMessage originalHttpInputMessage, byte[] buf) {this.originalHttpInputMessage = originalHttpInputMessage;this.buf = buf;}@Overridepublic InputStream getBody() throws IOException {return new ByteArrayInputStream(this.buf);}@Overridepublic HttpHeaders getHeaders() {HttpHeaders headers = this.originalHttpInputMessage.getHeaders();headers.setContentLength(this.buf.length);return headers;}}private static class MyHttpOutputMessage implements HttpOutputMessage {private final HttpOutputMessage originalHttpOutputMessage;private final OutputStream outputStream;public MyHttpOutputMessage(HttpOutputMessage originalHttpOutputMessage,OutputStream outputStream) {this.originalHttpOutputMessage = originalHttpOutputMessage;this.outputStream = outputStream;}@Overridepublic OutputStream getBody() throws IOException {return this.outputStream;}@Overridepublic HttpHeaders getHeaders() {return this.originalHttpOutputMessage.getHeaders();}}
}
注入HttpMessageConverter实现类对象
@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {@Overridepublic void extendMessageConverters(List<HttpMessageConverter<?>> converters) {MappingJackson2HttpMessageConverter converter = null;for (HttpMessageConverter<?> messageConverter : converters) {if (messageConverter instanceof MappingJackson2HttpMessageConverter) {converter = (MappingJackson2HttpMessageConverter) messageConverter;break;}}if (converter != null) {// 注入MyMappingJackson2HttpMessageConverterMyMappingJackson2HttpMessageConverter myMappingJackson2HttpMessageConverter =new MyMappingJackson2HttpMessageConverter(converter);converters.add(0, myMappingJackson2HttpMessageConverter);}}
}
相关文章:
SpringMVC DispatcherServlet源码(5) HttpMessageConverter扩展
前文通过阅读源码,深入分析了DispatcherServlet及相关组件的工作流程,本文不再阅读源码,介绍一下扩展HttpMessageConverter的方式。 HttpMessageConverter工作方式及扩展方式 前文介绍过,HttpMessageConverter是读写请求体和响应…...
day16_API
今日内容 上课同步视频:CuteN饕餮的个人空间_哔哩哔哩_bilibili 同步笔记沐沐霸的博客_CSDN博客-Java2301 零、 复习昨日 一、作业 二、String 三、StringBuffer&StringBuilder 四、日期 零、 复习昨日 见晨考 一、String String代表字符串,类,java程序中的所有字符串&…...

十二月券商金工精选
✦研报目录✦ ✦简述✦ 按发布时间排序 华宝证券 主动暴露的得与失—从Barra框架到私募指增因子分析方法 发布日期:2022-12-01 关键词:股票、Barra、风险暴露、指数增强 主要内容:本文针对私募指数增强产品的策略流程,设计…...
JUnit
Junit 简介 JUnit是一个开源的java单元测试框架,它是XUnit测试体系架架构的一种体现 是Java语言事实上的标准单元测试库真正的优势来自于JUnit所采作用的思想和技术,而不是框架本身。推动了单元测试、测试先行的编程和测试驱动的开发JUnit衍生了许多xUn…...
MySQL学习笔记4-乐观锁和悲观锁
1.定义 乐观锁和倍灌水是并发控制采用的技术手段,确保当多个数位同时对数据中同一数据存取时,不会破坏事物的隔离性、统一性和数据库统一性 乐观锁 假定不会发生并发冲突,只在提交操作时检测是否违反数据完整性 实现方式: 记录…...
踩大坑:json格式存储wav二进制内容
需求描述: 需要将wav音频文件以二进制的形式读出,存放到 json 中,发送post请求到服务,服务解析json,得到二进制内容后放进ASR模型得出转录结果。 记一次坑: # 将wav以二进制形式读出存放到json中 f ope…...

加入CSDN的一年,我收获了这些……
加入CSDN的一年,我收获了这些……加入CSDN的一年,我收获了这些……加入CSDN的一年,我收获了这些…… 🚀🚀时光如白驹过隙般,飞逝而过。一转眼,我就已经是一名大二的学生了,也已经在…...

【Python学习笔记】44.Python3 MongoDB和urllib
前言 本章介绍Python的MongoDB和urllib。 Python MongoDB MongoDB 是目前最流行的 NoSQL 数据库之一,使用的数据类型 BSON(类似 JSON)。 PyMongo Python 要连接 MongoDB 需要 MongoDB 驱动,这里我们使用 PyMongo 驱动来连接。…...

LVS中的keepalived高可用
文章目录前言一、Keepalived简介二、keepalived工作原理三、配置文件四、实验1.某台Real Server down2.LVS本身down实验过程:五、代码详细演示整体过程调度器安装软件、设置测试keepalived对后端RS的健康检测backup服务主机设置前言 一、Keepalived简介 Keepalived是…...

【Vue3】组件数据懒加载
组件数据懒加载-基本使用 目标:通过useIntersectionObserver优化新鲜好物和人气推荐模块 电商类网站,尤其是首页,内容有好几屏,而如果一上来就加载所有屏的数据,并渲染所有屏的内容会导致首页加载很慢。 数据懒加载&a…...

基于 SmartX 分布式存储的 iSCSI 与两种 NVMe-oF 技术与性能对比
作者:深耕行业的 SmartX 金融团队本文重点SmartX 分布式块存储 ZBS 提供 2 种存算分离架构下的数据接入协议,分别是 iSCSI 和 NVMe-oF。其中,iSCSI 虽然具有很多优势,但不适合支持高性能的工作负载,这也是 SmartX 选择…...
Anaconda 安装 Pytorch
下载Anaconda,最新版本的即可,默认安装,最好不要安装在C盘,否则后面C盘容量会很大。 安装Pytorch 打开 Anaconda Prompt ,先切换镜像源为国内清华镜像源,这样安装包的时候下载速度会快一些,也容易成功一些。 在 Anaconda Prompt 命令行依次输入以下四条命令切换到清华镜…...

从零开始使用MMSegmentation训练Segformer
从零开始使用MMSegmentation训练Segformer 写在前面:最新想要用最新的分割算法如:Segformer or SegNeXt 在自己的数据集上进行训练,但是有不是搞语义分割出身的,而且也没有系统的学过MMCV以及MMSegmentation。所以就折腾了很久&am…...

会利用信息差赚钱的人才是聪明人
毕业后找不到工作,穷到只剩下时间,大小做了20多份副业兼职,终于找到了可靠的渠道, 我是专科生,学历不好,专业拉胯。毕业后,我找了两三份工作。要么工资太低,只能交房租,…...

【机器学习】Adaboost
1.什么是Adaboost AdaBoost(adapt boost),自适应推进算法,属于Boosting方法的学习机制。是一种通过改变训练样本权重来学习多个弱分类器并进行线性结合的过程。它的自适应在于:被前一个基本分类器误分类的样本的权值会…...

深度学习神经网络基础知识(二)权重衰减、暂退法(Dropout)
专栏:神经网络复现目录 深度学习神经网络基础知识(二) 本文讲述神经网络基础知识,具体细节讲述前向传播,反向传播和计算图,同时讲解神经网络优化方法:权重衰减,Dropout等方法,最后进行Kaggle实…...
[面试直通版]网络协议面试核心之HTTP,HTTPS,DNS-DNS安全
点击->计算机网络复习的文章集<-点击 目录 典型问题: 部分现象 DNS劫持 DNS欺骗 DDoS攻击 典型问题: 什么是DNS劫持,DNS欺骗,是什么原理如何防范DNS攻击? 部分现象 错误域名解析到纠错导航页面错误域名解析…...

【OJ】A+B=X
📚Description: 数列S中有n个整数,判断S中是否存在两个数A、B,使之和等于X。 ⏳Input: 第一行为T,输入包括T组测试数据。 每组数据第一行包括两个数字n和X,第二行有n个整数,表示数列S,(1&l…...

Python实现性能自动化测试,还可以如此简单
Python实现性能自动化测试,还可以如此简单 目录:导读 一、思考❓❔ 二、基础操作🔨🔨 三、综合案例演练🔨🔨 四、总结💡💡 写在最后 一、思考❓❔ 1.什么是性能自动化测试? 性…...
Leetcode力扣秋招刷题路-0080
从0开始的秋招刷题路,记录下所刷每道题的题解,帮助自己回顾总结 80. 删除有序数组中的重复项 II 给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长…...

376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...

用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...