最近阅读公司的规范,发现了这样的一条:
【强制】POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误。
如果读者研发的产品正在大量使用阿里系框架,这个问题就需要十分注意。
我们假设一个场景,设计文档中设计了一个类,其中某个field命名为 isXx:
这时,对于前端后分离的项目,前端认为你返回的json中将存在一个 Key为 “isXx”的value。后端亦是如此开发。
Class SomeClass{
boolean isXx;
/...
}
如果使用FastJson框架序列化SomeClass类的实例对象。直观理论上,应该返回前端一个:
{
"isXx" : true/false
}
然而实际上,前端获取到的json数据为:
{
"Xx" : true/false
}
显然,前后端都按设计文档开发了,直观理论无误。那么问题,就出在了传递的过程。
通过阅读FastJson源码,发现:
//com.alibaba.fastjson.util.TypeUtils.computeGetters
if (methodName.startsWith("is")) {
if (methodName.length() < 3) {
continue;
}
if (returnType != Boolean.TYPE
&& returnType != Boolean.class) {
continue;
}
char c2 = methodName.charAt(2);
String propertyName;
Field field = null;
if (Character.isUpperCase(c2)) {
if (compatibleWithJavaBean) {
propertyName = decapitalize(methodName.substring(2));
} else {
propertyName = Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3);
}
propertyName = getPropertyNameByCompatibleFieldName(fieldCacheMap, methodName, propertyName, 2);
} else if (c2 == '_') {
propertyName = methodName.substring(3);
field = fieldCacheMap.get(propertyName);
if (field == null) {
String temp = propertyName;
propertyName = methodName.substring(2);
field = ParserConfig.getFieldFromCache(propertyName, fieldCacheMap);
if (field == null) {
propertyName = temp;
}
}
} else if (c2 == 'f') {
propertyName = methodName.substring(2);
} else {
propertyName = methodName.substring(2);
field = ParserConfig.getFieldFromCache(propertyName, fieldCacheMap);
if (field == null) {
continue;
}
}
boolean ignore = isJSONTypeIgnore(clazz, propertyName);
if (ignore) {
continue;
}
if (field == null) {
field = ParserConfig.getFieldFromCache(propertyName, fieldCacheMap);
}
if (field == null) {
field = ParserConfig.getFieldFromCache(methodName, fieldCacheMap);
}
JSONField fieldAnnotation = null;
if (field != null) {
fieldAnnotation = TypeUtils.getAnnotation(field, JSONField.class);
if (fieldAnnotation != null) {
if (!fieldAnnotation.serialize()) {
continue;
}
ordinal = fieldAnnotation.ordinal();
serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
parserFeatures = Feature.of(fieldAnnotation.parseFeatures());
if (fieldAnnotation.name().length() != 0) {
propertyName = fieldAnnotation.name();
if (aliasMap != null) {
propertyName = aliasMap.get(propertyName);
if (propertyName == null) {
continue;
}
}
}
if (fieldAnnotation.label().length() != 0) {
label = fieldAnnotation.label();
}
}
}
if (aliasMap != null) {
propertyName = aliasMap.get(propertyName);
if (propertyName == null) {
continue;
}
}
if (propertyNamingStrategy != null) {
propertyName = propertyNamingStrategy.translate(propertyName);
}
//优先选择get
if (fieldInfoMap.containsKey(propertyName)) {
continue;
}
FieldInfo fieldInfo = new FieldInfo(propertyName, method, field, clazz, null, ordinal, serialzeFeatures, parserFeatures,
annotation, fieldAnnotation, label);
fieldInfoMap.put(propertyName, fieldInfo);
}
逻辑并不复杂,判断以 'is'开头时,会从'is'后截取作为PropertyName。
源码中没有注释解释为何要这么做,作者理解为这与setter/getter构造原理相似,都是通过规范去反射实现功能。
Spring框架中依赖注入也有类似的原理。