排查 Mybatis-plus 3.5.5: 返回的pages参数是从哪里来的
1.现象
返回的参数里面有一个 pages 属性,表示一共分了几页
但是返回的Page对象里面并没有这个属性(而且其他无关属性最后没返回如orders,searchCount这些)
Pages类里面也没这个属性
1.开始分析
第一步:干 Page 这个类,全局搜 pages
干到了 IPage 接口
看看谁调用了 getPages() 这个方法
于是,再看看 PaginationInnerInterceptor 这个类吧。
我先实验一下,把这个类注释掉。看看这个类作用是啥,光顾这添加这个配置,也不知道干啥的。
但是,它也对 pages 做了一个重新赋值啊。
还是先看看这里它是怎么做的,因为另外一个看起来很难找,无语。
在 PaginationInnerInterceptor 里面 全局搜索 pages。只有刚刚看到的那个 continuePage() 方法在用。
看到了 setTotal()
那我再看看有没有setPages,其实不用看了,Page 类每这个属性,哪里来的它的set方法呢。
我感觉我应该换个方向,这个pages 很可能是序列化的时候,加进去的,但是那 PaginationInnerInterceptor 什么时候给 pages 赋值的呢。先序列化再赋值??? 感觉不可能。所以在 PaginationInnerInterceptor 之前肯定还有一个地方做了操作,到底在哪里。
开始百度。。。。。。。。。。。。
中间还用 postman 试了一下,是不是 工具的问题,结果是一样的,排除工具的影响。
百度无果,一堆乱七八糟的。于是加了MP的官方群,还没来得及问。
先问了一下我多几年经验的同事,他说 jackson 序列化 的时候 是按 get方法算的还是属性算的 记不清了,可能是这个。于是再去翻看Page
同事建议我新建一个类继承一下 Page,自己去加一个 未在结果里面显示的属性的get方法,验证一下。
同步修改一下 接口中的返回值 Page -> MyPage
返回结果确实多了 searchCount 这个属性
曾经,我离答案这么的近
我感觉我应该换个方向,这个pages 很可能是序列化的时候,加进去的,但是那 PaginationInnerInterceptor 什么时候给 pages 赋值的呢。先序列化再赋值??? 感觉不可能。所以在 PaginationInnerInterceptor 之前肯定还有一个地方做了操作,到底在哪里。
这里面是有问题,其实从来都没有创建一个新的属性 pages,所以不存在 之前肯定还有一个地方做了操作生成了 pages 属性,PaginationInnerInterceptor 做的也只是 setTotal的值,然后 序列化的时候走 getPages() 逻辑,我天真的以为 PaginationInnerInterceptor是给 pages 赋值的,事实上前面翻 PaginationInnerInterceptor 源码的时候,一直没找到 setPages 这种东西就在提醒我,这个地方不做这个操作,当时觉得肯定在其他地方做了,好吧,错误的想法。
然后随便百度一下 jackson的序列化依赖get方法,基本都可以看到这么一句话
Jackson中,序列化不依赖于实体类中的变量,只依赖于实体类中的get方法。同理反序列化依赖set方法
结论: 这就是答案,pages参数来源于 Jackson 序列化的时候依赖的 package com.baomidou.mybatisplus.extension.plugins.pagination.Page类的; getPages()方法,
有点好奇 Jackson 的怎么去做这个事情的,
后面还看到了这么一句
Jackson2在初始化序列器时,对pojo类型对象会收集其属性信息,属性包括成员变量及方法,然后属性名称和处理过后的方法名称做为key保存到一个LinkedHashMap中。收集过程中会调用com.fasterxml.jackson.databind.util.BeanUtil中的 legacyManglePropertyName 方法用来处理方法名称,它会将get/set方法前缀,即get或set去掉,并将其后面的连续大写字符转换成小写字符返回。
除boolean变量外的所有变量,get方法的解析规则:get后面的连续大写字母都转成小写,subString(3)作为变量名。
即name和nAme变量的get方法分别为getName(),getNAme(),但是jackson认为只有一个变量name。(这是 Lombok 不能单字母驼峰的问题,有兴趣的可百度,这里不提了)
那就去看看 com.fasterxml.jackson.databind.util.BeanUtil里面的 legacyManglePropertyName 的代码看看它咋处理这个东西的。
试一下 MyPage类 加一个 public 字段,不写对应的 get 方法,jackson 可不可以序列化它。
第三个这个方法调用的时候,baseName不带前缀(get set is)。
分为看看这三个方法就知道了。
最后再来看看这个 legacyManglePropertyName 这个方法。
上面那个截图有的地方写错了,
下一步 check,你这个 去掉get,剩下的第一个字符是不是大写,如果不是,比如说 getiPhone,到这里就直接返回了,所以 对于 iphone 这样的 单字母驼峰, lombok 重写成 getIPhone 是不行的 ,会走到下面的逻辑,变成 iphone,而自己手动写成 getiPhone,可以直接返回成 iPhone,没有任何问题