为什么建议不使用以is为前缀的名称命名布尔型字段

[为什么建议不使用以is为前缀的名称命名布尔型字段]

前后端在传递参数的过程中,需要经历一个序列化和反序列化的过程。前端把请求参数序列化后,通过网络发到后端。后端收到二进制数据后,根据在接口中定义的类型对收到的数据进行反序列化。现在我们定义一个RequestBody的对象如下所示:

image.png

查看编译好的类文件可以看到,@Data注解在编译的时候会把布尔型字段的gettersetter方法命名成以is为前缀的方法。 假设前端以如下参数请求后端接口

{
    "name": "test",
    "tall": false,
    "isHappy": true
}
复制代码

当springboot第一次收到这个请求的时候,会创建CustomObject这个类的反序列化器。在创建反序列化器的过程中,忽略掉其他步骤,我们直接看下面这个collectAll方法,这个方法用来给反序列化器收集对象的属性信息。在这个方法中,执行完_addFields方法后,我们可以看到props里有isHappy这个属性。

image.png

接着往下执行,在执行过_addMethods方法后,可以在下图中看到,我们的props中多了一个happy属性。

这个happy属性是从哪里来的呢?我们进入到_addMethod方法中看一下它的实现逻辑。在_addMethods方法中,先通过方法参数的个数来确定当前是getter方法,还是setter方法。如果参数个数为0,那就执行_addGetterMethod,添加属性的getter方法。

_addGetterMethod方法中,关键路径如下图所示,它会先执行findNameForRegularGetter,这个方法会以get前缀来匹配方法名,因为布尔型参数值的getter方法被lombok设置成了is前缀,所以这里返回的属性名为空。

因为属性名为空,所以会接着执行findNameForIsGetter方法,这个方法是以is前缀来匹配getter方法名,所以会返回一个新的属性名happy。

image.png

_addGetterMethod方法的最后,调用如下方法添加getter。

_property(props, implName).addGetter(m, pn, nameExplict, visible, ignore);
复制代码

_property方法执行的逻辑是先从props map中按name获取对象,如果为null,就新建一个属性对象,并添加到props map中。这样,props就多出了一个happy属性。

在执行_addSetterMethod时,统一用set前缀匹配方法名,isHappy属性的setter方法名为setHappy,所以获取到的属性名也为happy。这样,执行完_addMethods方法后,isHappy的getter方法和setter方法都被设置为了happy属性的方法。

此时,isHappy属性对应的POJOPropertyBuilder为

[Property 'isHappy'; ctors: null, field(s): [field com.example.demo.models.CustomObject#isHappy][visible=false,ignore=false,explicitName=false], getter(s): null, setter(s): null]
复制代码

执行完_addMethods方法后,在上文提及的collectAll方法中会执行_removeUnwantedProperties方法,删除不必要的属性。

image.png

因为isHappy对应的POJOPropertyBuilder对象没有任何visible=true的成员,所以prop.anyVisible方法为false,isHappy属性从props集合中被删除。从下图可见,执行完_removeUnwantedProperties方法后,props中已经不存在isHappy成员了。

image.png

这就导致了前端虽然传了isHappy的值,但在反序列化的时候,因为属性中不存在isHappy这个属性,所以isHappy的值就被丢弃了。至此,我们就找到了这个问题真正的原因,lombok和默认的Jackson反序列化机制”联手作案”,导致后端接收不到正确的布尔值变量。

猜你喜欢

转载自juejin.im/post/7106057512415133726