目录

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> T readValue(String content, Class<T> valueType)

  • readValue

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

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

  • createParser:

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

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源码:
    public JavaType constructType(Type type) {return _fromAny(null, type, EMPTY_BINDINGS);}

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

  • _fromAny

    • 如果类型信息作为从 getGenericXxx 方法返回的 Java 类型传递(通常用于返回或参数类型),则可以使用工厂方法;
    • 根据type的各种不同类型来获取相应的值;
  • _fromAny源码:
    /*** Factory method that can be used if type information is passed* as Java typing returned from <code>getGenericXxx</code> methods* (usually for a return or argument type).*/protected JavaType _fromAny(ClassStack context, Type type, TypeBindings bindings){JavaType resultType;// simple class?if (type instanceof Class<?>) {// Important: remove possible bindings since this is type-erased thingyresultType = _fromClass(context, (Class<?>) type, EMPTY_BINDINGS);}// But if not, need to start resolving.else if (type instanceof ParameterizedType) {resultType = _fromParamType(context, (ParameterizedType) type, bindings);}else if (type instanceof JavaType) { // [databind#116]// no need to modify further if we already had JavaTypereturn (JavaType) type;}else if (type instanceof GenericArrayType) {resultType = _fromArrayType(context, (GenericArrayType) type, bindings);}else if (type instanceof TypeVariable<?>) {resultType = _fromVariable(context, (TypeVariable<?>) type, bindings);}else if (type instanceof WildcardType) {resultType = _fromWildcard(context, (WildcardType) type, bindings);} else {// sanity checkthrow new IllegalArgumentException("Unrecognized Type: "+((type == null) ? "[null]" : type.toString()));}// 21-Feb-2016, nateB/tatu: as per [databind#1129] (applied for 2.7.2),//   we do need to let all kinds of types to be refined, esp. for Scala module.if (_modifiers != null) {TypeBindings b = resultType.getBindings();if (b == null) {b = EMPTY_BINDINGS;}for (TypeModifier mod : _modifiers) {JavaType t = mod.modifyType(resultType, type, b, this);if (t == null) {throw new IllegalStateException(String.format("TypeModifier %s (of type %s) return null for type %s",mod, mod.getClass().getName(), resultType));}resultType = t;}}return resultType;}

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源码:
    /*** @param bindings Mapping of formal parameter declarations (for generic*   types) into actual types*/protected JavaType _fromClass(ClassStack context, Class<?> rawType, TypeBindings bindings){// Very first thing: small set of core types we know well:JavaType result = _findWellKnownSimple(rawType);if (result != null) {return result;}// Barring that, we may have recently constructed an instancefinal Object key;if ((bindings == null) || bindings.isEmpty()) {key = rawType;} else {key = bindings.asKey(rawType);}result = _typeCache.get(key); // ok, cache object is syncedif (result != null) {return result;}// 15-Oct-2015, tatu: recursive reference?if (context == null) {context = new ClassStack(rawType);} else {ClassStack prev = context.find(rawType);if (prev != null) {// Self-reference: needs special handling, then...ResolvedRecursiveType selfRef = new ResolvedRecursiveType(rawType, EMPTY_BINDINGS);prev.addSelfReference(selfRef);return selfRef;}// no, but need to update context to allow for proper cycle resolutioncontext = context.child(rawType);}// First: do we have an array type?if (rawType.isArray()) {result = ArrayType.construct(_fromAny(context, rawType.getComponentType(), bindings),bindings);} else {// If not, need to proceed by first resolving parent type hierarchyJavaType superClass;JavaType[] superInterfaces;if (rawType.isInterface()) {superClass = null;superInterfaces = _resolveSuperInterfaces(context, rawType, bindings);} else {// Note: even Enums can implement interfaces, so cannot drop thosesuperClass = _resolveSuperClass(context, rawType, bindings);superInterfaces = _resolveSuperInterfaces(context, rawType, bindings);}// 19-Oct-2015, tatu: Bit messy, but we need to 'fix' java.util.Properties here...if (rawType == Properties.class) {result = MapType.construct(rawType, bindings, superClass, superInterfaces,CORE_TYPE_STRING, CORE_TYPE_STRING);}// And then check what flavor of type we got. Start by asking resolved// super-type if refinement is all that is needed?else if (superClass != null) {result = superClass.refine(rawType, bindings, superClass, superInterfaces);}// if not, perhaps we are now resolving a well-known class or interface?if (result == null) {result = _fromWellKnownClass(context, rawType, bindings, superClass, superInterfaces); if (result == null) {result = _fromWellKnownInterface(context, rawType, bindings, superClass, superInterfaces);if (result == null) {// but if nothing else, "simple" class for now:result = _newSimpleType(rawType, bindings, superClass, superInterfaces);}}}}context.resolveSelfReferences(result);// 16-Jul-2016, tatu: [databind#1302] is solved different way, but ideally we shouldn't//     cache anything with partially resolved `ResolvedRecursiveType`... so maybe improveif (!result.hasHandlers()) {_typeCache.putIfAbsent(key, result); // cache object syncs}return result;}

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

  • _readMapAndClose:

    • 调用_initForReading方法,以确保给定的解析器已准备好读取数据绑定的内容;

    • 进入START_OBJECT的解析,标识开始解析还原一个对象,要还原一个对象要获取这个JSON串对应的Deserializer,最终调用Deserializer#deserilize还原对象:
  • _readMapAndClose源码:
    protected Object _readMapAndClose(JsonParser p0, JavaType valueType)throws IOException{try (JsonParser p = p0) {Object result;JsonToken t = _initForReading(p, valueType);final DeserializationConfig cfg = getDeserializationConfig();final DeserializationContext ctxt = createDeserializationContext(p, cfg);if (t == JsonToken.VALUE_NULL) {// Ask JsonDeserializer what 'null value' to use:result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt);} else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {result = null;} else {JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);if (cfg.useRootWrapping()) {result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser);} else {result = deser.deserialize(p, ctxt);}ctxt.checkUnresolvedObjectId();}if (cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {_verifyNoTrailingTokens(p, ctxt, valueType);}return result;}}

3.2.1 protected DefaultDeserializationContext createDeserializationContext(JsonParser p, DeserializationConfig cfg)

  • createDeserializationContext

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

3.2.2 protected JsonDeserializer<Object> _findRootDeserializer(DeserializationContext ctxt, JavaType valueType)  throws JsonMappingException

  • _findRootDeserializer

    • 调用方法来定位传递的根级值的反序列化器。
    • 尝试从根节点去获取 反序列化器 ,类似于缓存的操作;
    • 因是第⼀次获取  反序列化器 ,所以从根节点取不到;
    • 然后进入 findRootValueDeserializer 获取 Deserializer;
  • _findRootDeserializer源码:
    /*** Method called to locate deserializer for the passed root-level value.*/protected JsonDeserializer<Object> _findRootDeserializer(DeserializationContext ctxt,JavaType valueType)throws JsonMappingException{// First: have we already seen it?JsonDeserializer<Object> deser = _rootDeserializers.get(valueType);if (deser != null) {return deser;}// Nope: need to ask provider to resolve itdeser = ctxt.findRootValueDeserializer(valueType);if (deser == null) { // can this happen?return ctxt.reportBadDefinition(valueType,"Cannot find a deserializer for type "+valueType);}_rootDeserializers.put(valueType, deser);return deser;}

3.2.3 public final JsonDeserializer<Object> findRootValueDeserializer(JavaType type) throws JsonMappingException

  • findRootValueDeserializer

    • 查找根级值的反序列化器的方法。
    • 该⽅法会从尝试从缓存中获取 Deserializer ,这⾥的获取⽅式有点不同了,它不是单纯的从个map中去调⽤get⽅法获取,⽽是会经过⼀系列很复杂的获取⽅式后,判断是否获取到了,如果获取不到,会调⽤ _createAndCacheValueDeserializer(该方法在findValueDeserializer中) 去创建⼀个 Deserializer 并对其进⾏缓存。最后会通过buildBeanDeserializer ⽅法创建⼀BeanDeserializer (因为前⾯的⼀系列判断都不满⾜,⽐如判断Type的类型,如判断是不是Enum,Container,Reference等)
  • 通过如下方法为创建好的 BeanDeserializer 对象赋值:
  • findRootValueDeserializer源码:
    /*** Method for finding a deserializer for root-level value.*/@SuppressWarnings("unchecked")public final JsonDeserializer<Object> findRootValueDeserializer(JavaType type)throws JsonMappingException{JsonDeserializer<Object> deser = _cache.findValueDeserializer(this,_factory, type);if (deser == null) { // can this occur?return null;}deser = (JsonDeserializer<Object>) handleSecondaryContextualization(deser, null, type);TypeDeserializer typeDeser = _factory.findTypeDeserializer(_config, type);if (typeDeser != null) {// important: contextualize to indicate this is for root valuetypeDeser = typeDeser.forProperty(null);return new TypeWrappedDeserializer(typeDeser, deser);}return deser;}
  • findValueDeserializer源码:
    /*** Method called to get hold of a deserializer for a value of given type;* or if no such deserializer can be found, a default handler (which* may do a best-effort generic serialization or just simply* throw an exception when invoked).*<p>* Note: this method is only called for value types; not for keys.* Key deserializers can be accessed using {@link #findKeyDeserializer}.*<p>* Note also that deserializer returned is guaranteed to be resolved* (if it is of type {@link ResolvableDeserializer}), but* not contextualized (wrt {@link ContextualDeserializer}): caller* has to handle latter if necessary.** @param ctxt Deserialization context* @param propertyType Declared type of the value to deserializer (obtained using*   'setter' method signature and/or type annotations** @throws JsonMappingException if there are fatal problems with*   accessing suitable deserializer; including that of not*   finding any serializer*/public JsonDeserializer<Object> findValueDeserializer(DeserializationContext ctxt,DeserializerFactory factory, JavaType propertyType)throws JsonMappingException{JsonDeserializer<Object> deser = _findCachedDeserializer(propertyType);if (deser == null) {// If not, need to request factory to construct (or recycle)deser = _createAndCacheValueDeserializer(ctxt, factory, propertyType);if (deser == null) {/* Should we let caller handle it? Let's have a helper method* decide it; can throw an exception, or return a valid* deserializer*/deser = _handleUnknownValueDeserializer(ctxt, propertyType);}}return deser;}
  • buildBeanDeserializer源码:
    /*** Method that is to actually build a bean deserializer instance.* All basic sanity checks have been done to know that what we have* may be a valid bean type, and that there are no default simple* deserializers.*/@SuppressWarnings("unchecked")public JsonDeserializer<Object> buildBeanDeserializer(DeserializationContext ctxt,JavaType type, BeanDescription beanDesc)throws JsonMappingException{// First: check what creators we can use, if anyValueInstantiator valueInstantiator;/* 04-Jun-2015, tatu: To work around [databind#636], need to catch the*    issue, defer; this seems like a reasonable good place for now.*   Note, however, that for non-Bean types (Collections, Maps) this*   probably won't work and needs to be added elsewhere.*/try {valueInstantiator = findValueInstantiator(ctxt, beanDesc);} catch (NoClassDefFoundError error) {return new ErrorThrowingDeserializer(error);} catch (IllegalArgumentException e) {// 05-Apr-2017, tatu: Although it might appear cleaner to require collector//   to throw proper exception, it doesn't actually have reference to this//   instance so...throw InvalidDefinitionException.from(ctxt.getParser(),ClassUtil.exceptionMessage(e),beanDesc, null);}BeanDeserializerBuilder builder = constructBeanDeserializerBuilder(ctxt, beanDesc);builder.setValueInstantiator(valueInstantiator);// And then setters for deserializing from JSON ObjectaddBeanProps(ctxt, beanDesc, builder);addObjectIdReader(ctxt, beanDesc, builder);// managed/back reference fields/setters need special handling... first partaddBackReferenceProperties(ctxt, beanDesc, builder);addInjectables(ctxt, beanDesc, builder);final DeserializationConfig config = ctxt.getConfig();if (_factoryConfig.hasDeserializerModifiers()) {for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {builder = mod.updateBuilder(config, beanDesc, builder);}}JsonDeserializer<?> deserializer;if (type.isAbstract() && !valueInstantiator.canInstantiate()) {deserializer = builder.buildAbstract();} else {deserializer = builder.build();}// may have modifier(s) that wants to modify or replace serializer we just built// (note that `resolve()` and `createContextual()` called later on)if (_factoryConfig.hasDeserializerModifiers()) {for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {deserializer = mod.modifyDeserializer(config, beanDesc, deserializer);}}return (JsonDeserializer<Object>) deserializer;}

3.2.4 buildBeanDeserializer 中的 addBeanProps

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

  • getter方法调用的条件:(序列化)

  • addBeanProps源码:
    /*** Method called to figure out settable properties for the* bean deserializer to use.*<p>* Note: designed to be overridable, and effort is made to keep interface* similar between versions.*/protected void addBeanProps(DeserializationContext ctxt,BeanDescription beanDesc, BeanDeserializerBuilder builder)throws JsonMappingException{final boolean isConcrete = !beanDesc.getType().isAbstract();final SettableBeanProperty[] creatorProps = isConcrete? builder.getValueInstantiator().getFromObjectArguments(ctxt.getConfig()): null;final boolean hasCreatorProps = (creatorProps != null);// 01-May-2016, tatu: Which base type to use here gets tricky, since//   it may often make most sense to use general type for overrides,//   but what we have here may be more specific impl type. But for now//   just use it as is.JsonIgnoreProperties.Value ignorals = ctxt.getConfig().getDefaultPropertyIgnorals(beanDesc.getBeanClass(),beanDesc.getClassInfo());Set<String> ignored;if (ignorals != null) {boolean ignoreAny = ignorals.getIgnoreUnknown();builder.setIgnoreUnknownProperties(ignoreAny);// Or explicit/implicit definitions?ignored = ignorals.findIgnoredForDeserialization();for (String propName : ignored) {builder.addIgnorable(propName);}} else {ignored = Collections.emptySet();}// Also, do we have a fallback "any" setter?AnnotatedMember anySetter = beanDesc.findAnySetterAccessor();if (anySetter != null) {builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetter));} else {// 23-Jan-2018, tatu: although [databind#1805] would suggest we should block//   properties regardless, for now only consider unless there's any setter...Collection<String> ignored2 = beanDesc.getIgnoredPropertyNames();if (ignored2 != null) {for (String propName : ignored2) {// allow ignoral of similarly named JSON property, but do not force;// latter means NOT adding this to 'ignored':builder.addIgnorable(propName);}}}final boolean useGettersAsSetters = ctxt.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS)&& ctxt.isEnabled(MapperFeature.AUTO_DETECT_GETTERS);// Ok: let's then filter out property definitionsList<BeanPropertyDefinition> propDefs = filterBeanProps(ctxt,beanDesc, builder, beanDesc.findProperties(), ignored);// After which we can let custom code change the setif (_factoryConfig.hasDeserializerModifiers()) {for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {propDefs = mod.updateProperties(ctxt.getConfig(), beanDesc, propDefs);}}// At which point we still have all kinds of properties; not all with mutators:for (BeanPropertyDefinition propDef : propDefs) {SettableBeanProperty prop = null;// 18-Oct-2013, tatu: Although constructor parameters have highest precedence,//   we need to do linkage (as per [databind#318]), and so need to start with//   other types, and only then create constructor parameter, if any.if (propDef.hasSetter()) {AnnotatedMethod setter = propDef.getSetter();JavaType propertyType = setter.getParameterType(0);prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);} else if (propDef.hasField()) {AnnotatedField field = propDef.getField();JavaType propertyType = field.getType();prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);} else {// NOTE: specifically getter, since field was already checked aboveAnnotatedMethod getter = propDef.getGetter();if (getter != null) {if (useGettersAsSetters && _isSetterlessType(getter.getRawType())) {// 23-Jan-2018, tatu: As per [databind#1805], need to ensure we don't//   accidentally sneak in getter-as-setter for `READ_ONLY` propertiesif (builder.hasIgnorable(propDef.getName())) {;} else {prop = constructSetterlessProperty(ctxt, beanDesc, propDef);}} else if (!propDef.hasConstructorParameter()) {PropertyMetadata md = propDef.getMetadata();// 25-Oct-2016, tatu: If merging enabled, might not need setter.//   We cannot quite support this with creator parameters; in theory//   possibly, but right not not due to complexities of routing, so//   just preventif (md.getMergeInfo() != null) {prop = constructSetterlessProperty(ctxt, beanDesc, propDef);}}}}// 25-Sep-2014, tatu: No point in finding constructor parameters for abstract types//   (since they are never used anyway)if (hasCreatorProps && propDef.hasConstructorParameter()) {/* If property is passed via constructor parameter, we must* handle things in special way. Not sure what is the most optimal way...* for now, let's just call a (new) method in builder, which does nothing.*/// but let's call a method just to allow custom builders to be aware...final String name = propDef.getName();CreatorProperty cprop = null;if (creatorProps != null) {for (SettableBeanProperty cp : creatorProps) {if (name.equals(cp.getName()) && (cp instanceof CreatorProperty)) {cprop = (CreatorProperty) cp;break;}}}if (cprop == null) {List<String> n = new ArrayList<>();for (SettableBeanProperty cp : creatorProps) {n.add(cp.getName());}ctxt.reportBadPropertyDefinition(beanDesc, propDef,"Could not find creator property with name '%s' (known Creator properties: %s)",name, n);continue;}if (prop != null) {cprop.setFallbackSetter(prop);}Class<?>[] views = propDef.findViews();if (views == null) {views = beanDesc.findDefaultViews();}cprop.setViews(views);builder.addCreatorProperty(cprop);continue;}if (prop != null) {// one more thing before adding to builder: copy any metadataClass<?>[] views = propDef.findViews();if (views == null) {views = beanDesc.findDefaultViews();}prop.setViews(views);builder.addProperty(prop);}}}

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

  • 用来解析还原对象的Deserializer.deserialize 依据_hashArea的name 与 Method对应还原对象

  • deserialize:
    • 基于 bean 的对象 (POJO) 的主要反序列化方法。
  • deserialize源码:
    /*** Main deserialization method for bean-based objects (POJOs).*/@Overridepublic Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException{// common case firstif (p.isExpectedStartObjectToken()) {if (_vanillaProcessing) {return vanillaDeserialize(p, ctxt, p.nextToken());}// 23-Sep-2015, tatu: This is wrong at some many levels, but for now... it is//    what it is, including "expected behavior".p.nextToken();if (_objectIdReader != null) {return deserializeWithObjectId(p, ctxt);}return deserializeFromObject(p, ctxt);}return _deserializeOther(p, ctxt, p.getCurrentToken());}

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源码:
     /*** Streamlined version that is only used when no "special"* features are enabled.*/private final Object vanillaDeserialize(JsonParser p,DeserializationContext ctxt, JsonToken t)throws IOException{final Object bean = _valueInstantiator.createUsingDefault(ctxt); //默认构造器初始化对象// [databind#631]: Assign current value, to be accessible by custom serializersp.setCurrentValue(bean);if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {String propName = p.getCurrentName();do {p.nextToken();SettableBeanProperty prop = _beanProperties.find(propName); //获取字段的设置方法if (prop != null) { // normal casetry {prop.deserializeAndSet(p, ctxt, bean); //调用初始化字段,调用每个properties对应的方法} catch (Exception e) {wrapAndThrow(e, bean, propName, ctxt);}continue;}handleUnknownVanilla(p, ctxt, bean, propName);} while ((propName = p.nextFieldName()) != null);}return bean;}

无参构造存在的时候:

  • createUsingDefault源码:
    @Overridepublic Object createUsingDefault(DeserializationContext ctxt) throws IOException{if (_defaultCreator == null) { // sanity-check; caller should checkreturn super.createUsingDefault(ctxt);}try {return _defaultCreator.call();} catch (Exception e) { // 19-Apr-2017, tatu: Let's not catch Errors, just Exceptionsreturn ctxt.handleInstantiationProblem(_valueClass, null, rewrapCtorProblem(ctxt, e));}}
  • call源码:调用构造器的newInstance()方法
    @Overridepublic final Object call() throws Exception {return _constructor.newInstance();}
  • Constructor

    • newInstance()底层调用的是该类型的无参数构造方法;

全参构造存在时:

  • createFromObjectWith源码:com.fasterxml.jackson.databind.deser.std.StdValueInstantiator#createFromObjectWith
    @Overridepublic Object createFromObjectWith(DeserializationContext ctxt, Object[] args) throws IOException{if (_withArgsCreator == null) { // sanity-check; caller should checkreturn super.createFromObjectWith(ctxt, args);}try {return _withArgsCreator.call(args);} catch (Exception e) { // 19-Apr-2017, tatu: Let's not catch Errors, just Exceptionsreturn ctxt.handleInstantiationProblem(_valueClass, args, rewrapCtorProblem(ctxt, e));}}
  • call源码:调用构造器的newInstance()方法
    @Overridepublic final Object call(Object[] args) throws Exception {return _constructor.newInstance(args);}

java.lang.reflect.Constructor#newInstance

  • newInstance

    • 使用此 Constructor 对象表示的构造函数,使用指定的初始化参数创建和初始化构造函数的声明类的新实例。各个参数会自动展开以匹配原始形式参数,并且原始参数和引用参数都根据需要进行方法调用转换。
    • 如果底层构造函数所需的形参数量为 0,则提供的 initargs 数组的长度可能为 0 或 null。
    • 如果构造函数的声明类是非静态上下文中的内部类,则构造函数的第一个参数需要是封闭实例;请参阅 Java™ 语言规范的第 15.9.3 节。
    • 如果所需的访问和参数检查成功并且实例化将继续,则如果尚未初始化构造函数的声明类,则将对其进行初始化。
    • 如果构造函数正常完成,则返回新创建和初始化的实例。
    • 参数:
      • initargs – 作为参数传递给构造函数调用的对象数组;原始类型的值被包装在适当类型的包装对象中(例如,浮点数中的浮点数)
    • 返回值:
      • 通过调用此对象表示的构造函数创建的新对象
    • 抛出异常:
      • IllegalAccessException – 如果此 Constructor 对象正在强制执行 Java 语言访问控制并且底层构造函数不可访问。
      • IllegalArgumentException – 如果实际参数和形式参数的数量不同;如果原始参数的展开转换失败;或者,如果在可能的展开之后,参数值不能通过方法调用转换转换为相应的形参类型;如果此构造函数属于枚举类型。
      • InstantiationException – 如果声明底层构造函数的类表示一个抽象类。
      • InvocationTargetException – 如果底层构造函数抛出异常。
      • ExceptionInInitializerError – 如果此方法引发的初始化失败。
  • newInstance源码:
     /*** Uses the constructor represented by this {@code Constructor} object to* create and initialize a new instance of the constructor's* declaring class, with the specified initialization parameters.* Individual parameters are automatically unwrapped to match* primitive formal parameters, and both primitive and reference* parameters are subject to method invocation conversions as necessary.** <p>If the number of formal parameters required by the underlying constructor* is 0, the supplied {@code initargs} array may be of length 0 or null.** <p>If the constructor's declaring class is an inner class in a* non-static context, the first argument to the constructor needs* to be the enclosing instance; see section 15.9.3 of* <cite>The Java&trade; Language Specification</cite>.** <p>If the required access and argument checks succeed and the* instantiation will proceed, the constructor's declaring class* is initialized if it has not already been initialized.** <p>If the constructor completes normally, returns the newly* created and initialized instance.** @param initargs array of objects to be passed as arguments to* the constructor call; values of primitive types are wrapped in* a wrapper object of the appropriate type (e.g. a {@code float}* in a {@link java.lang.Float Float})** @return a new object created by calling the constructor* this object represents** @exception IllegalAccessException    if this {@code Constructor} object*              is enforcing Java language access control and the underlying*              constructor is inaccessible.* @exception IllegalArgumentException  if the number of actual*              and formal parameters differ; if an unwrapping*              conversion for primitive arguments fails; or if,*              after possible unwrapping, a parameter value*              cannot be converted to the corresponding formal*              parameter type by a method invocation conversion; if*              this constructor pertains to an enum type.* @exception InstantiationException    if the class that declares the*              underlying constructor represents an abstract class.* @exception InvocationTargetException if the underlying constructor*              throws an exception.* @exception ExceptionInInitializerError if the initialization provoked*              by this method fails.*/@CallerSensitive@ForceInline // to ensure Reflection.getCallerClass optimizationpublic T newInstance(Object ... initargs)throws InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException{if (!override) {Class<?> caller = Reflection.getCallerClass();checkAccess(caller, clazz, clazz, modifiers);}if ((clazz.getModifiers() & Modifier.ENUM) != 0)throw new IllegalArgumentException("Cannot reflectively create enum objects");ConstructorAccessor ca = constructorAccessor;   // read volatileif (ca == null) {ca = acquireConstructorAccessor();}@SuppressWarnings("unchecked")T inst = (T) ca.newInstance(initargs);return inst;}

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源码:
    @Overridepublic void deserializeAndSet(JsonParser p, DeserializationContext ctxt,Object instance) throws IOException{Object value;if (p.hasToken(JsonToken.VALUE_NULL)) {if (_skipNulls) {return;}value = _nullProvider.getNullValue(ctxt);} else if (_valueTypeDeserializer == null) {value = _valueDeserializer.deserialize(p, ctxt);// 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give nullif (value == null) {if (_skipNulls) {return;}value = _nullProvider.getNullValue(ctxt);}} else {value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);}try {_setter.invoke(instance, value);} catch (Exception e) {_throwAsIOE(p, e, value);}}

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

        @Overridepublic Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {return p.getIntValue();}return _parseInteger(p, ctxt);}

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

    @Overridepublic int getIntValue() throws IOException{if ((_numTypesValid & NR_INT) == 0) {if (_numTypesValid == NR_UNKNOWN) { // not parsed at allreturn _parseIntValue();}if ((_numTypesValid & NR_INT) == 0) { // wasn't an int natively?convertNumberToInt(); // let's make it so, if possible}}return _numberInt;}

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

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

  1. hutol json null值没了_JSON数据处理框架Jackson精解第一篇-序列化与反序列化核心用法...

    Jackson是Spring Boot默认的JSON数据处理框架,但是其并不依赖于任何的Spring 库.有的小伙伴以为Jackson只能在Spring框架内使用,其实不是的,没有这种限制.它提供了很 ...

  2. python序列化和反序列化ppt_老生常谈Python序列化和反序列化

    通过将对象序列化可以将其存储在变量或者文件中,可以保存当时对象的状态,实现其生命周期的延长.并且需要时可以再次将这个对象读取出来.Python中有几个常用模块可实现这一功能. pickle模块 存储在 ...

  3. serializable java 规则_Java 序列化Serializable详解(附详细例子)

    1.什么是序列化和反序列化 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程. 2.什么情况下需要序 ...

  4. java 不能反序列化_java中的序列化与反序列化

    序列化就是将一个对象以及他的属性写入一个文件,保存在存中对象的状态 反序列化就是把保存的对象状态再读出来 实现序列化与反序列化就要使用到IO操作中文件的字节流输入与输出 inputStream与out ...

  5. 【反序列化漏洞01】序列化与反序列化简介

    目录 1 背景 2 定义 3 作用及优点 4 例子:测试php代码中序列化与反序列化执行过程 4.1 测试环境 4.2 测试序列化过程 4.3 测试反序列化过程 5 例子:SessionID在php中 ...

  6. SpringBoot的序列化和反序列化

    序列化与反序列化 1.认识序列化与反序列化 Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程. 2.为什么要实现对象的序列化和反序列化? ...

  7. Java 中序列化与反序列化

    一. 序列化和反序列化概念 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程.将程序中的对象,放入文 ...

  8. 十三、序列化和反序列化(部分转载)

    json和pickle序列化和反序列化 json是用来实现不同程序之间的文件交互,由于不同程序之间需要进行文件信息交互,由于用python写的代码可能要与其他语言写的代码进行数据传输,json支持所有 ...

  9. Java基础-序列化与反序列化

    序列化和反序列化在面试中也经常考查,下面就总结一下 Java 中的序列化和反序列化. 什么是序列化和反序列化? 序列化是将 Java 对象转换成与平台无关的二进制流,而反序列化则是将二进制流恢复成原来 ...

最新文章

  1. [转]GetProcAddress函数
  2. html属性选择器怎么写,html – 具有“type”属性与make-up属性的CSS属性选择器和区分大小写...
  3. 1.1节 Buck Converter--降压转换器 part1
  4. 线上报名 | 高性能深度学习推理引擎 TensorRT 实战编程讲解
  5. 组播IP地址到底是谁的IP?
  6. 最新完整版PHP配置文件翻译
  7. 字节前端终于开源!吹爆!
  8. 什么时候使用resulttype_ResultMap和ResultType在使用中的区别
  9. 嵌套 思维导图_看我怎么用思维导图,来轻松学习JavaScript,值得收藏
  10. 音乐在线播放Demo
  11. 017 打渔晒网问题
  12. python基础语法全解/数据类型/面向对象编程
  13. 小屏幕android电视,手机屏幕还能投屏到电视?教你4种方法,1秒钟小屏变大屏
  14. EasyPoi word导出教程
  15. bootstrap-select下拉选项数据超长换行显示
  16. Android系统消息推送
  17. 兼容IE的excel下载
  18. 自制WIFI放大器(信号真的有增强)
  19. 把一个学生的信息(包括学号、姓名、性别、住址)放在一个结构体变量中,然后输出这个学生的信息
  20. 电子合同实现招采文件电子化签署,高效采购

热门文章

  1. Android实现一维二维码扫描生成功能(一)-zxing导入现有项目
  2. 图像增强评价指标学习之——结构相似性SSIM
  3. 基于Android的校园跳蚤市场的设计与实现
  4. 使用Python实现一个七牛GUI小程序客户端
  5. 驾驶员嗜睡分类 - 深度学习
  6. matlab下载99时,matlab permute
  7. python爬取路况信息_高德地图api获取路况信息(python版)
  8. PHP脉聊交友系统网站源码 可通过功能+广告变现 社交在线聊天交友即时通讯【APP源码+视频教程】
  9. 怎么给c语言程序加个个密码,C语言对文件加上密码
  10. springboot:根据不同配置注入不同实现的bean