[alibaba/fastjson]问题:修改属性的类型后无法解析

2024-08-28 157 views
1
  • 项目第一次启动,调用public static <T> T parseObject(String text, Class<T> clazz)没有问题正常解析

  • 修改clazz中的某个属性String改为int再次调用parseObject便会出现问题,调试到FieldDeserializer.setValue(Object object, Object value) 中的method.invoke(object, value)

  • 部分信息如下

    
    com.alibaba.fastjson.JSONException: set property error, com.....

Caused by: java.lang.reflect.InvocationTargetException at com.zeroturnaround.jrebelbase.facade.w.throwWrappedNoSuchMethodError(SourceFile:179) at com.zeroturnaround.jrebelbase.facade.w.checkMethodRemoved(SourceFile:200) at java.lang.reflect.Method.invoke(Method.java:498) at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:110) ... 99 more Caused by: java.lang.NoSuchMethodError: com.test.saas.integration.dto.SmartBoxDto.setiErrorCode(I)V ... 103 more



- 貌似只有在这个项目里这样,换成Gson则没有这个问题
- 开发中使用了jrebel
- 想请教下有没有什么思路能定位出现这个问题的原因,现在是能是类型变了,重新启用项目才行。

回答

8

猜测与jrebel的热部署功能及fastjson的运行机制有关。 首先提一下fastjson的缓存机制:假如clazz类在项目启用后曾经被序列化/反序列化过,fastjson会缓存一份clazz类对象专属的序列化器和反序列化器,里面包含了clazz类的方法、属性等元数据,以供之后的序列化和反序列化使用。 而jrebel的热部署功能会使得即使clazz类定义被修改后,内存中仍保留原本的clazz类的标识和状态。 因此在下一次序列化、反序列化时fastjson通过这些标识和状态会认为可以直接复用之前缓存下来的数据,此时就出现了真实clazz数据与fastjson缓存的clazz元数据对应不上的情况。 当你重新启用项目时,相当于手动清除了fastjson缓存好的数据,所以可以生效。

可以试试在修改了clazz类型之后,通过调用SerializeConfigParseConfigput(clazz.class, null)方法,用null值取代原有的缓存,强制重新生成序列化器和反序列化器。

3

fastjson为了快速处理,缓存了对象的类型数据和反射相关数据,你改了类型,缓存的数据就对不上了。

改了类型就重启一下再debug

4

清除FastJson缓存的方法如下:

@RestController
@RequestMapping("/api")
public class SystemController {
    @ApiOperation(value = "开发时清除缓存", notes = "用于开发使用JRebel时清除FastJson的对象解析Cache")
    @RequestMapping(value = "/clearCache", method = RequestMethod.GET)
    public String clearCache() {
        SerializeConfig.getGlobalInstance().clearSerializers();
        ParserConfig.getGlobalInstance().clearDeserializers();
        return "";
    }
}

调用该方法即可清除缓存。 可以参见我的博文:解决JRebel进行代码热加载时FastJson的JsonField的缓存无法刷新问题

6

SerializeConfig.getGlobalInstance().setAsmEnable(false);