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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM