zoukankan      html  css  js  c++  java
  • 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内部实现

    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/

  • 相关阅读:
    ES学习(十)
    ES学习(九)
    ES学习(八)
    ES学习(七)--documentAPI
    ES学习(六)--shard和replica机制
    ES学习(四)--嵌套聚合、下钻分析、聚合分析
    uniapp中常见的请求方法封装 --来之插件市场(全局方法封装(请求/正则/URI)
    工具/插件
    express中文件的上传 multer
    express中开发常用
  • 原文地址:https://www.cnblogs.com/exmyth/p/13794524.html
Copyright © 2011-2022 走看看