• 程序验证Jackson反序列化的规则、Jackson序列化与反序列化关键方法程序详细分析


    目录

    0. 为什么要做这个分析

    1. Jackson反序列化时,无参构造、有参构造的执行顺序【附程序截图】

    1.1 没有无参构造时:

    1.2 无参构造和有参构造方法都有的时候先走无参构造;

    2. Jackson反序列化时,无参构造、有参构造的执行顺序的总结

    3. Jackson序列化与反序列化关键方法程序详细分析

    3.1 public T readValue(String content, Class valueType)

    3.1.1 public JsonParser createParser(String content) throws IOException, JsonParseException

    3.1.2 public JavaType constructType(Type type)

    3.1.3 protected JavaType _fromAny(ClassStack context, Type type, TypeBindings bindings)

    3.1.4 protected JavaType _fromClass(ClassStack context, Class rawType, TypeBindings bindings)

    3.2 protected Object _readMapAndClose(JsonParser p0, JavaType valueType)【重要方法】

    3.2.1 protected DefaultDeserializationContext createDeserializationContext(JsonParser p, DeserializationConfig cfg)

    3.2.2 protected JsonDeserializer_findRootDeserializer(DeserializationContext ctxt, JavaType valueType)  throws JsonMappingException

    3.2.3 public final JsonDeserializerfindRootValueDeserializer(JavaType type) throws JsonMappingException

    3.2.4 buildBeanDeserializer 中的 addBeanProps

    3.2.5 public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException

    3.2.6 com.fasterxml.jackson.databind.deser.BeanDeserializer#vanillaDeserialize

    无参构造存在的时候:

    全参构造存在时:

    java.lang.reflect.Constructor#newInstance

    3.2.7 com.fasterxml.jackson.databind.deser.impl.MethodProperty#deserializeAndSet

     3.2.8 com.fasterxml.jackson.databind.deser.std.NumberDeserializers.IntegerDeserializer#deserialize

     3.2.9 com.fasterxml.jackson.core.base.ParserBase#getIntValue

     3.2.10 java.lang.reflect.Method#invoke

    总结

    参考文献


    0. 为什么要做这个分析

    这个反序列化一般都不会出问题,但是但是但是,墨菲定理,在实习中发现了一年前的代码存在有关反序列化的问题;

    具体是原本场景就是直接读数据库,可通过Jackson反序列化读出来的数据,竟然和数据库中的不一样;

    所以就针对Jackson反序列化的规则以及源码进行分析

    1. Jackson反序列化时,无参构造、有参构造的执行顺序【附程序截图】

    1.1 没有无参构造时:

    • 如果有参构造的参数全,或者更多(就是有不存在的值),这样还能正常运行:
    • 如果参数不全则直接异常:

    1.2 无参构造和有参构造方法都有的时候先走无参构造;

    •  无参构造需要set/get方法来完成序列化和反序列化

      • 下图是有无参构造但是没有对应的set/get方法的程序截图,可以看出,age并没有成功读取2;
      • 下图是有无参构造且有对应的set/get方法的程序截图,可以看出,age成功读取2;

    2. Jackson反序列化时,无参构造、有参构造的执行顺序的总结

    • 没有无参构造时:
      • 如果有参构造的参数全,或者更多(就是有不存在的值),这样还能正常运行
      • 如果参数不全则直接异常
    • 无参构造和有参构造方法都有的时候先走无参构造;
      • 无参构造需要set/get方法来完成序列化和反序列化

    3. Jackson序列化与反序列化关键方法程序详细分析

    3.1 public T readValue(String content, Class valueType)

    • readValue
      • 从给定的 JSON 内容字符串反序列化 JSON 内容的方法
      • 抛出异常:
        • IOException – 如果发生低级 IO 问题(意外的输入结束、网络错误)(按原样传递而无需额外包装——请注意,这是 DeserializationFeature.WRAP_EXCEPTIONS 不会导致包装异常的一种情况,即使如果启用)
        • DeserializationFeature.WRAP_EXCEPTIONS :确定杰克逊代码是否应捕获和包装异常(但绝不是错误!)以添加有关问题位置(在输入内)的附加信息的功能。如果启用,大多数异常将被捕获并重新抛出(特别是 java.io.IOExceptions 可以按原样传递,因为它们被声明为可抛出);这很方便,因为所有异常都将被检查和声明,因此有更多的上下文信息。但是,有时调用应用程序可能只想按原样传递“原始”未经检查的异常。功能默认启用。
        • JsonParseException - 如果基础输入包含 JsonParser 支持的类型的无效内容(默认情况下为 JSON)
        • JsonMappingException - 如果输入 JSON 结构与结果类型的预期结构不匹配(或有其他不匹配问题)
    • readValue源码:
    1. /**
    2. * Method to deserialize JSON content from given JSON content String.
    3. *
    4. * @throws IOException if a low-level I/O problem (unexpected end-of-input,
    5. * network error) occurs (passed through as-is without additional wrapping -- note
    6. * that this is one case where {@link DeserializationFeature#WRAP_EXCEPTIONS}
    7. * does NOT result in wrapping of exception even if enabled)
    8. * @throws JsonParseException if underlying input contains invalid content
    9. * of type {@link JsonParser} supports (JSON for default case)
    10. * @throws JsonMappingException if the input JSON structure does not match structure
    11. * expected for result type (or has other mismatch issues)
    12. */
    13. @SuppressWarnings("unchecked")
    14. public T readValue(String content, Class valueType)
    15. throws IOException, JsonParseException, JsonMappingException
    16. {
    17. return (T) _readMapAndClose(_jsonFactory.createParser(content), _typeFactory.constructType(valueType));
    18. }

    3.1.1 public JsonParser createParser(String content) throws IOException, JsonParseException

    • createParser:
      • 构造解析器以解析给定字符串内容的方法;
      • 将String生成一个Paser对象,用于后面的解析;
    • createParser源码:
    1. /**
    2. * Method for constructing parser for parsing
    3. * contents of given String.
    4. *
    5. * @since 2.1
    6. */
    7. public JsonParser createParser(String content) throws IOException, JsonParseException {
    8. final int strLen = content.length();
    9. // Actually, let's use this for medium-sized content, up to 64kB chunk (32kb char)
    10. if ((_inputDecorator != null) || (strLen > 0x8000) || !canUseCharArrays()) {
    11. // easier to just wrap in a Reader than extend InputDecorator; or, if content
    12. // is too long for us to copy it over
    13. return createParser(new StringReader(content));
    14. }
    15. IOContext ctxt = _createContext(content, true);
    16. char[] buf = ctxt.allocTokenBuffer(strLen);
    17. content.getChars(0, strLen, buf, 0);
    18. return _createParser(buf, 0, strLen, ctxt, true);
    19. }

    3.1.2 public JavaType constructType(Type type)

    • _typeFactory.constructType(valueType):
      • 通过传⼊的第⼆个参数,即Target类对象获取⼀个Type对象;
      • 简要流程:
        • 它⾸先⾸先会从缓存尝试获取该Class对应的Type,缓存中有这个数据的前提是,这个objectMapper前有对这个Class进⾏过序列化,之后会经过⼀系列的判断,这些判断包括判断它的Class是否属于某些特殊的Class(Map,Collection,AtomicReference)以及判断这个Class是否是Interface,这些判断在我上⾯的代码中都不成⽴,最终会通过 _newSimpleType ⽅法来创建⼀个Type对象。
        • 这个创建的Type记录了Class、superClass等信息;
    • constructType源码:
    1. public JavaType constructType(Type type) {
    2. return _fromAny(null, type, EMPTY_BINDINGS);
    3. }

    3.1.3 protected JavaType _fromAny(ClassStack context, Type type, TypeBindings bindings)

    • _fromAny
      • 如果类型信息作为从 getGenericXxx 方法返回的 Java 类型传递(通常用于返回或参数类型),则可以使用工厂方法;
      • 根据type的各种不同类型来获取相应的值;
    • _fromAny源码:
    1. /**
    2. * Factory method that can be used if type information is passed
    3. * as Java typing returned from getGenericXxx methods
    4. * (usually for a return or argument type).
    5. */
    6. protected JavaType _fromAny(ClassStack context, Type type, TypeBindings bindings)
    7. {
    8. JavaType resultType;
    9. // simple class?
    10. if (type instanceof Class) {
    11. // Important: remove possible bindings since this is type-erased thingy
    12. resultType = _fromClass(context, (Class) type, EMPTY_BINDINGS);
    13. }
    14. // But if not, need to start resolving.
    15. else if (type instanceof ParameterizedType) {
    16. resultType = _fromParamType(context, (ParameterizedType) type, bindings);
    17. }
    18. else if (type instanceof JavaType) { // [databind#116]
    19. // no need to modify further if we already had JavaType
    20. return (JavaType) type;
    21. }
    22. else if (type instanceof GenericArrayType) {
    23. resultType = _fromArrayType(context, (GenericArrayType) type, bindings);
    24. }
    25. else if (type instanceof TypeVariable) {
    26. resultType = _fromVariable(context, (TypeVariable) type, bindings);
    27. }
    28. else if (type instanceof WildcardType) {
    29. resultType = _fromWildcard(context, (WildcardType) type, bindings);
    30. } else {
    31. // sanity check
    32. throw new IllegalArgumentException("Unrecognized Type: "+((type == null) ? "[null]" : type.toString()));
    33. }
    34. // 21-Feb-2016, nateB/tatu: as per [databind#1129] (applied for 2.7.2),
    35. // we do need to let all kinds of types to be refined, esp. for Scala module.
    36. if (_modifiers != null) {
    37. TypeBindings b = resultType.getBindings();
    38. if (b == null) {
    39. b = EMPTY_BINDINGS;
    40. }
    41. for (TypeModifier mod : _modifiers) {
    42. JavaType t = mod.modifyType(resultType, type, b, this);
    43. if (t == null) {
    44. throw new IllegalStateException(String.format(
    45. "TypeModifier %s (of type %s) return null for type %s",
    46. mod, mod.getClass().getName(), resultType));
    47. }
    48. resultType = t;
    49. }
    50. }
    51. return resultType;
    52. }

    3.1.4 protected JavaType _fromClass(ClassStack context, Class rawType, TypeBindings bindings)

    • _fromClass
      • 参数:绑定——将形式参数声明(对于泛型类型)映射到实际类型;
      • 会经过⼀系列的判断,这些判断包括判断它的Class是否属于某些特殊的Class(Map,Collection,AtomicReference)以及判断这个Class是否是Interface,这些判断在我上⾯的代码中都不成⽴,最终会通过 _newSimpleType ⽅法来创建⼀个Type对象。
    • 这个创建的Type记录了Class、superClass等信息;
    • _fromClass源码:
    1. /**
    2. * @param bindings Mapping of formal parameter declarations (for generic
    3. * types) into actual types
    4. */
    5. protected JavaType _fromClass(ClassStack context, Class rawType, TypeBindings bindings)
    6. {
    7. // Very first thing: small set of core types we know well:
    8. JavaType result = _findWellKnownSimple(rawType);
    9. if (result != null) {
    10. return result;
    11. }
    12. // Barring that, we may have recently constructed an instance
    13. final Object key;
    14. if ((bindings == null) || bindings.isEmpty()) {
    15. key = rawType;
    16. } else {
    17. key = bindings.asKey(rawType);
    18. }
    19. result = _typeCache.get(key); // ok, cache object is synced
    20. if (result != null) {
    21. return result;
    22. }
    23. // 15-Oct-2015, tatu: recursive reference?
    24. if (context == null) {
    25. context = new ClassStack(rawType);
    26. } else {
    27. ClassStack prev = context.find(rawType);
    28. if (prev != null) {
    29. // Self-reference: needs special handling, then...
    30. ResolvedRecursiveType selfRef = new ResolvedRecursiveType(rawType, EMPTY_BINDINGS);
    31. prev.addSelfReference(selfRef);
    32. return selfRef;
    33. }
    34. // no, but need to update context to allow for proper cycle resolution
    35. context = context.child(rawType);
    36. }
    37. // First: do we have an array type?
    38. if (rawType.isArray()) {
    39. result = ArrayType.construct(_fromAny(context, rawType.getComponentType(), bindings),
    40. bindings);
    41. } else {
    42. // If not, need to proceed by first resolving parent type hierarchy
    43. JavaType superClass;
    44. JavaType[] superInterfaces;
    45. if (rawType.isInterface()) {
    46. superClass = null;
    47. superInterfaces = _resolveSuperInterfaces(context, rawType, bindings);
    48. } else {
    49. // Note: even Enums can implement interfaces, so cannot drop those
    50. superClass = _resolveSuperClass(context, rawType, bindings);
    51. superInterfaces = _resolveSuperInterfaces(context, rawType, bindings);
    52. }
    53. // 19-Oct-2015, tatu: Bit messy, but we need to 'fix' java.util.Properties here...
    54. if (rawType == Properties.class) {
    55. result = MapType.construct(rawType, bindings, superClass, superInterfaces,
    56. CORE_TYPE_STRING, CORE_TYPE_STRING);
    57. }
    58. // And then check what flavor of type we got. Start by asking resolved
    59. // super-type if refinement is all that is needed?
    60. else if (superClass != null) {
    61. result = superClass.refine(rawType, bindings, superClass, superInterfaces);
    62. }
    63. // if not, perhaps we are now resolving a well-known class or interface?
    64. if (result == null) {
    65. result = _fromWellKnownClass(context, rawType, bindings, superClass, superInterfaces);
    66. if (result == null) {
    67. result = _fromWellKnownInterface(context, rawType, bindings, superClass, superInterfaces);
    68. if (result == null) {
    69. // but if nothing else, "simple" class for now:
    70. result = _newSimpleType(rawType, bindings, superClass, superInterfaces);
    71. }
    72. }
    73. }
    74. }
    75. context.resolveSelfReferences(result);
    76. // 16-Jul-2016, tatu: [databind#1302] is solved different way, but ideally we shouldn't
    77. // cache anything with partially resolved `ResolvedRecursiveType`... so maybe improve
    78. if (!result.hasHandlers()) {
    79. _typeCache.putIfAbsent(key, result); // cache object syncs
    80. }
    81. return result;
    82. }

    3.2 protected Object _readMapAndClose(JsonParser p0, JavaType valueType)【重要方法】

    • _readMapAndClose:
      • 调用_initForReading方法,以确保给定的解析器已准备好读取数据绑定的内容;
      • 进入START_OBJECT的解析,标识开始解析还原一个对象,要还原一个对象要获取这个JSON串对应的Deserializer,最终调用Deserializer#deserilize还原对象:
    • _readMapAndClose源码:
    1. protected Object _readMapAndClose(JsonParser p0, JavaType valueType)
    2. throws IOException
    3. {
    4. try (JsonParser p = p0) {
    5. Object result;
    6. JsonToken t = _initForReading(p, valueType);
    7. final DeserializationConfig cfg = getDeserializationConfig();
    8. final DeserializationContext ctxt = createDeserializationContext(p, cfg);
    9. if (t == JsonToken.VALUE_NULL) {
    10. // Ask JsonDeserializer what 'null value' to use:
    11. result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt);
    12. } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
    13. result = null;
    14. } else {
    15. JsonDeserializer deser = _findRootDeserializer(ctxt, valueType);
    16. if (cfg.useRootWrapping()) {
    17. result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser);
    18. } else {
    19. result = deser.deserialize(p, ctxt);
    20. }
    21. ctxt.checkUnresolvedObjectId();
    22. }
    23. if (cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
    24. _verifyNoTrailingTokens(p, ctxt, valueType);
    25. }
    26. return result;
    27. }
    28. }
    29. 3.2.1 protected DefaultDeserializationContext createDeserializationContext(JsonParser p, DeserializationConfig cfg)

      • createDeserializationContext
        • 调用内部辅助方法以创建 DeserializationContext 的实例以反序列化单个根值。如果需要自定义上下文,可以覆盖。
        • 依据配置和json串对应的paser获取上下文context,这些信息包括下图,用于解析;
        • 可以看到 inputBuffer 中已经缓存了待反序列化的json串
      • createDeserializationContext源码:
      1. protected DefaultDeserializationContext createDeserializationContext(JsonParser p,
      2.         DeserializationConfig cfg) {
      3.     return _deserializationContext.createInstance(cfg, p, _injectableValues);
      4. }

      3.2.2 protected JsonDeserializer _findRootDeserializer(DeserializationContext ctxt, JavaType valueType)  throws JsonMappingException
      • _findRootDeserializer
        • 调用方法来定位传递的根级值的反序列化器。
        • 尝试从根节点去获取 反序列化器 ,类似于缓存的操作;
        • 因是第⼀次获取  反序列化器 ,所以从根节点取不到;
        • 然后进入 findRootValueDeserializer 获取 Deserializer;
      • _findRootDeserializer源码:
      1. /**
      2. * Method called to locate deserializer for the passed root-level value.
      3. */
      4. protected JsonDeserializer _findRootDeserializer(DeserializationContext ctxt,
      5. JavaType valueType)
      6. throws JsonMappingException
      7. {
      8. // First: have we already seen it?
      9. JsonDeserializer deser = _rootDeserializers.get(valueType);
      10. if (deser != null) {
      11. return deser;
      12. }
      13. // Nope: need to ask provider to resolve it
      14. deser = ctxt.findRootValueDeserializer(valueType);
      15. if (deser == null) { // can this happen?
      16. return ctxt.reportBadDefinition(valueType,
      17. "Cannot find a deserializer for type "+valueType);
      18. }
      19. _rootDeserializers.put(valueType, deser);
      20. return deser;
      21. }
      22. 3.2.3 public final JsonDeserializer findRootValueDeserializer(JavaType type) throws JsonMappingException
        • findRootValueDeserializer
          • 查找根级值的反序列化器的方法。
          • 该⽅法会从尝试从缓存中获取 Deserializer ,这⾥的获取⽅式有点不同了,它不是单纯的从个map中去调⽤get⽅法获取,⽽是会经过⼀系列很复杂的获取⽅式后,判断是否获取到了,如果获取不到,会调⽤ _createAndCacheValueDeserializer(该方法在findValueDeserializer中) 去创建⼀个 Deserializer 并对其进⾏缓存。最后会通过buildBeanDeserializer ⽅法创建⼀BeanDeserializer (因为前⾯的⼀系列判断都不满⾜,⽐如判断Type的类型,如判断是不是Enum,Container,Reference等)
        • 通过如下方法为创建好的 BeanDeserializer 对象赋值:
        • findRootValueDeserializer源码:
        1. /**
        2. * Method for finding a deserializer for root-level value.
        3. */
        4. @SuppressWarnings("unchecked")
        5. public final JsonDeserializer findRootValueDeserializer(JavaType type)
        6. throws JsonMappingException
        7. {
        8. JsonDeserializer deser = _cache.findValueDeserializer(this,
        9. _factory, type);
        10. if (deser == null) { // can this occur?
        11. return null;
        12. }
        13. deser = (JsonDeserializer) handleSecondaryContextualization(deser, null, type);
        14. TypeDeserializer typeDeser = _factory.findTypeDeserializer(_config, type);
        15. if (typeDeser != null) {
        16. // important: contextualize to indicate this is for root value
        17. typeDeser = typeDeser.forProperty(null);
        18. return new TypeWrappedDeserializer(typeDeser, deser);
        19. }
        20. return deser;
        21. }
          • findValueDeserializer源码:
          1. /**
          2. * Method called to get hold of a deserializer for a value of given type;
          3. * or if no such deserializer can be found, a default handler (which
          4. * may do a best-effort generic serialization or just simply
          5. * throw an exception when invoked).
          6. *

          7. * Note: this method is only called for value types; not for keys.
          8. * Key deserializers can be accessed using {@link #findKeyDeserializer}.
          9. *

          10. * Note also that deserializer returned is guaranteed to be resolved
          11. * (if it is of type {@link ResolvableDeserializer}), but
          12. * not contextualized (wrt {@link ContextualDeserializer}): caller
          13. * has to handle latter if necessary.
          14. *
          15. * @param ctxt Deserialization context
          16. * @param propertyType Declared type of the value to deserializer (obtained using
          17. * 'setter' method signature and/or type annotations
          18. *
          19. * @throws JsonMappingException if there are fatal problems with
          20. * accessing suitable deserializer; including that of not
          21. * finding any serializer
          22. */
          23. public JsonDeserializer findValueDeserializer(DeserializationContext ctxt,
          24. DeserializerFactory factory, JavaType propertyType)
          25. throws JsonMappingException
          26. {
          27. JsonDeserializer deser = _findCachedDeserializer(propertyType);
          28. if (deser == null) {
          29. // If not, need to request factory to construct (or recycle)
          30. deser = _createAndCacheValueDeserializer(ctxt, factory, propertyType);
          31. if (deser == null) {
          32. /* Should we let caller handle it? Let's have a helper method
          33. * decide it; can throw an exception, or return a valid
          34. * deserializer
          35. */
          36. deser = _handleUnknownValueDeserializer(ctxt, propertyType);
          37. }
          38. }
          39. return deser;
          40. }
            • buildBeanDeserializer源码:
            1. /**
            2. * Method that is to actually build a bean deserializer instance.
            3. * All basic sanity checks have been done to know that what we have
            4. * may be a valid bean type, and that there are no default simple
            5. * deserializers.
            6. */
            7. @SuppressWarnings("unchecked")
            8. public JsonDeserializer buildBeanDeserializer(DeserializationContext ctxt,
            9. JavaType type, BeanDescription beanDesc)
            10. throws JsonMappingException
            11. {
            12. // First: check what creators we can use, if any
            13. ValueInstantiator valueInstantiator;
            14. /* 04-Jun-2015, tatu: To work around [databind#636], need to catch the
            15. * issue, defer; this seems like a reasonable good place for now.
            16. * Note, however, that for non-Bean types (Collections, Maps) this
            17. * probably won't work and needs to be added elsewhere.
            18. */
            19. try {
            20. valueInstantiator = findValueInstantiator(ctxt, beanDesc);
            21. } catch (NoClassDefFoundError error) {
            22. return new ErrorThrowingDeserializer(error);
            23. } catch (IllegalArgumentException e) {
            24. // 05-Apr-2017, tatu: Although it might appear cleaner to require collector
            25. // to throw proper exception, it doesn't actually have reference to this
            26. // instance so...
            27. throw InvalidDefinitionException.from(ctxt.getParser(),
            28. ClassUtil.exceptionMessage(e),
            29. beanDesc, null);
            30. }
            31. BeanDeserializerBuilder builder = constructBeanDeserializerBuilder(ctxt, beanDesc);
            32. builder.setValueInstantiator(valueInstantiator);
            33. // And then setters for deserializing from JSON Object
            34. addBeanProps(ctxt, beanDesc, builder);
            35. addObjectIdReader(ctxt, beanDesc, builder);
            36. // managed/back reference fields/setters need special handling... first part
            37. addBackReferenceProperties(ctxt, beanDesc, builder);
            38. addInjectables(ctxt, beanDesc, builder);
            39. final DeserializationConfig config = ctxt.getConfig();
            40. if (_factoryConfig.hasDeserializerModifiers()) {
            41. for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
            42. builder = mod.updateBuilder(config, beanDesc, builder);
            43. }
            44. }
            45. JsonDeserializer deserializer;
            46. if (type.isAbstract() && !valueInstantiator.canInstantiate()) {
            47. deserializer = builder.buildAbstract();
            48. } else {
            49. deserializer = builder.build();
            50. }
            51. // may have modifier(s) that wants to modify or replace serializer we just built
            52. // (note that `resolve()` and `createContextual()` called later on)
            53. if (_factoryConfig.hasDeserializerModifiers()) {
            54. for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
            55. deserializer = mod.modifyDeserializer(config, beanDesc, deserializer);
            56. }
            57. }
            58. return (JsonDeserializer) deserializer;
            59. }
            60. 3.2.4 buildBeanDeserializer 中的 addBeanProps

              • 调用方法来确定 bean 反序列化器使用的可设置属性。
              • 注意:设计为可覆盖,并努力保持版本之间的界面相似。
              • 主要的逻辑在于解决字段与setter | getter | 反射的绑定,用于后面解析json串还原对象;
              • setter方法调用的条件:(反序列化)
                • getSetter()方法调用后能看到name、age两个字段;

              • getter方法调用的条件:(序列化)
              • addBeanProps源码:
              1. /**
              2. * Method called to figure out settable properties for the
              3. * bean deserializer to use.
              4. *

              5. * Note: designed to be overridable, and effort is made to keep interface
              6. * similar between versions.
              7. */
              8. protected void addBeanProps(DeserializationContext ctxt,
              9. BeanDescription beanDesc, BeanDeserializerBuilder builder)
              10. throws JsonMappingException
              11. {
              12. final boolean isConcrete = !beanDesc.getType().isAbstract();
              13. final SettableBeanProperty[] creatorProps = isConcrete
              14. ? builder.getValueInstantiator().getFromObjectArguments(ctxt.getConfig())
              15. : null;
              16. final boolean hasCreatorProps = (creatorProps != null);
              17. // 01-May-2016, tatu: Which base type to use here gets tricky, since
              18. // it may often make most sense to use general type for overrides,
              19. // but what we have here may be more specific impl type. But for now
              20. // just use it as is.
              21. JsonIgnoreProperties.Value ignorals = ctxt.getConfig()
              22. .getDefaultPropertyIgnorals(beanDesc.getBeanClass(),
              23. beanDesc.getClassInfo());
              24. Set ignored;
              25. if (ignorals != null) {
              26. boolean ignoreAny = ignorals.getIgnoreUnknown();
              27. builder.setIgnoreUnknownProperties(ignoreAny);
              28. // Or explicit/implicit definitions?
              29. ignored = ignorals.findIgnoredForDeserialization();
              30. for (String propName : ignored) {
              31. builder.addIgnorable(propName);
              32. }
              33. } else {
              34. ignored = Collections.emptySet();
              35. }
              36. // Also, do we have a fallback "any" setter?
              37. AnnotatedMember anySetter = beanDesc.findAnySetterAccessor();
              38. if (anySetter != null) {
              39. builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetter));
              40. } else {
              41. // 23-Jan-2018, tatu: although [databind#1805] would suggest we should block
              42. // properties regardless, for now only consider unless there's any setter...
              43. Collection ignored2 = beanDesc.getIgnoredPropertyNames();
              44. if (ignored2 != null) {
              45. for (String propName : ignored2) {
              46. // allow ignoral of similarly named JSON property, but do not force;
              47. // latter means NOT adding this to 'ignored':
              48. builder.addIgnorable(propName);
              49. }
              50. }
              51. }
              52. final boolean useGettersAsSetters = ctxt.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS)
              53. && ctxt.isEnabled(MapperFeature.AUTO_DETECT_GETTERS);
              54. // Ok: let's then filter out property definitions
              55. List propDefs = filterBeanProps(ctxt,
              56. beanDesc, builder, beanDesc.findProperties(), ignored);
              57. // After which we can let custom code change the set
              58. if (_factoryConfig.hasDeserializerModifiers()) {
              59. for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
              60. propDefs = mod.updateProperties(ctxt.getConfig(), beanDesc, propDefs);
              61. }
              62. }
              63. // At which point we still have all kinds of properties; not all with mutators:
              64. for (BeanPropertyDefinition propDef : propDefs) {
              65. SettableBeanProperty prop = null;
              66. // 18-Oct-2013, tatu: Although constructor parameters have highest precedence,
              67. // we need to do linkage (as per [databind#318]), and so need to start with
              68. // other types, and only then create constructor parameter, if any.
              69. if (propDef.hasSetter()) {
              70. AnnotatedMethod setter = propDef.getSetter();
              71. JavaType propertyType = setter.getParameterType(0);
              72. prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);
              73. } else if (propDef.hasField()) {
              74. AnnotatedField field = propDef.getField();
              75. JavaType propertyType = field.getType();
              76. prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);
              77. } else {
              78. // NOTE: specifically getter, since field was already checked above
              79. AnnotatedMethod getter = propDef.getGetter();
              80. if (getter != null) {
              81. if (useGettersAsSetters && _isSetterlessType(getter.getRawType())) {
              82. // 23-Jan-2018, tatu: As per [databind#1805], need to ensure we don't
              83. // accidentally sneak in getter-as-setter for `READ_ONLY` properties
              84. if (builder.hasIgnorable(propDef.getName())) {
              85. ;
              86. } else {
              87. prop = constructSetterlessProperty(ctxt, beanDesc, propDef);
              88. }
              89. } else if (!propDef.hasConstructorParameter()) {
              90. PropertyMetadata md = propDef.getMetadata();
              91. // 25-Oct-2016, tatu: If merging enabled, might not need setter.
              92. // We cannot quite support this with creator parameters; in theory
              93. // possibly, but right not not due to complexities of routing, so
              94. // just prevent
              95. if (md.getMergeInfo() != null) {
              96. prop = constructSetterlessProperty(ctxt, beanDesc, propDef);
              97. }
              98. }
              99. }
              100. }
              101. // 25-Sep-2014, tatu: No point in finding constructor parameters for abstract types
              102. // (since they are never used anyway)
              103. if (hasCreatorProps && propDef.hasConstructorParameter()) {
              104. /* If property is passed via constructor parameter, we must
              105. * handle things in special way. Not sure what is the most optimal way...
              106. * for now, let's just call a (new) method in builder, which does nothing.
              107. */
              108. // but let's call a method just to allow custom builders to be aware...
              109. final String name = propDef.getName();
              110. CreatorProperty cprop = null;
              111. if (creatorProps != null) {
              112. for (SettableBeanProperty cp : creatorProps) {
              113. if (name.equals(cp.getName()) && (cp instanceof CreatorProperty)) {
              114. cprop = (CreatorProperty) cp;
              115. break;
              116. }
              117. }
              118. }
              119. if (cprop == null) {
              120. List n = new ArrayList<>();
              121. for (SettableBeanProperty cp : creatorProps) {
              122. n.add(cp.getName());
              123. }
              124. ctxt.reportBadPropertyDefinition(beanDesc, propDef,
              125. "Could not find creator property with name '%s' (known Creator properties: %s)",
              126. name, n);
              127. continue;
              128. }
              129. if (prop != null) {
              130. cprop.setFallbackSetter(prop);
              131. }
              132. Class[] views = propDef.findViews();
              133. if (views == null) {
              134. views = beanDesc.findDefaultViews();
              135. }
              136. cprop.setViews(views);
              137. builder.addCreatorProperty(cprop);
              138. continue;
              139. }
              140. if (prop != null) {
              141. // one more thing before adding to builder: copy any metadata
              142. Class[] views = propDef.findViews();
              143. if (views == null) {
              144. views = beanDesc.findDefaultViews();
              145. }
              146. prop.setViews(views);
              147. builder.addProperty(prop);
              148. }
              149. }
              150. }

              3.2.5 public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException

              • 用来解析还原对象的Deserializer.deserialize 依据_hashArea的name 与 Method对应还原对象
              • deserialize:
                • 基于 bean 的对象 (POJO) 的主要反序列化方法。
              • deserialize源码: 
              1. /**
              2. * Main deserialization method for bean-based objects (POJOs).
              3. */
              4. @Override
              5. public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
              6. {
              7. // common case first
              8. if (p.isExpectedStartObjectToken()) {
              9. if (_vanillaProcessing) {
              10. return vanillaDeserialize(p, ctxt, p.nextToken());
              11. }
              12. // 23-Sep-2015, tatu: This is wrong at some many levels, but for now... it is
              13. // what it is, including "expected behavior".
              14. p.nextToken();
              15. if (_objectIdReader != null) {
              16. return deserializeWithObjectId(p, ctxt);
              17. }
              18. return deserializeFromObject(p, ctxt);
              19. }
              20. return _deserializeOther(p, ctxt, p.getCurrentToken());
              21. }

              3.2.6 com.fasterxml.jackson.databind.deser.BeanDeserializer#vanillaDeserialize

              • private final Object vanillaDeserialize(JsonParser p, DeserializationContext ctxt, JsonToken t) throws IOException
              • vanillaDeserialize:
                • final Object bean = _valueInstantiator.createUsingDefault(ctxt); //默认构造器初始化对象
                • SettableBeanProperty prop = _beanProperties.find(propName); //获取字段的设置方法
                • prop.deserializeAndSet(p, ctxt, bean); //调用初始化字段,调用每个properties对应的方法
                  • 字段的赋值在 SettableBeanProperty.deserializeAndSet(): 不同的 SettableBeanProperty 有不同的行为
              • vanillaDeserialize源码:
              1. /**
              2. * Streamlined version that is only used when no "special"
              3. * features are enabled.
              4. */
              5. private final Object vanillaDeserialize(JsonParser p,
              6. DeserializationContext ctxt, JsonToken t)
              7. throws IOException
              8. {
              9. final Object bean = _valueInstantiator.createUsingDefault(ctxt); //默认构造器初始化对象
              10. // [databind#631]: Assign current value, to be accessible by custom serializers
              11. p.setCurrentValue(bean);
              12. if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
              13. String propName = p.getCurrentName();
              14. do {
              15. p.nextToken();
              16. SettableBeanProperty prop = _beanProperties.find(propName); //获取字段的设置方法
              17. if (prop != null) { // normal case
              18. try {
              19. prop.deserializeAndSet(p, ctxt, bean); //调用初始化字段,调用每个properties对应的方法
              20. } catch (Exception e) {
              21. wrapAndThrow(e, bean, propName, ctxt);
              22. }
              23. continue;
              24. }
              25. handleUnknownVanilla(p, ctxt, bean, propName);
              26. } while ((propName = p.nextFieldName()) != null);
              27. }
              28. return bean;
              29. }

              无参构造存在的时候:

              • createUsingDefault源码:
              1.     @Override
              2.     public Object createUsingDefault(DeserializationContext ctxt) throws IOException
              3.     {
              4.         if (_defaultCreator == null) { // sanity-check; caller should check
              5.             return super.createUsingDefault(ctxt);
              6.         }
              7.         try {
              8.             return _defaultCreator.call();
              9.         } catch (Exception e) { // 19-Apr-2017, tatu: Let's not catch Errors, just Exceptions
              10.             return ctxt.handleInstantiationProblem(_valueClass, null, rewrapCtorProblem(ctxt, e));
              11.         }
              12.     }
              • call源码:调用构造器的newInstance()方法
              1.     @Override
              2.     public final Object call() throws Exception {
              3.         return _constructor.newInstance();
              4.     }
              • Constructor
                • newInstance()底层调用的是该类型的无参数构造方法;

              全参构造存在时:

              • createFromObjectWith源码:com.fasterxml.jackson.databind.deser.std.StdValueInstantiator#createFromObjectWith
              1.     @Override
              2.     public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) throws IOException
              3.     {
              4.         if (_withArgsCreator == null) { // sanity-check; caller should check
              5.             return super.createFromObjectWith(ctxt, args);
              6.         }
              7.         try {
              8.             return _withArgsCreator.call(args);
              9.         } catch (Exception e) { // 19-Apr-2017, tatu: Let's not catch Errors, just Exceptions
              10.             return ctxt.handleInstantiationProblem(_valueClass, args, rewrapCtorProblem(ctxt, e));
              11.         }
              12.     }
              • call源码:调用构造器的newInstance()方法
              1.     @Override
              2.     public final Object call(Object[] args) throws Exception {
              3.         return _constructor.newInstance(args);
              4.     }

              java.lang.reflect.Constructor#newInstance

              • newInstance
                • 使用此 Constructor 对象表示的构造函数,使用指定的初始化参数创建和初始化构造函数的声明类的新实例。各个参数会自动展开以匹配原始形式参数,并且原始参数和引用参数都根据需要进行方法调用转换。
                • 如果底层构造函数所需的形参数量为 0,则提供的 initargs 数组的长度可能为 0 或 null。
                • 如果构造函数的声明类是非静态上下文中的内部类,则构造函数的第一个参数需要是封闭实例;请参阅 Java™ 语言规范的第 15.9.3 节。
                • 如果所需的访问和参数检查成功并且实例化将继续,则如果尚未初始化构造函数的声明类,则将对其进行初始化。
                • 如果构造函数正常完成,则返回新创建和初始化的实例。
                • 参数:
                  • initargs – 作为参数传递给构造函数调用的对象数组;原始类型的值被包装在适当类型的包装对象中(例如,浮点数中的浮点数)
                • 返回值:
                  • 通过调用此对象表示的构造函数创建的新对象
                • 抛出异常:
                  • IllegalAccessException – 如果此 Constructor 对象正在强制执行 Java 语言访问控制并且底层构造函数不可访问。
                  • IllegalArgumentException – 如果实际参数和形式参数的数量不同;如果原始参数的展开转换失败;或者,如果在可能的展开之后,参数值不能通过方法调用转换转换为相应的形参类型;如果此构造函数属于枚举类型。
                  • InstantiationException – 如果声明底层构造函数的类表示一个抽象类。
                  • InvocationTargetException – 如果底层构造函数抛出异常。
                  • ExceptionInInitializerError – 如果此方法引发的初始化失败。
              • newInstance源码:
              1. /**
              2. * Uses the constructor represented by this {@code Constructor} object to
              3. * create and initialize a new instance of the constructor's
              4. * declaring class, with the specified initialization parameters.
              5. * Individual parameters are automatically unwrapped to match
              6. * primitive formal parameters, and both primitive and reference
              7. * parameters are subject to method invocation conversions as necessary.
              8. *
              9. *

                If the number of formal parameters required by the underlying constructor

              10. * is 0, the supplied {@code initargs} array may be of length 0 or null.
              11. *
              12. *

                If the constructor's declaring class is an inner class in a

              13. * non-static context, the first argument to the constructor needs
              14. * to be the enclosing instance; see section 15.9.3 of
              15. * The Java™ Language Specification.
              16. *
              17. *

                If the required access and argument checks succeed and the

              18. * instantiation will proceed, the constructor's declaring class
              19. * is initialized if it has not already been initialized.
              20. *
              21. *

                If the constructor completes normally, returns the newly

              22. * created and initialized instance.
              23. *
              24. * @param initargs array of objects to be passed as arguments to
              25. * the constructor call; values of primitive types are wrapped in
              26. * a wrapper object of the appropriate type (e.g. a {@code float}
              27. * in a {@link java.lang.Float Float})
              28. *
              29. * @return a new object created by calling the constructor
              30. * this object represents
              31. *
              32. * @exception IllegalAccessException if this {@code Constructor} object
              33. * is enforcing Java language access control and the underlying
              34. * constructor is inaccessible.
              35. * @exception IllegalArgumentException if the number of actual
              36. * and formal parameters differ; if an unwrapping
              37. * conversion for primitive arguments fails; or if,
              38. * after possible unwrapping, a parameter value
              39. * cannot be converted to the corresponding formal
              40. * parameter type by a method invocation conversion; if
              41. * this constructor pertains to an enum type.
              42. * @exception InstantiationException if the class that declares the
              43. * underlying constructor represents an abstract class.
              44. * @exception InvocationTargetException if the underlying constructor
              45. * throws an exception.
              46. * @exception ExceptionInInitializerError if the initialization provoked
              47. * by this method fails.
              48. */
              49. @CallerSensitive
              50. @ForceInline // to ensure Reflection.getCallerClass optimization
              51. public T newInstance(Object ... initargs)
              52. throws InstantiationException, IllegalAccessException,
              53. IllegalArgumentException, InvocationTargetException
              54. {
              55. if (!override) {
              56. Class caller = Reflection.getCallerClass();
              57. checkAccess(caller, clazz, clazz, modifiers);
              58. }
              59. if ((clazz.getModifiers() & Modifier.ENUM) != 0)
              60. throw new IllegalArgumentException("Cannot reflectively create enum objects");
              61. ConstructorAccessor ca = constructorAccessor; // read volatile
              62. if (ca == null) {
              63. ca = acquireConstructorAccessor();
              64. }
              65. @SuppressWarnings("unchecked")
              66. T inst = (T) ca.newInstance(initargs);
              67. return inst;
              68. }

              3.2.7 com.fasterxml.jackson.databind.deser.impl.MethodProperty#deserializeAndSet

              • public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object instance) throws IOException
              • deserializeAndSet:
                • 字段的赋值在 SettableBeanProperty.deserializeAndSet(): 不同的 SettableBeanProperty 有不同的行为;
                  • MethodProperty通过反射调用bean的setter方法;
                  • FieldProperty通过反射设置Bean的值;
              • deserializeAndSet源码:
              1. @Override
              2. public void deserializeAndSet(JsonParser p, DeserializationContext ctxt,
              3. Object instance) throws IOException
              4. {
              5. Object value;
              6. if (p.hasToken(JsonToken.VALUE_NULL)) {
              7. if (_skipNulls) {
              8. return;
              9. }
              10. value = _nullProvider.getNullValue(ctxt);
              11. } else if (_valueTypeDeserializer == null) {
              12. value = _valueDeserializer.deserialize(p, ctxt);
              13. // 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null
              14. if (value == null) {
              15. if (_skipNulls) {
              16. return;
              17. }
              18. value = _nullProvider.getNullValue(ctxt);
              19. }
              20. } else {
              21. value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
              22. }
              23. try {
              24. _setter.invoke(instance, value);
              25. } catch (Exception e) {
              26. _throwAsIOE(p, e, value);
              27. }
              28. }

               3.2.8 com.fasterxml.jackson.databind.deser.std.NumberDeserializers.IntegerDeserializer#deserialize

              1. @Override
              2. public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
              3. if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
              4. return p.getIntValue();
              5. }
              6. return _parseInteger(p, ctxt);
              7. }

               3.2.9 com.fasterxml.jackson.core.base.ParserBase#getIntValue

              1. @Override
              2. public int getIntValue() throws IOException
              3. {
              4. if ((_numTypesValid & NR_INT) == 0) {
              5. if (_numTypesValid == NR_UNKNOWN) { // not parsed at all
              6. return _parseIntValue();
              7. }
              8. if ((_numTypesValid & NR_INT) == 0) { // wasn't an int natively?
              9. convertNumberToInt(); // let's make it so, if possible
              10. }
              11. }
              12. return _numberInt;
              13. }

               3.2.10 java.lang.reflect.Method#invoke

              • public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
              • 这个方法一顿调用,就找到了自己写的set方法;

              总结

              • 序列化和反序列化,主要通过getXX获取字段值序列化 与 默认构造器+setXX反序列化还原对象。
                • 走无参构造的时候,参数赋值需要用到set、get方法
                • 如果是全参构造,则无需走set、gei方法
              • Jackson只支持对象的public属性或者有对象getter方法的属性序列化,即 protected,private,final,static 等属性将不会序列化到json串中,这是JavaBean的向后兼容使然。即Jackson在序列化的时候优先通过getXXX方法获取属性,如果没有则判断该属性是否public访问,是则通过反射获取该属性值。
              • Json的反序列化是不论是Fastjson还是Jackson,都限定了从某一个类的constructor默认参数实例化(如Fastjson的的autotype,Jackson在parseObject传入的Target.Class),通过getter or setter方法给成员对象赋值还原对象,利用的仅仅是这个Taget类与Target类的成员的setter or getter方法(这是一个递归的解析还原JSON串)。

              参考文献

              JavaSec Jackjson反序列化漏洞利用原理 | thonsun's blog

            61. 相关阅读:
              arraybuffer 转json
              619. 只出现一次的最大数字
              vivado 串行 I/O 硬件调试流程
              iOS17正式版BUG汇总:无法正常拨打电话、小组件不可用、无线充电不可用等问题
              LeetCode90. 子集 II
              title标签和meta标签怎样设置?有什么含义?
              python 抽象类
              postgresql并行查询(高级特性)
              MobaXterm配置ssh端口转发(tensorboard使用)
              双十一买led灯哪个品牌性价比高?2022适合学生使用的护眼灯推荐
            62. 原文地址:https://blog.csdn.net/jiayoudangdang/article/details/127813330