#JAVA #FastJSON #Jackson #源码分析
# 起因
刚开始是有一个业务需求,需要判断用户手机号是否存在,返回不同的属性,我寻思着就在实体类里写个 getUserPhone 之类的方法,并且为了防止和原本的 getPhone 冲突,特地命名成不一样的名字,但是没想到还是被调用了,虽然猜到这俩 json 库大致是获取了所有的 get 和 set 前缀的方法,然后再进行什么操作,不过还是想知道为什么有这种需求,于是调试下源码记录下。
# 找入口点
业务代码出问题的地方是我进行了一次 JSON.toJSONString () 操作,导致触发了 get 方法,不过我调试过程中发现 FastJSON 获取的 Serializer 是 ASMSerializer,
public final void write(Object object) { | |
if (object == null) { | |
out.writeNull(); | |
return; } | |
Class<?> clazz = object.getClass(); | |
ObjectSerializer writer = getObjectWriter(clazz); // ASMSerializer | |
try { | |
writer.write(this, object, null, null, 0); | |
} catch (IOException e) { | |
throw new JSONException(e.getMessage(), e); | |
} | |
} |
并且这个类的 write 方法我跟不进去,所以只好换成 jackson。找入口点的方式很简单,既然已经知道会调用 get 方法,那就只需要在 get 方法处打一个断点即可。值得一提的是,这个方法必须要有返回值,否则的话会序列化时就不会去调用这个方法。
触发断点之后,只需要去看调用堆栈,一步一步往上找就可以了。
可以看到,invoke 就是通过反射去调用 get 方法了,而上一层的 serializeAsField 自然是已经获取到 get 方法了。
/** | |
* Method called to access property that this bean stands for, from within * given bean, and to serialize it as a JSON Object field using appropriate * serializer. */@Override | |
public void serializeAsField(Object bean, JsonGenerator gen, | |
SerializerProvider prov) throws Exception { | |
// inlined 'get()' | |
final Object value = (_accessorMethod == null) ? _field.get(bean) | |
: _accessorMethod.invoke(bean, (Object[]) null); | |
//...... 省略以下代码 | |
} |
在这里, _accessorMethod
也可以通过 idea 看到内容是什么:
那么我们就需要找到 _accessorMethod
是如何被设置的,这里需要在字段上面设置一个断点,类型为监视修改。
经过调试之后找到了相关代码:
/** | |
* @since 2.9 (added `includeInViews` since 2.8) | |
*/@SuppressWarnings("unchecked") | |
public BeanPropertyWriter(BeanPropertyDefinition propDef, | |
AnnotatedMember member, Annotations contextAnnotations, | |
JavaType declaredType, | |
JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType, | |
boolean suppressNulls, Object suppressableValue, | |
Class<?>[] includeInViews) | |
{ | |
// ...... | |
if (member instanceof AnnotatedField) { | |
_accessorMethod = null; | |
_field = (Field) member.getMember(); | |
} else if (member instanceof AnnotatedMethod) { | |
_accessorMethod = (Method) member.getMember(); // 这里就是被赋值的地方 | |
_field = null; | |
} else { | |
// ...... | |
} | |
// ...... | |
} |
member 是通过参数传进来的,继续通过调用堆栈往上找。跟了一路,进入了一个 findBeanProperties 方法,
/** | |
* Method used to collect all actual serializable properties. * Can be overridden to implement custom detection schemes. */protected List<BeanPropertyWriter> findBeanProperties(SerializerProvider prov, | |
BeanDescription beanDesc, BeanSerializerBuilder builder) | |
throws JsonMappingException | |
{ | |
List<BeanPropertyDefinition> properties = beanDesc.findProperties(); | |
final SerializationConfig config = prov.getConfig(); | |
// ignore specified types | |
removeIgnorableTypes(config, beanDesc, properties); | |
// and possibly remove ones without matching mutator... | |
if (config.isEnabled(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS)) { | |
removeSetterlessGetters(config, beanDesc, properties); | |
} | |
// nothing? can't proceed (caller may or may not throw an exception) | |
if (properties.isEmpty()) { | |
return null; | |
} | |
// null is for value type serializer, which we don't have access to from here (ditto for bean prop) | |
boolean staticTyping = usesStaticTyping(config, beanDesc, null); | |
PropertyBuilder pb = constructPropertyBuilder(config, beanDesc); | |
ArrayList<BeanPropertyWriter> result = new ArrayList<BeanPropertyWriter>(properties.size()); | |
for (BeanPropertyDefinition property : properties) { | |
final AnnotatedMember accessor = property.getAccessor(); | |
// Type id? Requires special handling: | |
if (property.isTypeId()) { | |
if (accessor != null) { | |
builder.setTypeId(accessor); | |
} | |
continue; | |
} | |
// suppress writing of back references | |
AnnotationIntrospector.ReferenceProperty refType = property.findReferenceType(); | |
if (refType != null && refType.isBackReference()) { | |
continue; | |
} | |
if (accessor instanceof AnnotatedMethod) { | |
result.add(_constructWriter(prov, property, pb, staticTyping, (AnnotatedMethod) accessor)); | |
} else { | |
result.add(_constructWriter(prov, property, pb, staticTyping, (AnnotatedField) accessor)); | |
} | |
} | |
return result; | |
} |
就如同名字所描述的作用一样,该方法从 beanDesc 这个参数中获取 properties,也就是实体类的属性。
既然 beanDesc 也是传进来的,就继续往上找。最终来到了 beanDesc 获取的地方:
/** | |
* Main serializer constructor method. We will have to be careful * with respect to ordering of various method calls: essentially * we want to reliably figure out which classes are standard types, * and which are beans. The problem is that some bean Classes may * implement standard interfaces (say, {@link java.lang.Iterable}. | |
*<p> | |
* Note: sub-classes may choose to complete replace implementation, | |
* if they want to alter priority of serializer lookups. */@Override | |
@SuppressWarnings("unchecked") | |
public JsonSerializer<Object> createSerializer(SerializerProvider prov, | |
JavaType origType) | |
throws JsonMappingException | |
{ | |
// Very first thing, let's check if there is explicit serializer annotation: | |
final SerializationConfig config = prov.getConfig(); | |
BeanDescription beanDesc = config.introspect(origType); | |
JsonSerializer<?> ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo()); | |
if (ser != null) { | |
return (JsonSerializer<Object>) ser; | |
} | |
boolean staticTyping; | |
// Next: we may have annotations that further indicate actual type to use (a super type) | |
final AnnotationIntrospector intr = config.getAnnotationIntrospector(); | |
JavaType type; | |
if (intr == null) { | |
type = origType; | |
} else { | |
try { | |
type = intr.refineSerializationType(config, beanDesc.getClassInfo(), origType); | |
} catch (JsonMappingException e) { | |
return prov.reportBadTypeDefinition(beanDesc, e.getMessage()); | |
} | |
} | |
if (type == origType) { // no changes, won't force static typing | |
staticTyping = false; | |
} else { // changes; assume static typing; plus, need to re-introspect if class differs | |
staticTyping = true; | |
if (!type.hasRawClass(origType.getRawClass())) { | |
beanDesc = config.introspect(type); | |
} | |
} | |
// Slight detour: do we have a Converter to consider? | |
Converter<Object,Object> conv = beanDesc.findSerializationConverter(); | |
if (conv == null) { // no, simple | |
return (JsonSerializer<Object>) _createSerializer2(prov, type, beanDesc, staticTyping); | |
} | |
JavaType delegateType = conv.getOutputType(prov.getTypeFactory()); | |
// One more twist, as per [databind#288]; probably need to get new BeanDesc | |
if (!delegateType.hasRawClass(type.getRawClass())) { | |
beanDesc = config.introspect(delegateType); | |
// [#359]: explicitly check (again) for @JsonSerializer... | |
ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo()); | |
} | |
// [databind#731]: Should skip if nominally java.lang.Object | |
if (ser == null && !delegateType.isJavaLangObject()) { | |
ser = _createSerializer2(prov, delegateType, beanDesc, true); | |
} | |
return new StdDelegatingSerializer(conv, delegateType, ser); | |
} |
在获取 beanDesc 的地方打个断点,跟进去看看到底进行了什么操作。
# 调试
@Override | |
public BasicBeanDescription forSerialization(SerializationConfig config, | |
JavaType type, MixInResolver r) | |
{ | |
// minor optimization: for some JDK types do minimal introspection | |
BasicBeanDescription desc = _findStdTypeDesc(config, type); | |
if (desc == null) { | |
// As per [databind#550], skip full introspection for some of standard | |
// structured types as well | |
desc = _findStdJdkCollectionDesc(config, type); | |
if (desc == null) { | |
desc = BasicBeanDescription.forSerialization(collectProperties(config, | |
type, r, true)); | |
} | |
} | |
return desc; | |
} |
首先是进行了一些简单的类型判断,比如第一个 _findStdTypeDesc
,判断是否是基本类型,然后获取 BeanDescription。
因为这里是自己定义的实体类,所以进入 collectProperties 方法中。
/** | |
* @since 2.12 | |
*/protected POJOPropertiesCollector collectProperties(MapperConfig<?> config, | |
JavaType type, MixInResolver r, boolean forSerialization) | |
{ | |
final AnnotatedClass classDef = _resolveAnnotatedClass(config, type, r); | |
final AccessorNamingStrategy accNaming = type.isRecordType() | |
? config.getAccessorNaming().forRecord(config, classDef) | |
: config.getAccessorNaming().forPOJO(config, classDef); | |
return constructPropertyCollector(config, classDef, type, forSerialization, accNaming); | |
} |
这里基本上比较明了了,判断是否是 Record 类型,不是的话走 forPOJO,并且这里还用到了策略模式,而且也提供了接口来重写,也就是说用户可以定义自己的规则,来获取属性。
@Override | |
public AccessorNamingStrategy forPOJO(MapperConfig<?> config, AnnotatedClass targetClass) | |
{ | |
return new DefaultAccessorNamingStrategy(config, targetClass, | |
_setterPrefix, _getterPrefix, _isGetterPrefix, | |
_baseNameValidator); | |
} |
这里面三个常量 _setterPrefix, _getterPrefix, _isGetterPrefix
分别是 get、set、is,只要是方法前缀带这三个其中之一的,都会被调用。
拿到了正确的策略对象之后,进入 constructPropertyCollector 方法,依旧只是构造一个对象。这里这个对象之后会用来获取实体类的 properties。
/** | |
* Overridable method called for creating {@link POJOPropertiesCollector} instance | |
* to use; override is needed if a custom sub-class is to be used. * * @since 2.12 | |
*/protected POJOPropertiesCollector constructPropertyCollector(MapperConfig<?> config, | |
AnnotatedClass classDef, JavaType type, boolean forSerialization, | |
AccessorNamingStrategy accNaming) | |
{ | |
return new POJOPropertiesCollector(config, forSerialization, type, classDef, accNaming); | |
} |
构造完对象后,通过之前的 forSerialization 方法,创建一个 BasicBeanDescription 对象,不过目前这个对象里面还不存在 properties。
接着回到 createSerializer 方法,继续往下执行:
// ...... | |
// Slight detour: do we have a Converter to consider? | |
Converter<Object,Object> conv = beanDesc.findSerializationConverter(); | |
if (conv == null) { // no, simple | |
return (JsonSerializer<Object>) _createSerializer2(prov, type, beanDesc, staticTyping); | |
} | |
// ...... |
到这里后,进入 _createSerializer2
方法,
protected JsonSerializer<?> _createSerializer2(SerializerProvider prov, | |
JavaType type, BeanDescription beanDesc, boolean staticTyping) | |
throws JsonMappingException | |
{ | |
JsonSerializer<?> ser = null; | |
final SerializationConfig config = prov.getConfig(); | |
// Container types differ from non-container types | |
// (note: called method checks for module-provided serializers) if (type.isContainerType()) { | |
if (!staticTyping) { | |
staticTyping = usesStaticTyping(config, beanDesc, null); | |
} | |
// 03-Aug-2012, tatu: As per [databind#40], may require POJO serializer... | |
ser = buildContainerSerializer(prov, type, beanDesc, staticTyping); | |
// Will return right away, since called method does post-processing: | |
if (ser != null) { | |
return ser; | |
} | |
} else { | |
if (type.isReferenceType()) { | |
ser = findReferenceSerializer(prov, (ReferenceType) type, beanDesc, staticTyping); | |
} else { | |
// Modules may provide serializers of POJO types: | |
for (Serializers serializers : customSerializers()) { | |
ser = serializers.findSerializer(config, type, beanDesc); | |
if (ser != null) { | |
break; | |
} | |
} | |
} | |
// 25-Jun-2015, tatu: Then JsonSerializable, @JsonValue etc. NOTE! Prior to 2.6, | |
// this call was BEFORE custom serializer lookup, which was wrong. if (ser == null) { | |
ser = findSerializerByAnnotations(prov, type, beanDesc); | |
} | |
} | |
if (ser == null) { | |
// Otherwise, we will check "primary types"; both marker types that | |
// indicate specific handling (JsonSerializable), or main types that have // precedence over container types ser = findSerializerByLookup(type, config, beanDesc, staticTyping); | |
if (ser == null) { | |
ser = findSerializerByPrimaryType(prov, type, beanDesc, staticTyping); | |
if (ser == null) { | |
// And this is where this class comes in: if type is not a | |
// known "primary JDK type", perhaps it's a bean? We can still // get a null, if we can't find a single suitable bean property. ser = findBeanOrAddOnSerializer(prov, type, beanDesc, staticTyping); | |
// 18-Sep-2014, tatu: Actually, as per [jackson-databind#539], need to get | |
// 'unknown' serializer assigned earlier, here, so that it gets properly // post-processed if (ser == null) { | |
ser = prov.getUnknownTypeSerializer(beanDesc.getBeanClass()); | |
} | |
} | |
} | |
} | |
if (ser != null) { | |
// [databind#120]: Allow post-processing | |
if (_factoryConfig.hasSerializerModifiers()) { | |
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { | |
ser = mod.modifySerializer(config, beanDesc, ser); | |
} | |
} | |
} | |
return ser; | |
} |
前面一堆 findSerializer 直接跳过,到最后的
ser = findBeanOrAddOnSerializer(prov, type, beanDesc, staticTyping); |
至于为啥,因为之前在调用堆栈里见过,一定会走这个方法的。
/** | |
* Method that will try to construct a {@link BeanSerializer} for | |
* given class if at least one property is found, OR, if not, * one of add-on types. *<p> | |
* NOTE: behavior changed a bit | |
*/public JsonSerializer<Object> findBeanOrAddOnSerializer(SerializerProvider prov, JavaType type, | |
BeanDescription beanDesc, boolean staticTyping) | |
throws JsonMappingException | |
{ | |
// First things first: we know some types are not beans... | |
if (!isPotentialBeanType(type.getRawClass())) { | |
// 03-Aug-2012, tatu: Except we do need to allow serializers for Enums, | |
// as per [databind#24], [databind#2576] if (!ClassUtil.isEnumType(type.getRawClass())) { | |
return null; | |
} | |
} | |
return constructBeanOrAddOnSerializer(prov, type, beanDesc, staticTyping); | |
} |
可以看到最后又对 Bean 进行一次构造,继续跟进去:
/** | |
* Method called to construct serializer for serializing specified bean type if * (but only if, as of 2.10), at least one property is found. ** @since 2.10 | |
*/ @SuppressWarnings("unchecked") | |
protected JsonSerializer<Object> constructBeanOrAddOnSerializer(SerializerProvider prov, | |
JavaType type, BeanDescription beanDesc, boolean staticTyping) | |
throws JsonMappingException | |
{ | |
// 13-Oct-2010, tatu: quick sanity check: never try to create bean serializer for plain Object | |
// 05-Jul-2012, tatu: ... but we should be able to just return "unknown type" serializer, right? if (beanDesc.getBeanClass() == Object.class) { | |
return prov.getUnknownTypeSerializer(Object.class); | |
// throw new IllegalArgumentException("Cannot create bean serializer for Object.class"); | |
} | |
JsonSerializer<?> ser = _findUnsupportedTypeSerializer(prov, type, beanDesc); | |
if (ser != null) { | |
return (JsonSerializer<Object>) ser; | |
} | |
// 02-Sep-2021, tatu: [databind#3244] Should not try "proper" serialization of | |
// things like ObjectMapper, JsonParser or JsonGenerator... if (_isUnserializableJacksonType(prov, type)) { | |
return new ToEmptyObjectSerializer(type); | |
} | |
final SerializationConfig config = prov.getConfig(); | |
BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc); | |
builder.setConfig(config); | |
// First: any detectable (auto-detect, annotations) properties to serialize? | |
List<BeanPropertyWriter> props = findBeanProperties(prov, beanDesc, builder); | |
if (props == null) { | |
props = new ArrayList<BeanPropertyWriter>(); | |
} else { | |
props = removeOverlappingTypeIds(prov, beanDesc, builder, props); | |
} | |
// [databind#638]: Allow injection of "virtual" properties: | |
prov.getAnnotationIntrospector().findAndAddVirtualProperties(config, beanDesc.getClassInfo(), props); | |
// [JACKSON-440] Need to allow modification bean properties to serialize: | |
if (_factoryConfig.hasSerializerModifiers()) { | |
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { | |
props = mod.changeProperties(config, beanDesc, props); | |
} | |
} | |
// Any properties to suppress? | |
// 10-Dec-2021, tatu: [databind#3305] Some JDK types need special help // (initially, `CharSequence` with its `isEmpty()` default impl) props = filterUnwantedJDKProperties(config, beanDesc, props); | |
props = filterBeanProperties(config, beanDesc, props); | |
// Need to allow reordering of properties to serialize | |
if (_factoryConfig.hasSerializerModifiers()) { | |
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { | |
props = mod.orderProperties(config, beanDesc, props); | |
} | |
} | |
// And if Object Id is needed, some preparation for that as well: better | |
// do before view handling, mostly for the custom id case which needs // access to a property builder.setObjectIdWriter(constructObjectIdHandler(prov, beanDesc, props)); | |
builder.setProperties(props); | |
builder.setFilterId(findFilterId(config, beanDesc)); | |
AnnotatedMember anyGetter = beanDesc.findAnyGetter(); | |
if (anyGetter != null) { | |
JavaType anyType = anyGetter.getType(); | |
// copied from BasicSerializerFactory.buildMapSerializer(): | |
JavaType valueType = anyType.getContentType(); | |
TypeSerializer typeSer = createTypeSerializer(config, valueType); | |
// last 2 nulls; don't know key, value serializers (yet) | |
// 23-Feb-2015, tatu: As per [databind#705], need to support custom serializers JsonSerializer<?> anySer = findSerializerFromAnnotation(prov, anyGetter); | |
if (anySer == null) { | |
// TODO: support '@JsonIgnoreProperties' with any setter? | |
anySer = MapSerializer.construct(/* ignored props*/ (Set<String>) null, | |
anyType, config.isEnabled(MapperFeature.USE_STATIC_TYPING), | |
typeSer, null, null, /*filterId*/ null); | |
} | |
// TODO: can we find full PropertyName? | |
PropertyName name = PropertyName.construct(anyGetter.getName()); | |
BeanProperty.Std anyProp = new BeanProperty.Std(name, valueType, null, | |
anyGetter, PropertyMetadata.STD_OPTIONAL); | |
builder.setAnyGetter(new AnyGetterWriter(anyProp, anyGetter, anySer)); | |
} | |
// Next: need to gather view information, if any: | |
processViews(config, builder); | |
// Finally: let interested parties mess with the result bit more... | |
if (_factoryConfig.hasSerializerModifiers()) { | |
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { | |
builder = mod.updateBuilder(config, beanDesc, builder); | |
} | |
} | |
try { | |
ser = builder.build(); | |
} catch (RuntimeException e) { | |
return prov.reportBadTypeDefinition(beanDesc, "Failed to construct BeanSerializer for %s: (%s) %s", | |
beanDesc.getType(), e.getClass().getName(), e.getMessage()); | |
} | |
if (ser == null) { // Means that no properties were found | |
// 21-Aug-2020, tatu: Empty Records should be fine tho if (type.isRecordType()) { | |
return builder.createDummy(); | |
} | |
// 06-Aug-2019, tatu: As per [databind#2390], we need to check for add-ons here, | |
// before considering fallbacks ser = (JsonSerializer<Object>) findSerializerByAddonType(config, type, beanDesc, staticTyping); | |
if (ser == null) { | |
// If we get this far, there were no properties found, so no regular BeanSerializer | |
// would be constructed. But, couple of exceptions. // First: if there are known annotations, just create 'empty bean' serializer if (beanDesc.hasKnownClassAnnotations()) { | |
return builder.createDummy(); | |
} | |
} | |
} | |
return (JsonSerializer<Object>) ser; | |
} |
在中间可以看到有这么一句:
// First: any detectable (auto-detect, annotations) properties to serialize? | |
List<BeanPropertyWriter> props = findBeanProperties(prov, beanDesc, builder); |
一路跟进去,
/** | |
* Method used to collect all actual serializable properties. * Can be overridden to implement custom detection schemes. */protected List<BeanPropertyWriter> findBeanProperties(SerializerProvider prov, | |
BeanDescription beanDesc, BeanSerializerBuilder builder) | |
throws JsonMappingException | |
{ | |
List<BeanPropertyDefinition> properties = beanDesc.findProperties(); | |
//..... 省略 | |
} |
@Override | |
public List<BeanPropertyDefinition> findProperties() { | |
return _properties(); | |
} |
来到 BeanDescription 的 _properties()
方法处:
protected List<BeanPropertyDefinition> _properties() { | |
if (_properties == null) { | |
_properties = _propCollector.getProperties(); | |
} | |
return _properties; | |
} |
这里这个 _propCollector
就是在第一次创建 BeanDescription 时设置进去的 POJOPropertiesCollector 对象。
进入 getProperteis 方法,
public List<BeanPropertyDefinition> getProperties() { | |
// make sure we return a copy, so caller can remove entries if need be: | |
Map<String, POJOPropertyBuilder> props = getPropertyMap(); | |
return new ArrayList<BeanPropertyDefinition>(props.values()); | |
} |
接着进入 getPropertyMap:
protected Map<String, POJOPropertyBuilder> getPropertyMap() { | |
if (!_collected) { | |
collectAll(); | |
} | |
return _properties; | |
} |
不过在这里发现_collected 是 true,并且_properties 已经被初始化了,不过问题也不大,按照正常逻辑的话,这里_collected 大概是 false 的状态,接着会进入 collectAll 方法。之后在_properties 上打断点,也可以证实这一点。
/** | |
* Internal method that will collect actual property information. * * @since 2.6 | |
*/protected void collectAll() | |
{ | |
LinkedHashMap<String, POJOPropertyBuilder> props = new LinkedHashMap<String, POJOPropertyBuilder>(); | |
// First: gather basic data | |
_addFields(props); // note: populates _fieldRenameMappings | |
_addMethods(props); | |
// 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static | |
// inner classes, see [databind#1502] if (!_classDef.isNonStaticInnerClass()) { | |
_addCreators(props); | |
} | |
// Remove ignored properties, first; this MUST precede annotation merging | |
// since logic relies on knowing exactly which accessor has which annotation _removeUnwantedProperties(props); | |
// and then remove unneeded accessors (wrt read-only, read-write) | |
_removeUnwantedAccessor(props); | |
// Rename remaining properties | |
_renameProperties(props); | |
// and now add injectables, but taking care to avoid overlapping ones | |
// via creator and regular properties _addInjectables(props); | |
// then merge annotations, to simplify further processing | |
// 26-Sep-2017, tatu: Before 2.9.2 was done earlier but that prevented some of // annotations from getting properly merged for (POJOPropertyBuilder property : props.values()) { | |
property.mergeAnnotations(_forSerialization); | |
} | |
// And use custom naming strategy, if applicable... | |
// 18-Jan-2021, tatu: To be done before trimming, to resolve // [databind#3368] PropertyNamingStrategy naming = _findNamingStrategy(); | |
if (naming != null) { | |
_renameUsing(props, naming); | |
} | |
// Sort by visibility (explicit over implicit); drop all but first of member | |
// type (getter, setter etc) if there is visibility difference for (POJOPropertyBuilder property : props.values()) { | |
property.trimByVisibility(); | |
} | |
// and, if required, apply wrapper name: note, MUST be done after | |
// annotations are merged. if (_config.isEnabled(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME)) { | |
_renameWithWrappers(props); | |
} | |
// well, almost last: there's still ordering... | |
_sortProperties(props); | |
_properties = props; | |
_collected = true; | |
} |
主要逻辑就是在 addFields 方法和 addMethods 方法里面了,因为代码太长,就不放出来了,简单讲一下逻辑。
# _addFields
这个方法的逻辑比较简单,就是将实体类中的所有字段加入到 Map 中,大致形式如下:
name -> {POJOPropertyBuilder@11000} "[Property 'name'; ctors: null, field(s): [field com.example.fastjsondemo.Entity#name][visible=false,ignore=false,explicitName=false], getter(s): null, setter(s): null]" |
可以看到,此时 getter 和 setter 都还是 null 的状态。
# _addMethods
这个方法的代码量就比较多了,分步骤来讲:
- 通过方法参数数量,判断是 get 方法还是 set 方法,需要注意的是,这里面拿到的是全部的方法,不只是 get 或 set 开头的。
- 如果是 get 方法,判断返回值是否为空,为空则直接跳过,这也解释了为什么需要有返回值才会被调用。
- 判断注解,@JsonAnyGetter,@JsonKey,@JsonValue,分别使用不同的 LinkedList 存储。
- 通过 findNameForRegularGetter,findNameForIsGetter,分别获取 get 开头和 is 开头的方法的后缀,例如 getName,则会获取到 name。
- set 方法大致流程如 get 方法。
- 获取到名字之后,就将其于 map 中的字段进行匹配,放入对应的 getter 和 setter
那么问题就来了,假设我只有方法,没有字段呢,也就是我的业务最开始遇到的问题。
直接进入对应操作的代码中:
protected POJOPropertyBuilder _property(Map<String, POJOPropertyBuilder> props, | |
String implName) | |
{ | |
POJOPropertyBuilder prop = props.get(implName); | |
if (prop == null) { | |
prop = new POJOPropertyBuilder(_config, _annotationIntrospector, _forSerialization, | |
PropertyName.construct(implName)); | |
props.put(implName, prop); | |
} | |
return prop; | |
} |
这一段的逻辑就是,假设 map 中不存在那个字段,就重新创建一个放进去,这样会发生什么呢,假设我的实体类只有一个 name,和一个 getPassword 方法,那么一旦我将这个实体类直接返回给前端,那么前端会接收到一个 json:
{ | |
"name":"", | |
"password":"" | |
} |
password 值自然就是 getPassword 执行出来的结果,如果里面有什么业务逻辑,也会一并执行。
所以实体类中除了对应属性的方法,最好是不要放其他 is,get,set 开头的方法。