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内部实现

  1.  
    /** @deprecated */
  2.  
    @Deprecated
  3.  
    public ObjectMapper enableDefaultTyping(ObjectMapper.DefaultTyping dti) {
  4.  
    return this.enableDefaultTyping(dti, As.WRAPPER_ARRAY);
  5.  
    }

查看enableDefaultTyping内部实现

  1.  
    /** @deprecated */
  2.  
    @Deprecated
  3.  
    public ObjectMapper enableDefaultTyping(ObjectMapper.DefaultTyping applicability, As includeAs) {
  4.  
    return this.activateDefaultTyping(this.getPolymorphicTypeValidator(), applicability, includeAs);
  5.  
    }

查看activateDefaultTyping内部实现

  1.  
    public ObjectMapper activateDefaultTyping(PolymorphicTypeValidator ptv, ObjectMapper.DefaultTyping applicability, As includeAs) {
  2.  
    if (includeAs == As.EXTERNAL_PROPERTY) {
  3.  
    throw new IllegalArgumentException("Cannot use includeAs of " + includeAs);
  4.  
    } else {
  5.  
    TypeResolverBuilder<?> typer = this._constructDefaultTypeResolverBuilder(applicability, ptv);
  6.  
    typer = typer.init(Id.CLASS, (TypeIdResolver)null);
  7.  
    typer = typer.inclusion(includeAs);
  8.  
    return this.setDefaultTyping(typer);
  9.  
    }
  10.  
    }

这里我们可以直接调用activateDefaultTyping方法了,从而不用调用过期的enableDefaultTyping方法。

再看activateDefaultTyping的参数默认是哪些呢?代码里有这样一个静态初始化:

  1.  
    static {
  2.  
    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.  
    }

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去吧:

  1.  
    public static enum DefaultTyping {
  2.  
    JAVA_LANG_OBJECT,
  3.  
    OBJECT_AND_NON_CONCRETE,
  4.  
    NON_CONCRETE_AND_ARRAYS,
  5.  
    NON_FINAL,
  6.  
    EVERYTHING;
  7.  
    private DefaultTyping() {
  8.  
    }
  9.  
    }

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 。

  1.  
    package com.l1nk3r.jackson;
  2.  
    import com.fasterxml.jackson.databind.ObjectMapper;
  3.  
    import java.io.IOException;
  4.  
     
  5.  
    public class JavaLangObject {
  6.  
    public static void main(String args[]) throws IOException {
  7.  
    People p = new People();
  8.  
    p.age = 10;
  9.  
    p.name = "com.l1nk3r.jackson.l1nk3r";
  10.  
    p.object = new l1nk3r();
  11.  
    ObjectMapper mapper = new ObjectMapper();
  12.  
    mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT);
  13.  
    String json = mapper.writeValueAsString(p);
  14.  
    System.out.println(json);
  15.  
    //{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["com.l1nk3r.jackson.l1nk3r",{"length":100}]}
  16.  
    People p2 = mapper.readValue(json, People.class);
  17.  
    System.out.println(p2);
  18.  
    //age=10, name=com.l1nk3r.jackson.l1nk3r, com.l1nk3r.jackson.l1nk3r@4566e5bd
  19.  
    }
  20.  
    }
  21.  
    class People {
  22.  
    public int age;
  23.  
    public String name;
  24.  
    public Object object;
  25.  
     
  26.  
    @Override
  27.  
    public String toString() {
  28.  
    return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object);
  29.  
    }
  30.  
    }
  31.  
    class l1nk3r {
  32.  
    public int length = 100;
  33.  
    }

4.2、OBJECT_AND_NON_CONCRETE

所以按照上面的描述那么输出的序列化json信息中应该携带了相关的类的信息,而在反序列化的时候自然会进行还原。

OBJECT_AND_NON_CONCRETE :除了上文 提到的特征,当类里有 Interface 、 AbstractClass 时,对其进行序列化和反序列化。(当然,这些类本身需要是合法的、可以被序列化/反序列化的对象)。

例如下面的代码,这次我们添加名为 Sex 的 interface ,发现它被正确序列化、反序列化了,就是这个选项控制的。

  1.  
    package com.l1nk3r.jackson;
  2.  
    import com.fasterxml.jackson.databind.ObjectMapper;
  3.  
    import java.io.IOException;
  4.  
     
  5.  
    public class JavaLangObject {
  6.  
    public static void main(String args[]) throws IOException {
  7.  
    People p = new People();
  8.  
    p.age = 10;
  9.  
    p.name = "com.l1nk3r.jackson.l1nk3r";
  10.  
    p.object = new l1nk3r();
  11.  
    p.sex=new MySex();
  12.  
    ObjectMapper mapper = new ObjectMapper();
  13.  
    mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
  14.  
    String json = mapper.writeValueAsString(p);
  15.  
    System.out.println(json);
  16.  
    //{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["com.l1nk3r.jackson.l1nk3r",{"length":100}],"sex":["com.l1nk3r.jackson.MySex",{"sex":0}]}
  17.  
    People p2 = mapper.readValue(json, People.class);
  18.  
    System.out.println(p2);
  19.  
    //age=10, name=com.l1nk3r.jackson.l1nk3r, com.l1nk3r.jackson.l1nk3r@ff5b51f
  20.  
    }
  21.  
    }
  22.  
    class People {
  23.  
    public int age;
  24.  
    public String name;
  25.  
    public Object object;
  26.  
    public Sex sex;
  27.  
     
  28.  
    @Override
  29.  
    public String toString() {
  30.  
    return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object);
  31.  
    }
  32.  
    }
  33.  
    class l1nk3r {
  34.  
    public int length = 100;
  35.  
    }
  36.  
    class MySex implements Sex {
  37.  
    int sex;
  38.  
     
  39.  
    @Override
  40.  
    public int getSex() {
  41.  
    return sex;
  42.  
    }
  43.  
     
  44.  
    @Override
  45.  
    public void setSex(int sex) {
  46.  
    this.sex = sex;
  47.  
    }
  48.  
    }
  49.  
     
  50.  
    interface Sex {
  51.  
    public void setSex(int sex);
  52.  
    public int getSex();
  53.  
    }

默认的、无参的 enableDefaultTyping 是 OBJECT_AND_NON_CONCRETE 。

4.3、NON_CONCRETE_AND_ARRAYS

NON_CONCRETE_AND_ARRAYS :除了上文提到的特征,还支持上文全部类型的Array类型。

例如下面的代码,我们的Object里存放l1nk3r的数组。

  1.  
    package com.l1nk3r.jackson;
  2.  
    import com.fasterxml.jackson.databind.ObjectMapper;
  3.  
    import java.io.IOException;
  4.  
     
  5.  
    public class JavaLangObject {
  6.  
    public static void main(String args[]) throws IOException {
  7.  
    People p = new People();
  8.  
    p.age = 10;
  9.  
    p.name = "com.l1nk3r.jackson.l1nk3r";
  10.  
    l1nk3r[] l1nk3rs= new l1nk3r[2];
  11.  
    l1nk3rs[0]=new l1nk3r();
  12.  
    l1nk3rs[1]=new l1nk3r();
  13.  
    p.object = l1nk3rs;
  14.  
    p.sex=new MySex();
  15.  
    ObjectMapper mapper = new ObjectMapper();
  16.  
    mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS);
  17.  
    String json = mapper.writeValueAsString(p);
  18.  
    System.out.println(json);
  19.  
    //{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["[Lcom.l1nk3r.jackson.l1nk3r;",[{"length":100},{"length":100}]],"sex":["com.l1nk3r.jackson.MySex",{"sex":0}]}
  20.  
    People p2 = mapper.readValue(json, People.class);
  21.  
    System.out.println(p2);
  22.  
    //age=10, name=com.l1nk3r.jackson.l1nk3r, [Lcom.l1nk3r.jackson.l1nk3r;@1e127982
  23.  
    }
  24.  
    }
  25.  
    class People {
  26.  
    public int age;
  27.  
    public String name;
  28.  
    public Object object;
  29.  
    public Sex sex;
  30.  
     
  31.  
    @Override
  32.  
    public String toString() {
  33.  
    return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object);
  34.  
    }
  35.  
    }
  36.  
    class l1nk3r {
  37.  
    public int length = 100;
  38.  
    }
  39.  
    class MySex implements Sex {
  40.  
    int sex;
  41.  
     
  42.  
    @Override
  43.  
    public int getSex() {
  44.  
    return sex;
  45.  
    }
  46.  
     
  47.  
    @Override
  48.  
    public void setSex(int sex) {
  49.  
    this.sex = sex;
  50.  
    }
  51.  
    }
  52.  
     
  53.  
    interface Sex {
  54.  
    public void setSex(int sex);
  55.  
     
  56.  
    public int getSex();
  57.  
    }

4.4、NON_FINAL

NON_FINAL :包括上文提到的所有特征,而且包含即将被序列化的类里的全部、非final的属性,也就是相当于整个类、除final外的的属性信息都需要被序列化和反序列化。

例如下面的代码,添加了类型为l1nk3r的变量,非Object也非虚,但也可以被序列化出来。

  1.  
    package com.l1nk3r.jackson;
  2.  
    import com.fasterxml.jackson.databind.ObjectMapper;
  3.  
    import java.io.IOException;
  4.  
     
  5.  
    public class JavaLangObject {
  6.  
    public static void main(String args[]) throws IOException {
  7.  
    People p = new People();
  8.  
    p.age = 10;
  9.  
    p.name = "l1nk3r";
  10.  
    p.object = new l1nk3r();
  11.  
    p.sex=new MySex();
  12.  
    p.l1nk3r=new l1nk3r();
  13.  
    ObjectMapper mapper = new ObjectMapper();
  14.  
    mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  15.  
    String json = mapper.writeValueAsString(p);
  16.  
    System.out.println(json);
  17.  
    //["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}]}]
  18.  
    People p2 = mapper.readValue(json, People.class);
  19.  
    System.out.println(p2);
  20.  
    //age=10, name=l1nk3r, com.l1nk3r.jackson.l1nk3r@ff5b51f
  21.  
    }
  22.  
    }
  23.  
    class People {
  24.  
    public int age;
  25.  
    public String name;
  26.  
    public Object object;
  27.  
    public Sex sex;
  28.  
    public l1nk3r l1nk3r;
  29.  
     
  30.  
    @Override
  31.  
    public String toString() {
  32.  
    return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object, sex == null ? "null" : sex,
  33.  
    l1nk3r == null ? "null" : l1nk3r);
  34.  
    }
  35.  
    }
  36.  
    class l1nk3r {
  37.  
    public int length = 100;
  38.  
    }
  39.  
    class MySex implements Sex {
  40.  
    int sex;
  41.  
     
  42.  
    @Override
  43.  
    public int getSex() {
  44.  
    return sex;
  45.  
    }
  46.  
     
  47.  
    @Override
  48.  
    public void setSex(int sex) {
  49.  
    this.sex = sex;
  50.  
    }
  51.  
    }
  52.  
     
  53.  
    interface Sex {
  54.  
    public void setSex(int sex);
  55.  
     
  56.  
    public int getSex();
  57.  
    }

5 JsonTypeInfo注解

@JsonTypeInfo 也是jackson多态类型绑定的一种方式,它一共支持下面5种类型的取值。

  1.  
    @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
  2.  
    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
  3.  
    @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)
  4.  
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
  5.  
    @JsonTypeInfo(use = JsonTypeInfo.Id.COSTOM)

下面使用一段测试代码可以看看这五个类型的分别作用。

  1.  
    package com.l1nk3r.jackson;
  2.  
    import com.fasterxml.jackson.annotation.JsonTypeInfo;
  3.  
    import com.fasterxml.jackson.databind.ObjectMapper;
  4.  
    import java.io.IOException;
  5.  
     
  6.  
    public class Jsontypeinfo {
  7.  
    public static void main(String[] args) throws IOException {
  8.  
    ObjectMapper mapper= new ObjectMapper();
  9.  
    User user = new User();
  10.  
    user.name= "l1nk3r";
  11.  
    user.age=100;
  12.  
    user.obj=new Height();
  13.  
    String json = mapper.writeValueAsString(user);
  14.  
    System.out.println(json);
  15.  
    }
  16.  
    }
  17.  
     
  18.  
    class User{
  19.  
    public String name;
  20.  
    public int age;
  21.  
    @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
  22.  
    public Object obj;
  23.  
     
  24.  
    public String toString(){
  25.  
    return "name:" + name + " age:" + age + " obj:" + obj;
  26.  
    }
  27.  
    }
  28.  
     
  29.  
    class Height{
  30.  
    public int h = 100;
  31.  
    }

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方法过期解决的更多相关文章

  1. spring boot定制Jackson ObjectMapper,为什么不生效

    先说结论: 项目中定制了spring 的redisTemplate,而这个template没有使用我自定义的Jackson ObjectMapper.所以不生效. 下面是详细过程: 起因是spring ...

  2. 180611-Spring之RedisTemplate配置与使用

        logo 文章链接:https://liuyueyi.github.io/hexblog/2018/06/11/180611-Spring之RedisTemplate配置与使用/ Spring ...

  3. Maven:如何在eclipse里新建一个Maven的java项目和web项目

    如何在eclipse里新建一个Maven的java项目和web项目: 一:java项目 New-->Other-->Maven 右击项目-->properties,修改以下文件: ① ...

  4. 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的返回值自动进行各种序列化处理(序列化为 ...

  5. Entity Framework 6 Recipes 2nd Edition(11-6)译 -> 从一个”模型定义”函数里返回一个复杂类型

    11-6.从一个”模型定义”函数里返回一个复杂类型 问题 想要从一个”模型定义”函数返回一个复杂类型 解决方案 假设我们有一个病人(patient)和他们访客(visit)的模型,如 Figure 1 ...

  6. Jackson ObjectMapper类使用解析

    /** * Jackson ObjectMapper类 */ //ObjectMapper类是Jackson库的主要类.它提供一些功能将转换成Java对象匹配JSON结构,反之亦然.它使用JsonPa ...

  7. 第一次使用Android Studio时你应该知道的一切配置(二):新建一个属于自己的工程并安装Genymotion模拟器

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  8. try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,还是在return之后执行?

    这是一个很有趣的问题,我测试的结果是:是在return中间执行. 我在网上搜寻了一些资料,下面是参考代码: /** * */ package com.b510.test; /** * try {}里有 ...

  9. jquery-multiselect在ie6里的一个bug

    在使用jquery-multiselect(一个把下拉框改造成带checkbox的可以多选的控件)时,正常时应该是下面这样:而它在ie6里是下面这样: 其中第一个bug参考‘ie6里png图片不透明’ ...

  10. Jackson ObjectMapper类

    ObjectMapper类是Jackson库的主要类.它提供一些功能将转换成Java对象匹配JSON结构,反之亦然.它使用JsonParser和JsonGenerator的实例实现JSON实际的读/写 ...

随机推荐

  1. PC软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具

    前言 国庆假期各种活动比较多,直到上班才有时间来更新文章~ 不过这两天我还是做了个小玩意(Clipify),起因是想给之前开发来自己用的简单视频剪辑工具 QuickCutSharp 加个功能,不过这个 ...

  2. Android复习(三)清单文件中的元素——> activity

    转自:  https://developer.android.google.cn/guide/topics/manifest/activity-element <activity> 语法: ...

  3. KubeKey 离线部署 KubeSphere v3.4.1 和 K8s v1.26 实战指南

    作者:运维有术 前言 知识点 定级:入门级 了解清单 (manifest) 和制品 (artifact) 的概念 掌握 manifest 清单的编写方法 根据 manifest 清单制作 artifa ...

  4. 对于 Serverless, DevOps, 多云及边缘可观察性的思考与实践

    从单体到微服务,再到 Serverless,应用在逐渐地轻量化.有人可能会有疑问,微服务都还没有顺畅的搭建起来,现在又出了 Serverless,应该怎么办? 其实两者之间并不是一个相互替代的关系.我 ...

  5. (系列八).net8 webApi后端框架轮子,欢迎下载。

    说明 该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发). 该系统文章,我会尽量说的非常详细,做到不管新手.老手都能看懂. 说明:OverallAuth2 ...

  6. C语言之声明

    C语言之声明 1.声明与定义 声明语法 说明符(说明类型或修改缺省属性) 声明表达式列表 说明符 类型说明:int, float 存储属性:static,auto 类型限定:const, volati ...

  7. 开源一款AI聊天工具

    学习Spring AI项目,开发一款问答工具 AI大模型请注册并生成API密钥 获取API密钥地址:https://bigmodel.cn/console/overview 开源地址:LuckyAI ...

  8. Python自带difflib模块

    官方文档:https://docs.python.org/3/library/difflib.html difflib模块的作用是比对文本之间的差异,且支持输出可读性比较强的HTML文档,与Linux ...

  9. SaaS平台的组织数据模型设计

    大家好,我是汤师爷~ 想要深入理解零售企业的组织架构并不容易.大多数人并没有实际经营过零售企业,更不曾参与设计其组织架构. 在调研商家的过程中,我们通常只能了解他们组织架构的现状,却难以直接与企业高层 ...

  10. 如何在cnblogs的发文中使用自定义地址作为发文链接

    要知道在cnblogs中发表内容后其默认的链接地址都是一串数字的形式,比如本篇的默认地址:https://www.cnblogs.com/xyz/p/18461898 但是为了让发表的内容更有个性化, ...