排坑:kotlin对象序列化过程参数丢失

来源:互联网 发布:淘宝极限词 编辑:程序博客网 时间:2024/06/08 10:26

问题现象与背景:

这个bug出现在引入kotlin代码的过程中。我们的项目采用的是spring boot框架,为前端提供业务接口。通过周密调研,我们计划在一个子模块中应用kotlin来进行业务开发。springbootkotlin的结合方式不再本文累述。

开发过程中发现了一个bug:系统提供的接口有部分参数丢失。如图所示,输出的接口数据中没有is_try_readis_onlineis_free3个数据。


进一步进行debug,发现内存中该对象确实包含对应属性。


 


方法已经运行到了return这一步,那么为什么在输出环节丢失了数据呢?


排查与定位:

首先分析该输出对象的类的设计是否有异常。该对象的类采用kotlin的数据类实现。


Kotlin 可以创建一个只包含数据的类,关键字为data。编译器会自动的从主构造函数中根据所有声明的属性生产equals()、hashCode()、toString() 、componentN()、和copy() 函数。kotlin的数据类简化了这种用于表示特定实体的类,不用再麻烦地设置settergetter方法。根据kotlin代码设计原则,属性默认为public级别可见性,编译器会为其生成settergetter方法。如图所示的数据类设计应该没有问题。

 

接着我反编译数据类class文件,发现了一些端倪。如图所示,"is"开头的变量,编译器自动加上setget方法时,命名规则和其他在变量首部添加"set""get"字母的驼峰方式有所不同,例如is_online变量自动生成的方法为set_online()is_online()isOnline变量自动生成方法为setOnline()isOnline()。这本身无可厚非,但是和某些java框架、工具结合起来就显现出了问题。

 


综上,我觉得可能spring boot默认的序列化工具在把对象转换成json字符串时可能找不到上述"is"开头的变量的get方法才导致该变量丢失。于是我写了一个简单的测试方法来测试fastJsonGsonJackson3个序列化工具。如图所示。


其中Book是一个kotlin数据类,从输出结果发现:只有Gson保留了完整数据。


 

 

解决方案:

至此,定位到了问题原因是kotlin"is"开头变量编译生成的setget方法命名方式有所不同导致某些java序列化工具不能识别。因为项目用的序列化工具是jackson,所以我升级了jackson的版本,发现变量仍然丢失。

解决方案一:

数据类不用"is"开头来命名变量。但是这个始终留下了隐患,开发人员稍不注意可能就掉坑里了,治标不治本。

解决方案二:

Gson替换框架的序列化工具。在典型的Spring场景中,一旦请求退出后,那么@Controller注解就会去渲染一个视图。我们可以通过@RequestBody或者@RestController这种注解来请求Spring,并且可以直接将Model中的java对象注入到Response中,而这个过程中应用了序列化。我采用HttpMessageConverters来自定义gson为定首选序列化工具。配置如下:

@Configuration

publicclass CustomConfiguration {

    @Bean

    public HttpMessageConverterscustomConverters() {

       Collection<HttpMessageConverter<?>> messageConverters = newArrayList<>();

        GsonHttpMessageConvertergsonHttpMessageConverter = new GsonHttpMessageConverter();

       messageConverters.add(gsonHttpMessageConverter);

        return new HttpMessageConverters(true,messageConverters);

    }

}

通过这个配置,springframework框架默认使用的HttpMessageConverter中追加了GsonHttpMessageConverter来进行序列化工作。

最终我采用了方案二的方式来处理这个问题。


其他:

定位问题过程中其实还有一些曲折,例如is_try_readis_onlineis_free3个变量都是kotlinInt类型,而kotlinIntjavaintInteger相对应,怀疑可能是这个类型出现的序列化问题。但是在控制变量多次尝试后,发现与类型无关,String类型的is_online变量依然会丢失。

Gson提供了流式序列化的方法,核心是各种adapterwrite方法。


通过getAdapter(TypeToken<T> type)方法来绑定对象的变量到其AdapterboundFields来进行后续转换。


 

参考资料:

《Configure gson in spring using GsonHttpMessageConverter》


原创粉丝点击