2026/4/18 11:15:23
网站建设
项目流程
微信开发者工具是干嘛的,企业网站seo托管怎么做,盐城公司网站建设电话,自己做网站想更换网址一、PageHelper基础回顾1.1 PageHelper简介PageHelper是国内广泛使用的MyBatis分页插件#xff0c;通过拦截器机制实现对SQL的自动分页改写。1.2 原生PageHelper使用方式java// 传统使用方式
PageHelper.startPage(pageNum, pageSize);
ListUser users userMapper.se…一、PageHelper基础回顾1.1 PageHelper简介PageHelper是国内广泛使用的MyBatis分页插件通过拦截器机制实现对SQL的自动分页改写。1.2 原生PageHelper使用方式java// 传统使用方式 PageHelper.startPage(pageNum, pageSize); ListUser users userMapper.selectAll(); PageInfoUser pageInfo new PageInfo(users);1.3 原生方式的缺点代码侵入性强需要手动调用startPage分页逻辑与业务逻辑混合难以统一管理分页参数容易忘记清理ThreadLocal中的分页参数二、自定义分页注解设计与实现2.1 注解定义java/** * 自定义分页注解 */ Target({ElementType.METHOD}) Retention(RetentionPolicy.RUNTIME) Documented public interface PageQuery { /** * 页码参数名从请求参数中获取 */ String pageNum() default pageNum; /** * 每页数量参数名从请求参数中获取 */ String pageSize() default pageSize; /** * 默认页码 */ int defaultPageNum() default 1; /** * 默认每页数量 */ int defaultPageSize() default 10; /** * 每页最大数量限制 */ int maxPageSize() default 100; /** * 是否进行count查询 */ boolean count() default true; /** * count查询的Mapper方法全限定名 */ String countMethod() default ; /** * 是否合理化页码超过最大页时返回最后一页 */ boolean reasonable() default true; /** * 是否支持方法参数传递分页参数 */ boolean supportMethodsArguments() default true; /** * 是否在分页后清理ThreadLocal */ boolean autoClear() default true; /** * 排序字段格式field1,asc;field2,desc */ String orderBy() default ; /** * 是否允许返回空页 */ boolean allowEmptyPage() default false; }2.2 返回结果封装java/** * 统一分页返回结果 */ Data NoArgsConstructor AllArgsConstructor public class PageResultT implements Serializable { private static final long serialVersionUID 1L; /** * 状态码 */ private Integer code; /** * 提示信息 */ private String message; /** * 分页数据 */ private PageDataT data; /** * 成功返回 */ public static T PageResultT success(PageDataT data) { return new PageResult(200, success, data); } /** * 失败返回 */ public static T PageResultT error(String message) { return new PageResult(500, message, null); } } /** * 分页数据封装 */ Data NoArgsConstructor AllArgsConstructor public class PageDataT implements Serializable { private static final long serialVersionUID 1L; /** * 当前页码 */ private int pageNum; /** * 每页数量 */ private int pageSize; /** * 总记录数 */ private long total; /** * 总页数 */ private int pages; /** * 数据列表 */ private ListT list; /** * 是否有上一页 */ private boolean hasPreviousPage; /** * 是否有下一页 */ private boolean hasNextPage; /** * 是否是第一页 */ private boolean isFirstPage; /** * 是否是最后一页 */ private boolean isLastPage; /** * 从PageInfo转换 */ public static T PageDataT fromPageInfo(PageInfoT pageInfo) { PageDataT pageData new PageData(); pageData.setPageNum(pageInfo.getPageNum()); pageData.setPageSize(pageInfo.getPageSize()); pageData.setTotal(pageInfo.getTotal()); pageData.setPages(pageInfo.getPages()); pageData.setList(pageInfo.getList()); pageData.setHasPreviousPage(pageInfo.isHasPreviousPage()); pageData.setHasNextPage(pageInfo.isHasNextPage()); pageData.setFirstPage(pageInfo.isIsFirstPage()); pageData.setLastPage(pageInfo.isIsLastPage()); return pageData; } /** * 从Page转换 */ public static T PageDataT fromPage(PageT page) { PageDataT pageData new PageData(); pageData.setPageNum(page.getPageNum()); pageData.setPageSize(page.getPageSize()); pageData.setTotal(page.getTotal()); pageData.setPages(page.getPages()); pageData.setList(page.getResult()); pageData.setHasPreviousPage(page.getPageNum() 1); pageData.setHasNextPage(page.getPageNum() page.getPages()); pageData.setFirstPage(page.getPageNum() 1); pageData.setLastPage(page.getPageNum().equals(page.getPages())); return pageData; } }2.3 切面实现核心java/** * 分页注解切面 */ Aspect Component Slf4j public class PageQueryAspect { Autowired(required false) private HttpServletRequest request; /** * 定义切点所有被PageQuery注解的方法 */ Pointcut(annotation(com.example.annotation.PageQuery)) public void pageQueryPointcut() { } /** * 环绕通知 */ Around(pageQueryPointcut()) public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature (MethodSignature) joinPoint.getSignature(); Method method signature.getMethod(); PageQuery pageQuery method.getAnnotation(PageQuery.class); // 1. 获取分页参数 PageParam pageParam extractPageParam(joinPoint, pageQuery); // 2. 设置分页参数到ThreadLocal setPageHelperParams(pageParam); try { // 3. 执行原方法 Object result joinPoint.proceed(); // 4. 处理返回结果 return processResult(result, pageParam); } finally { // 5. 清理ThreadLocal if (pageQuery.autoClear()) { PageHelper.clearPage(); } } } /** * 提取分页参数 */ private PageParam extractPageParam(ProceedingJoinPoint joinPoint, PageQuery pageQuery) { PageParam pageParam new PageParam(); // 从请求参数获取 if (request ! null) { extractFromRequest(pageParam, pageQuery); } // 从方法参数获取 if (pageQuery.supportMethodsArguments()) { extractFromMethodArgs(joinPoint, pageParam, pageQuery); } // 设置默认值 if (pageParam.getPageNum() null) { pageParam.setPageNum(pageQuery.defaultPageNum()); } if (pageParam.getPageSize() null) { pageParam.setPageSize(pageQuery.defaultPageSize()); } // 验证和限制 validateAndLimit(pageParam, pageQuery); // 设置其他参数 pageParam.setCount(pageQuery.count()); pageParam.setReasonable(pageQuery.reasonable()); pageParam.setOrderBy(pageQuery.orderBy()); return pageParam; } /** * 从HTTP请求中提取参数 */ private void extractFromRequest(PageParam pageParam, PageQuery pageQuery) { String pageNumStr request.getParameter(pageQuery.pageNum()); String pageSizeStr request.getParameter(pageQuery.pageSize()); String orderBy request.getParameter(orderBy); if (StringUtils.isNotBlank(pageNumStr)) { pageParam.setPageNum(convertToInteger(pageNumStr, pageQuery.defaultPageNum())); } if (StringUtils.isNotBlank(pageSizeStr)) { pageParam.setPageSize(convertToInteger(pageSizeStr, pageQuery.defaultPageSize())); } if (StringUtils.isNotBlank(orderBy)) { pageParam.setOrderBy(orderBy); } } /** * 从方法参数中提取 */ private void extractFromMethodArgs(ProceedingJoinPoint joinPoint, PageParam pageParam, PageQuery pageQuery) { Object[] args joinPoint.getArgs(); String[] paramNames ((MethodSignature) joinPoint.getSignature()).getParameterNames(); for (int i 0; i args.length; i) { String paramName paramNames[i]; Object arg args[i]; // 通过参数名匹配 if (pageQuery.pageNum().equals(paramName) arg ! null) { pageParam.setPageNum(convertToInteger(arg.toString(), null)); } if (pageQuery.pageSize().equals(paramName) arg ! null) { pageParam.setPageSize(convertToInteger(arg.toString(), null)); } // 如果参数是PageParam对象 if (arg instanceof PageParam) { PageParam param (PageParam) arg; if (param.getPageNum() ! null) { pageParam.setPageNum(param.getPageNum()); } if (param.getPageSize() ! null) { pageParam.setPageSize(param.getPageSize()); } if (StringUtils.isNotBlank(param.getOrderBy())) { pageParam.setOrderBy(param.getOrderBy()); } } } } /** * 设置PageHelper参数 */ private void setPageHelperParams(PageParam pageParam) { PageHelper.startPage( pageParam.getPageNum(), pageParam.getPageSize() ); // 设置是否进行count查询 if (!pageParam.isCount()) { PageHelper.getLocalPage().setCount(false); } // 设置合理化 if (pageParam.isReasonable()) { PageHelper.getLocalPage().setReasonable(true); } // 设置排序 if (StringUtils.isNotBlank(pageParam.getOrderBy())) { PageHelper.orderBy(pageParam.getOrderBy()); } } /** * 处理返回结果 */ private Object processResult(Object result, PageParam pageParam) { // 如果方法返回的是List自动包装成PageInfo if (result instanceof List) { List? list (List?) result; // 如果是Page对象PageHelper返回的 if (!list.isEmpty() list.get(0) instanceof Page) { Page? page (Page?) list.get(0); return PageData.fromPage(page); } // 普通List使用PageInfo包装 PageInfo? pageInfo new PageInfo(list); // 如果不进行count查询需要特殊处理 if (!pageParam.isCount()) { pageInfo.setTotal(-1L); pageInfo.setPages(-1); } return PageData.fromPageInfo(pageInfo); } // 如果已经是包装好的结果直接返回 if (result instanceof PageData || result instanceof PageResult) { return result; } // 其他类型直接返回 return result; } /** * 验证和限制分页参数 */ private void validateAndLimit(PageParam pageParam, PageQuery pageQuery) { // 页码最小为1 if (pageParam.getPageNum() 1) { pageParam.setPageNum(1); } // 每页数量限制 if (pageParam.getPageSize() pageQuery.maxPageSize()) { pageParam.setPageSize(pageQuery.maxPageSize()); } if (pageParam.getPageSize() 1) { pageParam.setPageSize(pageQuery.defaultPageSize()); } } private Integer convertToInteger(String str, Integer defaultValue) { try { return Integer.parseInt(str); } catch (NumberFormatException e) { return defaultValue; } } } /** * 分页参数封装类 */ Data public class PageParam { private Integer pageNum; private Integer pageSize; private Boolean count true; private Boolean reasonable true; private String orderBy; }2.4 增强功能实现java/** * 分页注解增强功能切面 */ Aspect Component Slf4j public class PageQueryEnhanceAspect { /** * 多数据源分页支持 */ Around(annotation(pageQuery)) public Object handleMultiDataSource(ProceedingJoinPoint joinPoint, PageQuery pageQuery) throws Throwable { String dataSourceKey determineDataSource(joinPoint, pageQuery); if (StringUtils.isNotBlank(dataSourceKey)) { DynamicDataSourceContextHolder.setDataSourceKey(dataSourceKey); try { return joinPoint.proceed(); } finally { DynamicDataSourceContextHolder.clearDataSourceKey(); } } return joinPoint.proceed(); } /** * 分页查询缓存支持 */ Around(annotation(pageQuery)) public Object handlePageCache(ProceedingJoinPoint joinPoint, PageQuery pageQuery) throws Throwable { // 生成缓存key String cacheKey generateCacheKey(joinPoint, pageQuery); // 尝试从缓存获取 Object cachedResult getFromCache(cacheKey); if (cachedResult ! null) { return cachedResult; } // 执行查询 Object result joinPoint.proceed(); // 存入缓存 cacheResult(cacheKey, result, pageQuery); return result; } /** * 分页查询性能监控 */ Around(annotation(pageQuery)) public Object monitorPagePerformance(ProceedingJoinPoint joinPoint, PageQuery pageQuery) throws Throwable { long startTime System.currentTimeMillis(); String methodName joinPoint.getSignature().toShortString(); try { return joinPoint.proceed(); } finally { long cost System.currentTimeMillis() - startTime; log.info(分页查询方法 {} 执行耗时: {}ms, methodName, cost); // 慢查询告警 if (cost 1000) { // 超过1秒视为慢查询 log.warn(慢分页查询告警: {}, 耗时: {}ms, methodName, cost); // 可以发送告警通知 } } } private String determineDataSource(ProceedingJoinPoint joinPoint, PageQuery pageQuery) { // 根据业务规则确定数据源 // 可以从注解参数、方法参数、请求头等获取 return null; } private String generateCacheKey(ProceedingJoinPoint joinPoint, PageQuery pageQuery) { StringBuilder keyBuilder new StringBuilder(); // 类名方法名 keyBuilder.append(joinPoint.getSignature().getDeclaringTypeName()) .append(.) .append(joinPoint.getSignature().getName()); // 方法参数 Object[] args joinPoint.getArgs(); if (args ! null args.length 0) { keyBuilder.append(:); for (Object arg : args) { if (arg ! null) { keyBuilder.append(arg.toString()).append(,); } } } // 分页参数从请求或ThreadLocal获取 Page? page PageHelper.getLocalPage(); if (page ! null) { keyBuilder.append(:page).append(page.getPageNum()) .append(:size).append(page.getPageSize()); } return MD5Util.md5(keyBuilder.toString()); } private Object getFromCache(String cacheKey) { // 从Redis或其他缓存获取 return null; } private void cacheResult(String cacheKey, Object result, PageQuery pageQuery) { // 存入缓存可以设置不同的过期策略 } }三、高级特性实现3.1 动态排序支持java/** * 动态排序解析器 */ Component public class OrderByParser { /** * 解析排序参数 * 支持格式field1,asc;field2,desc 或 field1 asc, field2 desc */ public String parse(String orderBy) { if (StringUtils.isBlank(orderBy)) { return ; } // 去除多余空格 orderBy orderBy.trim(); // 支持多种分隔符 String[] items; if (orderBy.contains(;)) { items orderBy.split(;); } else if (orderBy.contains(,) orderBy.contains( )) { // 处理 field1 asc, field2 desc 格式 items orderBy.split(,); } else { items new String[]{orderBy}; } ListString orderList new ArrayList(); for (String item : items) { if (StringUtils.isBlank(item)) { continue; } item item.trim(); String[] parts item.split(\\s); if (parts.length 1) { // 默认升序 orderList.add(parts[0] asc); } else if (parts.length 2) { // 验证排序方向 String direction parts[1].toLowerCase(); if (asc.equals(direction) || desc.equals(direction)) { orderList.add(parts[0] direction); } else { throw new IllegalArgumentException(不支持的排序方向: direction); } } } return String.join(, , orderList); } /** * 安全处理排序字段防止SQL注入 */ public String safeParse(String orderBy, MapString, String allowedFields) { String parsed parse(orderBy); if (StringUtils.isBlank(parsed)) { return ; } String[] items parsed.split(,); ListString safeItems new ArrayList(); for (String item : items) { item item.trim(); String[] parts item.split(\\s); if (parts.length 2) { String field parts[0]; String direction parts[1]; // 检查字段是否允许排序 if (allowedFields.containsKey(field)) { String dbField allowedFields.get(field); safeItems.add(dbField direction); } } } return String.join(, , safeItems); } }3.2 分页参数验证器java/** * 分页参数验证器 */ Component public class PageParamValidator { /** * 验证分页参数 */ public void validate(PageParam pageParam, PageQuery pageQuery) { // 验证页码 if (pageParam.getPageNum() null || pageParam.getPageNum() 1) { throw new IllegalArgumentException(页码必须大于0); } // 验证每页数量 if (pageParam.getPageSize() null || pageParam.getPageSize() 1) { throw new IllegalArgumentException(每页数量必须大于0); } if (pageParam.getPageSize() pageQuery.maxPageSize()) { throw new IllegalArgumentException( String.format(每页数量不能超过%s, pageQuery.maxPageSize()) ); } // 验证排序字段 if (StringUtils.isNotBlank(pageParam.getOrderBy())) { validateOrderBy(pageParam.getOrderBy()); } } /** * 验证排序字段 */ private void validateOrderBy(String orderBy) { // 简单的SQL注入检查 String[] dangerousKeywords { select, insert, update, delete, drop, truncate, union, join, script, , }; for (String keyword : dangerousKeywords) { if (orderBy.toLowerCase().contains(keyword)) { throw new IllegalArgumentException(非法的排序参数); } } } /** * 验证最大页码限制 */ public void validateMaxPage(PageData? pageData, int maxPage) { if (pageData.getPageNum() maxPage maxPage 0) { throw new IllegalArgumentException( String.format(最大页码不能超过%s, maxPage) ); } } }3.3 分页结果增强器java/** * 分页结果增强器 */ Component public class PageResultEnhancer { /** * 添加分页导航信息 */ public T PageDataT addNavigation(PageDataT pageData) { if (pageData null) { return null; } // 计算导航页码 ListInteger navigatePageNums calculateNavigatePages( pageData.getPageNum(), pageData.getPages() ); // 这里可以通过反射或其他方式设置到pageData // 或者创建增强的PageData对象 return pageData; } /** * 添加汇总信息 */ public T PageDataT addSummary(PageDataT pageData, String summaryKey, Object summaryValue) { // 可以通过Map扩展字段的方式添加汇总信息 MapString, Object extra new HashMap(); extra.put(summaryKey, summaryValue); // 设置额外信息 // pageData.setExtra(extra); return pageData; } /** * 计算导航页码 */ private ListInteger calculateNavigatePages(int pageNum, int totalPages) { ListInteger navigatePages new ArrayList(); // 导航页码数量 int navSize 8; // 当总页数小于等于导航页码数量时 if (totalPages navSize) { for (int i 1; i totalPages; i) { navigatePages.add(i); } } else { // 当前页在导航中的位置 int startNum pageNum - navSize / 2; int endNum pageNum navSize / 2; if (startNum 1) { startNum 1; endNum navSize; } if (endNum totalPages) { endNum totalPages; startNum totalPages - navSize 1; } for (int i startNum; i endNum; i) { navigatePages.add(i); } } return navigatePages; } /** * 转换DTO */ public T, R PageDataR convert(PageDataT source, FunctionT, R converter) { if (source null) { return null; } ListR convertedList source.getList().stream() .map(converter) .collect(Collectors.toList()); PageDataR target new PageData(); target.setPageNum(source.getPageNum()); target.setPageSize(source.getPageSize()); target.setTotal(source.getTotal()); target.setPages(source.getPages()); target.setList(convertedList); target.setHasPreviousPage(source.isHasPreviousPage()); target.setHasNextPage(source.isHasNextPage()); target.setFirstPage(source.isFirstPage()); target.setLastPage(source.isLastPage()); return target; } }四、使用示例4.1 基础使用javaRestController RequestMapping(/api/users) public class UserController { Autowired private UserService userService; /** * 基础分页查询 */ GetMapping PageQuery public PageResultUser listUsers() { // 方法体可以为空切面会自动处理 // 或者返回Mapper查询结果 return PageResult.success(null); } /** * 自定义分页参数名 */ GetMapping(/custom) PageQuery(pageNum current, pageSize size, maxPageSize 50) public PageResultUser listUsersCustom() { return PageResult.success(null); } /** * 带排序的分页查询 */ GetMapping(/sorted) PageQuery(orderBy createTime,desc;username,asc) public PageResultUser listUsersSorted() { return PageResult.success(null); } }4.2 Service层使用javaService public class UserServiceImpl implements UserService { Autowired private UserMapper userMapper; /** * Service层分页查询 */ Override PageQuery public PageDataUser queryUsers(UserQuery query) { // 这里直接返回null切面会拦截并处理分页 // 实际查询由切面后的Mapper调用完成 return null; } /** * 复杂分页查询 */ Override PageQuery(countMethod com.example.mapper.UserMapper.countByQuery) public PageDataUserDTO queryUsersComplex(UserQuery query) { ListUser users userMapper.selectByQuery(query); // 转换为DTO return convertToDTO(users); } /** * 多表关联分页查询 */ Override PageQuery public PageDataUserWithDeptDTO queryUsersWithDept(UserQuery query) { // 使用PageQuery注解自动分页 return null; } }4.3 Mapper层配置xml!-- UserMapper.xml -- mapper namespacecom.example.mapper.UserMapper !-- 基础分页查询 -- select idselectByPage resultTypeUser SELECT * FROM user WHERE status 1 if testusername ! null and username ! AND username LIKE CONCAT(%, #{username}, %) /if if testemail ! null and email ! AND email LIKE CONCAT(%, #{email}, %) /if ORDER BY create_time DESC /select !-- 自定义count查询 -- select idcountByQuery parameterTypeUserQuery resultTypelong SELECT COUNT(*) FROM user WHERE status 1 if testusername ! null and username ! AND username LIKE CONCAT(%, #{username}, %) /if if testemail ! null and email ! AND email LIKE CONCAT(%, #{email}, %) /if /select !-- 多表关联分页查询 -- select idselectWithDept resultMapUserWithDeptResult SELECT u.*, d.dept_name, d.dept_code FROM user u LEFT JOIN department d ON u.dept_id d.id WHERE u.status 1 if testdeptName ! null and deptName ! AND d.dept_name LIKE CONCAT(%, #{deptName}, %) /if ORDER BY u.create_time DESC /select resultMap idUserWithDeptResult typeUserWithDeptDTO id propertyid columnid/ result propertyusername columnusername/ result propertyemail columnemail/ result propertydeptName columndept_name/ result propertydeptCode columndept_code/ /resultMap /mapper五、高级应用场景5.1 分布式环境下的分页优化java/** * 分布式分页处理器 */ Component public class DistributedPageHandler { Autowired private RedisTemplateString, Object redisTemplate; /** * 处理游标分页适用于大数据量 */ public T PageDataT cursorPage(CursorPageQuery query, FunctionCursorPageQuery, ListT dataFetcher) { String cursorKey query.getCursorKey(); Long cursor getCursor(cursorKey); if (cursor null) { cursor 0L; } query.setCursor(cursor); ListT data dataFetcher.apply(query); // 更新游标 if (!data.isEmpty()) { T lastItem data.get(data.size() - 1); Long newCursor extractCursor(lastItem); setCursor(cursorKey, newCursor); // 设置下一次查询的游标 query.setNextCursor(newCursor); } PageDataT pageData new PageData(); pageData.setList(data); pageData.setPageSize(data.size()); pageData.setHasNextPage(data.size() query.getPageSize()); return pageData; } /** * 基于时间戳的分页 */ public T PageDataT timeBasedPage(TimePageQuery query, FunctionTimePageQuery, ListT dataFetcher) { ListT data dataFetcher.apply(query); // 获取最小时间戳作为下一次查询的条件 if (!data.isEmpty()) { T lastItem data.get(data.size() - 1); Long minTimestamp extractTimestamp(lastItem); query.setLastTimestamp(minTimestamp); } PageDataT pageData new PageData(); pageData.setList(data); pageData.setPageSize(data.size()); pageData.setHasNextPage(data.size() query.getPageSize()); return pageData; } private Long getCursor(String cursorKey) { Object value redisTemplate.opsForValue().get(cursorKey); return value ! null ? Long.parseLong(value.toString()) : null; } private void setCursor(String cursorKey, Long cursor) { redisTemplate.opsForValue().set(cursorKey, cursor.toString(), 1, TimeUnit.HOURS); } private Long extractCursor(Object item) { // 根据业务逻辑提取游标值 return null; } private Long extractTimestamp(Object item) { // 根据业务逻辑提取时间戳 return null; } }5.2 分页缓存策略java/** * 分页缓存管理器 */ Component Slf4j public class PageCacheManager { Autowired private RedisTemplateString, Object redisTemplate; /** * 分页查询缓存键生成策略 */ public String generateCacheKey(String prefix, Object... params) { StringBuilder keyBuilder new StringBuilder(prefix); for (Object param : params) { if (param ! null) { keyBuilder.append(:).append(param.toString()); } } // 添加分页参数 Page? page PageHelper.getLocalPage(); if (page ! null) { keyBuilder.append(:page).append(page.getPageNum()) .append(:size).append(page.getPageSize()) .append(:order).append(page.getOrderBy()); } return keyBuilder.toString(); } /** * 获取缓存数据 */ SuppressWarnings(unchecked) public T PageDataT getPageFromCache(String cacheKey) { try { Object cached redisTemplate.opsForValue().get(cacheKey); if (cached ! null) { return (PageDataT) cached; } } catch (Exception e) { log.error(从缓存获取分页数据失败, e); } return null; } /** * 缓存分页数据 */ public T void cachePageData(String cacheKey, PageDataT pageData, long ttlSeconds) { try { redisTemplate.opsForValue().set(cacheKey, pageData, ttlSeconds, TimeUnit.SECONDS); } catch (Exception e) { log.error(缓存分页数据失败, e); } } /** * 清除相关缓存 */ public void clearRelatedCache(String pattern) { SetString keys redisTemplate.keys(pattern *); if (keys ! null !keys.isEmpty()) { redisTemplate.delete(keys); } } /** * 批量缓存预热 */ public T void warmUpCache(String keyPrefix, ListT allData, int pageSize) { int total allData.size(); int totalPages (total pageSize - 1) / pageSize; for (int i 1; i totalPages; i) { int fromIndex (i - 1) * pageSize; int toIndex Math.min(i * pageSize, total); ListT pageData allData.subList(fromIndex, toIndex); PageDataT pageResult new PageData(); pageResult.setPageNum(i); pageResult.setPageSize(pageSize); pageResult.setTotal(total); pageResult.setPages(totalPages); pageResult.setList(pageData); String cacheKey keyPrefix :page i :size pageSize; cachePageData(cacheKey, pageResult, 3600); // 缓存1小时 } } }5.3 分页查询性能监控java/** * 分页查询监控器 */ Component Slf4j public class PageQueryMonitor { private static final ThreadLocalPageQueryMetrics metricsThreadLocal new ThreadLocal(); /** * 开始监控 */ public void startMonitor(String methodName) { PageQueryMetrics metrics new PageQueryMetrics(); metrics.setMethodName(methodName); metrics.setStartTime(System.currentTimeMillis()); metricsThreadLocal.set(metrics); } /** * 记录SQL执行 */ public void recordSqlExecution(String sql, long cost) { PageQueryMetrics metrics metricsThreadLocal.get(); if (metrics ! null) { metrics.setSqlCost(cost); metrics.setSql(sql); } } /** * 结束监控并记录日志 */ public void endMonitor(PageData? pageData) { PageQueryMetrics metrics metricsThreadLocal.get(); if (metrics ! null) { metrics.setEndTime(System.currentTimeMillis()); metrics.setTotalCost(System.currentTimeMillis() - metrics.getStartTime()); if (pageData ! null) { metrics.setPageNum(pageData.getPageNum()); metrics.setPageSize(pageData.getPageSize()); metrics.setTotal(pageData.getTotal()); metrics.setDataSize(pageData.getList() ! null ? pageData.getList().size() : 0); } // 记录监控日志 logMetrics(metrics); // 慢查询告警 checkSlowQuery(metrics); metricsThreadLocal.remove(); } } /** * 记录监控指标 */ private void logMetrics(PageQueryMetrics metrics) { log.info(分页查询监控 - 方法: {}, 总耗时: {}ms, SQL耗时: {}ms, 页码: {}, 页大小: {}, 总数: {}, 数据量: {}, metrics.getMethodName(), metrics.getTotalCost(), metrics.getSqlCost(), metrics.getPageNum(), metrics.getPageSize(), metrics.getTotal(), metrics.getDataSize()); } /** * 检查慢查询 */ private void checkSlowQuery(PageQueryMetrics metrics) { if (metrics.getTotalCost() 1000) { // 超过1秒 log.warn(慢分页查询告警 - 方法: {}, 耗时: {}ms, metrics.getMethodName(), metrics.getTotalCost()); // 发送告警通知 sendAlert(metrics); } } /** * 发送告警 */ private void sendAlert(PageQueryMetrics metrics) { // 实现告警逻辑如发送邮件、钉钉、短信等 } } /** * 分页查询监控指标 */ Data class PageQueryMetrics { private String methodName; private String sql; private long startTime; private long endTime; private long totalCost; private long sqlCost; private int pageNum; private int pageSize; private long total; private int dataSize; }六、Spring Boot集成配置6.1 自动配置类java/** * 分页注解自动配置 */ Configuration ConditionalOnClass({PageHelper.class, PageQuery.class}) EnableAspectJAutoProxy AutoConfigureAfter(PageHelperAutoConfiguration.class) public class PageQueryAutoConfiguration { Bean ConditionalOnMissingBean public PageQueryAspect pageQueryAspect() { return new PageQueryAspect(); } Bean ConditionalOnMissingBean public PageQueryEnhanceAspect pageQueryEnhanceAspect() { return new PageQueryEnhanceAspect(); } Bean ConditionalOnMissingBean public PageParamValidator pageParamValidator() { return new PageParamValidator(); } Bean ConditionalOnMissingBean public OrderByParser orderByParser() { return new OrderByParser(); } Bean ConditionalOnMissingBean public PageResultEnhancer pageResultEnhancer() { return new PageResultEnhancer(); } Bean ConditionalOnMissingBean public PageCacheManager pageCacheManager() { return new PageCacheManager(); } Bean ConditionalOnMissingBean public PageQueryMonitor pageQueryMonitor() { return new PageQueryMonitor(); } Bean ConditionalOnMissingBean public DistributedPageHandler distributedPageHandler() { return new DistributedPageHandler(); } }6.2 配置文件yaml# application.yml page-query: enabled: true default-page-num: 1 default-page-size: 20 max-page-size: 100 reasonable: true support-methods-arguments: true auto-clear: true # 监控配置 monitor: enabled: true slow-query-threshold: 1000 # 慢查询阈值(ms) # 缓存配置 cache: enabled: false ttl: 300 # 缓存时间(秒) key-prefix: page:cache # 排序配置 order-by: allowed-fields: - id - createTime - updateTime - username - email七、最佳实践与注意事项7.1 性能优化建议合理使用count查询javaPageQuery(count false) // 大数据量时禁用count索引优化确保排序字段有索引复合查询条件建立联合索引分页深度限制java// 限制最大页码 if (pageNum 100) { throw new BusinessException(页码过大); }7.2 安全注意事项SQL注入防护java// 使用安全的排序字段映射 MapString, String safeFields new HashMap(); safeFields.put(username, u.username); safeFields.put(createTime, u.create_time); String safeOrderBy orderByParser.safeParse(orderBy, safeFields);参数校验javaValidated public PageDataUser query(Valid UserQuery query) { // ... }7.3 异常处理java/** * 分页异常处理器 */ ControllerAdvice Slf4j public class PageExceptionHandler { /** * 分页参数异常处理 */ ExceptionHandler(IllegalArgumentException.class) ResponseBody public PageResult? handlePageParamException(IllegalArgumentException e) { log.warn(分页参数异常: {}, e.getMessage()); return PageResult.error(分页参数错误: e.getMessage()); } /** * 分页查询异常处理 */ ExceptionHandler(PageQueryException.class) ResponseBody public PageResult? handlePageQueryException(PageQueryException e) { log.error(分页查询异常, e); return PageResult.error(分页查询失败: e.getMessage()); } } /** * 自定义分页异常 */ public class PageQueryException extends RuntimeException { public PageQueryException(String message) { super(message); } public PageQueryException(String message, Throwable cause) { super(message, cause); } }八、总结8.1 核心优势代码简洁通过注解减少模板代码功能强大支持排序、缓存、监控等高级特性易于扩展通过切面和组件化设计支持自定义扩展性能优化内置多种性能优化策略8.2 适用场景RESTful API的分页查询管理后台的数据列表展示大数据量的分批处理多条件复杂查询的分页8.3 扩展方向支持更多数据库适配Oracle、SQL Server等前端集成自动生成分页组件代码查询优化智能选择分页策略监控告警集成APM系统