2025-12-01
心有多大,舞台就有多大。——巴尔扎克
近期有社区用户反馈,后台管理系统出现了严重分页 BUG:Selector 和 Rule 列表分页失效,始终只显示第一页,总数消失(issue #6239)。作为 API 网关领域的核心组件,分页异常直接影响日常运维和平台体验。那么问题到底出在了哪里?又该如何优雅修复?本文带你一步步梳理、避坑、实践!
一、问题现象回顾
- 主要表现:
- Selector / Rule 的分页查询只能看到第一页,页码和总数全部失灵
- 查询结果总是只有第一页内容
相关 issue:https://github.com/apache/shenyu/issues/6239
修复 PR:https://github.com/apache/shenyu/pull/6243
二、根本原因追溯 —— PageHelper 的使用陷阱
问题根因在于分页核心逻辑的隐藏陷阱:
经典"陷阱"代码:
1 | |
为什么是陷阱?
-
searchByCondition返回值类型是List<T>,但可能是普通的ArrayList,也可能是com.github.pagehelper.Page(PageHelper会返回Page对象,但如果下游有包裹、转换则可能不是)。 -
PageInfo 构造函数在源码如下:
1
2
3
4
5
6
7public PageInfo(List<? extends T> list, int navigatePages) {
if (list instanceof Page) {
// 只有Page对象才能正常获取分页元数据
} else {
// 普通List没有分页信息
}
} -
一旦
searchByCondition返回的不再是Page,PageInfo 就只能包裹普通 List,无法拿到总数、页码等分页信息!
案例回溯
- 某些查询能保证 Mapper 直接返回 Page 对象(MyBatis + PageHelper联用,自动包装)。
- 但由于调用链复用,
searchByCondition可能返回值类型提前变成普通 List,分页能力被“吃掉”。
结论
new PageInfo<>(searchByCondition(…)) 属于易踩坑误导写法,只有拿到 Page 对象时才能安全用,复杂链路/多场景容易失效!
三、修复思路与改进实践
1. 提取 PageHelper 真分页能力
- 必须保证分页 Bean 能准确拿到 Page 对象
- 不能希望所有地方都用 PageHelper
- 只动 Selector 和 Rule 模块(涉及分页失效,且最易受影响)
2. 优化代码实现
Selector 分页代码优化:
1 | |
Rule 分页同理,只需用 PageHelper 原生流程即可。
这样做的好处是:再也不用担心 searchByCondition 返回的是 ArrayList 还是 Page 对象,保证每次都能拿到核心分页能力!
四、相关源码片段及解释
PageHelper核心对象原理:
1 | |
PageInfo构造函数要求:
1 | |
所以务必用Page对象!!,否则开发者每次都容易被误导踩坑。
五、陷阱总结与后续展望
易踩坑点
- 各种“快捷”写法虽然方便,但实际返回类型往往不可控
- PageHelper的拦截器只能保证最直接的 Mapper/SQL 返回 Page 类型
- 任意再包一层 List,或被其他泛型原型包裹,会丢失分页元数据
修复建议与最佳实践
- 必须保证分页被 Mapper 端页面级拦截并返回 Page
- 只对核心场景做更改(Selector 和 Rule),兼顾解耦与通用性
- 其他地方如用到 searchByCondition 需要仔细校验,不要“偷懒”滥用包裹写法
- 可以考虑统一分页处理工具类或代码注释,减小误导风险
六、更多高级改造建议
- 可以用全局分页工具进行类型判断,自动降级为 List 但告警
- 定义统一分页接口约束,后续新模块统一走 Page
- 建议社区维护统一分页文档,供团队协作时查阅
七、PR详情与链接
本次修复 PR 👉 #6243
- 由 VampireAchao 提交
- 只改动 Selector/Rule 分页逻辑,其他模块暂不变动
- 彻底解决官方后台分页失效“史诗大坑”
八、总结
分页逻辑看似简单,实则暗藏“类型陷阱”。本次 ShenYu 的 issue #6239 与 PR #6243,不仅修复了实际线上BUG,更为后续所有基于 PageHelper 的开发场景敲响了警钟:一定要确保 Page 对象贯穿全流程,避免隐式 List 误包。
面对海量数据与高并发场景,只有把分页基础做好,才能为平台稳定和高效打好坚实基础!
欢迎讨论更多ShenYu相关疑问与最佳实践,也欢迎关注PR进展和后续优化。