前因
某次线上测试发现,通过 arthas 发现是对象判断不一致,代码逻辑反复检查没有问题。于是把目光放到实体类。
结果
先说结果,和人家 ObjectMapper 没啥关系,是没遵守变量命名规范导致的,lombok在这个过程中隐藏了这个问题导致没能及时发现。
解决方法
- 改变量名字。推荐。直接治本。但是要考虑考虑在动手哦。
- 使用包装类型,这样就不走 primitive 的命名范式。十分不推荐!
- 在 setter 上使用 @JsonGetter([value = ] “序列化后的名字”)。临时兼容方案。
- 在属性上加 @JsonProperty 这样 Jackson 就会知道用这个名字。临时兼容方案。
但是线上问题可不能这样粗暴的解决,会存在数据不一致的,之前的老数据不就成了脏数据了,我的处理方式是对被影响的数据进行筛选,确定范围后选个时间把正确代码更新上服务器,之后把脏数据给修复一下,修复要重复两次,避免修复过程中有新的脏数据。
排查过程
测试代码如下,可以自行尝试,记得加上依赖
public class OtherTest {
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
ObjectMapperTestModel testModel = ObjectMapperTestModel.builder()
.isA(1)
.isB(2)
.enabled(true)
.isEnabled(false)
.build();
try {
String jsonStr = objectMapper.writeValueAsString(testModel);
System.out.println("这里是序列化后的对象: " + jsonStr);
ObjectMapperTestModel result = objectMapper.readValue(jsonStr, ObjectMapperTestModel.class);
System.out.println("这里是反序列化后的对象: " + result.toString());
} catch (JsonProcessingException e) {
System.out.println("发生异常");
throw new RuntimeException(e);
}
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ObjectMapperTestModel {
int isA;
Integer isB;
boolean enabled;
boolean isEnabled;
}
执行结果
不知道大家有没有看出问题,isEnabled 这个变量丢了,在序列化的过程中就没了!
我们转到生成类中看一下, 可以发现不论是 getter 还是 setter 都没有对 isEnabled 进行操作,仿佛一开始就不存在。这就是问题原因所在。
解决这个问题很简单,但是得先知道为什么会这样,简单来说就是命名规范不对!
java bean 的命名是通过访问器(getter/setter) 决定,重点来了: boolean 类型默认使用 is<属性名> 而非优先使用 get<属性名> 作为访问器名称。原文如下
到这里其实问题就明晰了,大家可以各显神通了。接下来是对一些解决方法的验证。
使用包装类解决
把 model 中的 isEnabled 改成 Boolean 后重新build
可以看到,我们钻一下文档的漏洞,人家说 boolean 类型是这样,那我用 Boolean 不就好了。事实上确实OK(仅做演示, 可别这么搞)。
使用注解( @JsonProperty / @JsonGetter)
前者是属性可用,后者是方法限定,参数填你想反序列化用哪一个属性的名称
本例中对被忽略的 isEnabled 加上 @JsonProperty("enabled")
这样的话反序列化就会使用 enabled 的值了
最后吐槽一下: 用工具虽然爽,但是坑也得注意才行,不然工具就和坑上的树叶一样,把坑伪装的很完美,最后得不偿失。