RedisTemplate配置的jackson.ObjectMapper里的一个enableDefaultTyping方法过期解决
1、前言
最近升级SpringBoot,从2.1.6版本升级到2.2.6版本,发现enableDefaultTyping方法过期过期了。

该方法是指定序列化输入的类型,就是将数据库里的数据安装一定类型存储到redis缓存中。
2、为什么要指定序列化输入类型
2.1、没有指定序列化输入类型
如果注释掉enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL),那存储到redis里的数据将是没有类型的纯json,我们调用redis API获取到数据后,java解析将是一个LinkHashMap类型的key-value的数据结构,我们需要使用的话就要自行解析,这样增加了编程的复杂度。
[{"id":72,"uuid":"c4d7fc52-4096-4c79-81ef-32cb1b87fd28","type":2}]
2.2、指定序列化输入类型
指定enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL)的话,存储到redis里的数据将是有类型的json数据,例如:
["java.util.ArrayList",[{"@class":"com.model.app","id":72,"uuid":"c4d7fc52-4096-4c79-81ef-32cb1b87fd28","type":2}]]
这样java获取到数据后,将会将数据自动转化为java.util.ArrayList和com.model.app,方便直接使用。
3、enableDefaultTyping过期怎么解决
3.1 查找函数接口
查看enableDefaultTyping内部实现
- /** @deprecated */
- @Deprecated
- public ObjectMapper enableDefaultTyping(ObjectMapper.DefaultTyping dti) {
- return this.enableDefaultTyping(dti, As.WRAPPER_ARRAY);
- }
查看enableDefaultTyping内部实现
- /** @deprecated */
- @Deprecated
- public ObjectMapper enableDefaultTyping(ObjectMapper.DefaultTyping applicability, As includeAs) {
- return this.activateDefaultTyping(this.getPolymorphicTypeValidator(), applicability, includeAs);
- }
查看activateDefaultTyping内部实现
- public ObjectMapper activateDefaultTyping(PolymorphicTypeValidator ptv, ObjectMapper.DefaultTyping applicability, As includeAs) {
- if (includeAs == As.EXTERNAL_PROPERTY) {
- throw new IllegalArgumentException("Cannot use includeAs of " + includeAs);
- } else {
- TypeResolverBuilder<?> typer = this._constructDefaultTypeResolverBuilder(applicability, ptv);
- typer = typer.init(Id.CLASS, (TypeIdResolver)null);
- typer = typer.inclusion(includeAs);
- return this.setDefaultTyping(typer);
- }
- }
这里我们可以直接调用activateDefaultTyping方法了,从而不用调用过期的enableDefaultTyping方法。
再看activateDefaultTyping的参数默认是哪些呢?代码里有这样一个静态初始化:
- static {
- DEFAULT_BASE = new BaseSettings((ClassIntrospector)null, DEFAULT_ANNOTATION_INTROSPECTOR, (PropertyNamingStrategy)null, TypeFactory.defaultInstance(), (TypeResolverBuilder)null, StdDateFormat.instance, (HandlerInstantiator)null, Locale.getDefault(), (TimeZone)null, Base64Variants.getDefaultVariant(), LaissezFaireSubTypeValidator.instance);
- }
3.2 解决
从而我们知道,默认的参数有哪些,
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance ,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.WRAPPER_ARRAY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
4 DefaultTyping 类型定义
如果想知道详细的说明,大家google去吧:
- public static enum DefaultTyping {
- JAVA_LANG_OBJECT,
- OBJECT_AND_NON_CONCRETE,
- NON_CONCRETE_AND_ARRAYS,
- NON_FINAL,
- EVERYTHING;
- private DefaultTyping() {
- }
- }
DefaultTyping有四个选项:
JAVA_LANG_OBJECT: 当对象属性类型为Object时生效;
OBJECT_AND_NON_CONCRETE: 当对象属性类型为Object或者非具体类型(抽象类和接口)时生效;
NON_CONCRETE_AND+_ARRAYS: 同上, 另外所有的数组元素的类型都是非具体类型或者对象类型;
NON_FINAL: 对所有非final类型或者非final类型元素的数组。
因此,当开启DefaultTyping后,会开发者在反序列化时指定要还原的类,过程中调用其构造方法setter方法或某些特殊的getter方法,当这些方法中存在一些危险操作时就造成了代码执行。
下面其实可以分别看看这四个值的作用是什么。
4.1、JAVA_LANG_OBJECT
JAVA_LANG_OBJECT :当类里的属性声明为一个Object时,会对该属性进行序列化和反序列化,并且明确规定类名。(当然,这个Object本身也得是一个可被序列化/反序列化的类)。
例如下面的代码,我们给 People 里添加一个 Object object 。
- package com.l1nk3r.jackson;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import java.io.IOException;
- public class JavaLangObject {
- public static void main(String args[]) throws IOException {
- People p = new People();
- p.age = 10;
- p.name = "com.l1nk3r.jackson.l1nk3r";
- p.object = new l1nk3r();
- ObjectMapper mapper = new ObjectMapper();
- mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT);
- String json = mapper.writeValueAsString(p);
- System.out.println(json);
- //{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["com.l1nk3r.jackson.l1nk3r",{"length":100}]}
- People p2 = mapper.readValue(json, People.class);
- System.out.println(p2);
- //age=10, name=com.l1nk3r.jackson.l1nk3r, com.l1nk3r.jackson.l1nk3r@4566e5bd
- }
- }
- class People {
- public int age;
- public String name;
- public Object object;
- @Override
- public String toString() {
- return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object);
- }
- }
- class l1nk3r {
- public int length = 100;
- }
4.2、OBJECT_AND_NON_CONCRETE
所以按照上面的描述那么输出的序列化json信息中应该携带了相关的类的信息,而在反序列化的时候自然会进行还原。
OBJECT_AND_NON_CONCRETE :除了上文 提到的特征,当类里有 Interface 、 AbstractClass 时,对其进行序列化和反序列化。(当然,这些类本身需要是合法的、可以被序列化/反序列化的对象)。
例如下面的代码,这次我们添加名为 Sex 的 interface ,发现它被正确序列化、反序列化了,就是这个选项控制的。
- package com.l1nk3r.jackson;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import java.io.IOException;
- public class JavaLangObject {
- public static void main(String args[]) throws IOException {
- People p = new People();
- p.age = 10;
- p.name = "com.l1nk3r.jackson.l1nk3r";
- p.object = new l1nk3r();
- p.sex=new MySex();
- ObjectMapper mapper = new ObjectMapper();
- mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
- String json = mapper.writeValueAsString(p);
- System.out.println(json);
- //{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["com.l1nk3r.jackson.l1nk3r",{"length":100}],"sex":["com.l1nk3r.jackson.MySex",{"sex":0}]}
- People p2 = mapper.readValue(json, People.class);
- System.out.println(p2);
- //age=10, name=com.l1nk3r.jackson.l1nk3r, com.l1nk3r.jackson.l1nk3r@ff5b51f
- }
- }
- class People {
- public int age;
- public String name;
- public Object object;
- public Sex sex;
- @Override
- public String toString() {
- return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object);
- }
- }
- class l1nk3r {
- public int length = 100;
- }
- class MySex implements Sex {
- int sex;
- @Override
- public int getSex() {
- return sex;
- }
- @Override
- public void setSex(int sex) {
- this.sex = sex;
- }
- }
- interface Sex {
- public void setSex(int sex);
- public int getSex();
- }
默认的、无参的 enableDefaultTyping 是 OBJECT_AND_NON_CONCRETE 。
4.3、NON_CONCRETE_AND_ARRAYS
NON_CONCRETE_AND_ARRAYS :除了上文提到的特征,还支持上文全部类型的Array类型。
例如下面的代码,我们的Object里存放l1nk3r的数组。
- package com.l1nk3r.jackson;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import java.io.IOException;
- public class JavaLangObject {
- public static void main(String args[]) throws IOException {
- People p = new People();
- p.age = 10;
- p.name = "com.l1nk3r.jackson.l1nk3r";
- l1nk3r[] l1nk3rs= new l1nk3r[2];
- l1nk3rs[0]=new l1nk3r();
- l1nk3rs[1]=new l1nk3r();
- p.object = l1nk3rs;
- p.sex=new MySex();
- ObjectMapper mapper = new ObjectMapper();
- mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS);
- String json = mapper.writeValueAsString(p);
- System.out.println(json);
- //{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["[Lcom.l1nk3r.jackson.l1nk3r;",[{"length":100},{"length":100}]],"sex":["com.l1nk3r.jackson.MySex",{"sex":0}]}
- People p2 = mapper.readValue(json, People.class);
- System.out.println(p2);
- //age=10, name=com.l1nk3r.jackson.l1nk3r, [Lcom.l1nk3r.jackson.l1nk3r;@1e127982
- }
- }
- class People {
- public int age;
- public String name;
- public Object object;
- public Sex sex;
- @Override
- public String toString() {
- return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object);
- }
- }
- class l1nk3r {
- public int length = 100;
- }
- class MySex implements Sex {
- int sex;
- @Override
- public int getSex() {
- return sex;
- }
- @Override
- public void setSex(int sex) {
- this.sex = sex;
- }
- }
- interface Sex {
- public void setSex(int sex);
- public int getSex();
- }
4.4、NON_FINAL
NON_FINAL :包括上文提到的所有特征,而且包含即将被序列化的类里的全部、非final的属性,也就是相当于整个类、除final外的的属性信息都需要被序列化和反序列化。
例如下面的代码,添加了类型为l1nk3r的变量,非Object也非虚,但也可以被序列化出来。
- package com.l1nk3r.jackson;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import java.io.IOException;
- public class JavaLangObject {
- public static void main(String args[]) throws IOException {
- People p = new People();
- p.age = 10;
- p.name = "l1nk3r";
- p.object = new l1nk3r();
- p.sex=new MySex();
- p.l1nk3r=new l1nk3r();
- ObjectMapper mapper = new ObjectMapper();
- mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
- String json = mapper.writeValueAsString(p);
- System.out.println(json);
- //["com.l1nk3r.jackson.People",{"age":10,"name":"l1nk3r","object":["com.l1nk3r.jackson.l1nk3r",{"length":100}],"sex":["com.l1nk3r.jackson.MySex",{"sex":0}],"l1nk3r":["com.l1nk3r.jackson.l1nk3r",{"length":100}]}]
- People p2 = mapper.readValue(json, People.class);
- System.out.println(p2);
- //age=10, name=l1nk3r, com.l1nk3r.jackson.l1nk3r@ff5b51f
- }
- }
- class People {
- public int age;
- public String name;
- public Object object;
- public Sex sex;
- public l1nk3r l1nk3r;
- @Override
- public String toString() {
- return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object, sex == null ? "null" : sex,
- l1nk3r == null ? "null" : l1nk3r);
- }
- }
- class l1nk3r {
- public int length = 100;
- }
- class MySex implements Sex {
- int sex;
- @Override
- public int getSex() {
- return sex;
- }
- @Override
- public void setSex(int sex) {
- this.sex = sex;
- }
- }
- interface Sex {
- public void setSex(int sex);
- public int getSex();
- }
5 JsonTypeInfo注解
@JsonTypeInfo 也是jackson多态类型绑定的一种方式,它一共支持下面5种类型的取值。
- @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
- @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
- @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)
- @JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
- @JsonTypeInfo(use = JsonTypeInfo.Id.COSTOM)
下面使用一段测试代码可以看看这五个类型的分别作用。
- package com.l1nk3r.jackson;
- import com.fasterxml.jackson.annotation.JsonTypeInfo;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import java.io.IOException;
- public class Jsontypeinfo {
- public static void main(String[] args) throws IOException {
- ObjectMapper mapper= new ObjectMapper();
- User user = new User();
- user.name= "l1nk3r";
- user.age=100;
- user.obj=new Height();
- String json = mapper.writeValueAsString(user);
- System.out.println(json);
- }
- }
- class User{
- public String name;
- public int age;
- @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
- public Object obj;
- public String toString(){
- return "name:" + name + " age:" + age + " obj:" + obj;
- }
- }
- class Height{
- public int h = 100;
- }
5.1、Id.NONE
这种方式的输出结果实际上是我们最想要的,这里只需要相关参数的值,并没有其他一些无用信息。
{"name":"l1nk3r","age":100,"obj":{"h":100}}
5.2、Id.CLASS
这种方式的输出结果中携带了相关java类,也就是说反序列化的时候如果使用了JsonTypeInfo.Id.CLASS修饰的话,可以通过 @class 方式指定相关类,并进行相关调用。
{"name":"l1nk3r","age":100,"obj":{"@class":"com.l1nk3r.jackson.Height","h":100}}
5.3、Id.MINIMAL_CLASS
这种方式的输出结果也携带了相关类,和 id.CLASS 的区别在 @class 变成了 @c ,从官方文档中描述中这个应该是一个更短的类名字。同样也就是说反序列化的时候如果使用了JsonTypeInfo.Id.MINIMAL_CLASS修饰的话,可以通过 @c 方式指定相关类,并进行相关调用。
{"name":"l1nk3r","age":100,"obj":{"@c":"com.l1nk3r.jackson.Height","h":100}}
5.4、Id.NAME
这种输出方式没有携带类名字,在反序列化时也是不可以利用的。
{"name":"l1nk3r","age":100,"obj":{"@type":"Height","h":100}}
5.5、Id.COSTOM
这个无法直接用,需要手写一个解析器才可以配合使用,所以直接回抛出异常。
6、参考:
http://www.lmxspace.com/2019/07/30/Jackson-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%B1%87%E6%80%BB/
RedisTemplate配置的jackson.ObjectMapper里的一个enableDefaultTyping方法过期解决的更多相关文章
- spring boot定制Jackson ObjectMapper,为什么不生效
先说结论: 项目中定制了spring 的redisTemplate,而这个template没有使用我自定义的Jackson ObjectMapper.所以不生效. 下面是详细过程: 起因是spring ...
- 180611-Spring之RedisTemplate配置与使用
logo 文章链接:https://liuyueyi.github.io/hexblog/2018/06/11/180611-Spring之RedisTemplate配置与使用/ Spring ...
- Maven:如何在eclipse里新建一个Maven的java项目和web项目
如何在eclipse里新建一个Maven的java项目和web项目: 一:java项目 New-->Other-->Maven 右击项目-->properties,修改以下文件: ① ...
- ASP.NET MVC WebApi 返回数据类型序列化控制(json,xml) 用javascript在客户端删除某一个cookie键值对 input点击链接另一个页面,各种操作。 C# 往线程里传参数的方法总结 TCP/IP 协议 用C#+Selenium+ChromeDriver 生成我的咕咚跑步路线地图 (转)值得学习百度开源70+项目
ASP.NET MVC WebApi 返回数据类型序列化控制(json,xml) 我们都知道在使用WebApi的时候Controller会自动将Action的返回值自动进行各种序列化处理(序列化为 ...
- Entity Framework 6 Recipes 2nd Edition(11-6)译 -> 从一个”模型定义”函数里返回一个复杂类型
11-6.从一个”模型定义”函数里返回一个复杂类型 问题 想要从一个”模型定义”函数返回一个复杂类型 解决方案 假设我们有一个病人(patient)和他们访客(visit)的模型,如 Figure 1 ...
- Jackson ObjectMapper类使用解析
/** * Jackson ObjectMapper类 */ //ObjectMapper类是Jackson库的主要类.它提供一些功能将转换成Java对象匹配JSON结构,反之亦然.它使用JsonPa ...
- 第一次使用Android Studio时你应该知道的一切配置(二):新建一个属于自己的工程并安装Genymotion模拟器
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,还是在return之后执行?
这是一个很有趣的问题,我测试的结果是:是在return中间执行. 我在网上搜寻了一些资料,下面是参考代码: /** * */ package com.b510.test; /** * try {}里有 ...
- jquery-multiselect在ie6里的一个bug
在使用jquery-multiselect(一个把下拉框改造成带checkbox的可以多选的控件)时,正常时应该是下面这样:而它在ie6里是下面这样: 其中第一个bug参考‘ie6里png图片不透明’ ...
- Jackson ObjectMapper类
ObjectMapper类是Jackson库的主要类.它提供一些功能将转换成Java对象匹配JSON结构,反之亦然.它使用JsonParser和JsonGenerator的实例实现JSON实际的读/写 ...
随机推荐
- PC软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
前言 国庆假期各种活动比较多,直到上班才有时间来更新文章~ 不过这两天我还是做了个小玩意(Clipify),起因是想给之前开发来自己用的简单视频剪辑工具 QuickCutSharp 加个功能,不过这个 ...
- Android复习(三)清单文件中的元素——> activity
转自: https://developer.android.google.cn/guide/topics/manifest/activity-element <activity> 语法: ...
- KubeKey 离线部署 KubeSphere v3.4.1 和 K8s v1.26 实战指南
作者:运维有术 前言 知识点 定级:入门级 了解清单 (manifest) 和制品 (artifact) 的概念 掌握 manifest 清单的编写方法 根据 manifest 清单制作 artifa ...
- 对于 Serverless, DevOps, 多云及边缘可观察性的思考与实践
从单体到微服务,再到 Serverless,应用在逐渐地轻量化.有人可能会有疑问,微服务都还没有顺畅的搭建起来,现在又出了 Serverless,应该怎么办? 其实两者之间并不是一个相互替代的关系.我 ...
- (系列八).net8 webApi后端框架轮子,欢迎下载。
说明 该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发). 该系统文章,我会尽量说的非常详细,做到不管新手.老手都能看懂. 说明:OverallAuth2 ...
- C语言之声明
C语言之声明 1.声明与定义 声明语法 说明符(说明类型或修改缺省属性) 声明表达式列表 说明符 类型说明:int, float 存储属性:static,auto 类型限定:const, volati ...
- 开源一款AI聊天工具
学习Spring AI项目,开发一款问答工具 AI大模型请注册并生成API密钥 获取API密钥地址:https://bigmodel.cn/console/overview 开源地址:LuckyAI ...
- Python自带difflib模块
官方文档:https://docs.python.org/3/library/difflib.html difflib模块的作用是比对文本之间的差异,且支持输出可读性比较强的HTML文档,与Linux ...
- SaaS平台的组织数据模型设计
大家好,我是汤师爷~ 想要深入理解零售企业的组织架构并不容易.大多数人并没有实际经营过零售企业,更不曾参与设计其组织架构. 在调研商家的过程中,我们通常只能了解他们组织架构的现状,却难以直接与企业高层 ...
- 如何在cnblogs的发文中使用自定义地址作为发文链接
要知道在cnblogs中发表内容后其默认的链接地址都是一串数字的形式,比如本篇的默认地址:https://www.cnblogs.com/xyz/p/18461898 但是为了让发表的内容更有个性化, ...