Spring PageImpl含冗余内容,能否自定义CustomPageImpl及最佳实践?
解决方案与最佳实践
针对Spring默认Page类型返回JSON存在冗余字段的问题,既可以自定义Page实现类(比如CustomPageImpl),也可以通过DTO按需传输数据,以下是具体实现方案和最佳实践:
一、自定义Page实现类(CustomPageImpl)
可以通过实现Spring的Page<T>接口,只保留业务需要的核心字段,过滤冗余内容。
实现代码示例
public class CustomPageImpl<T> implements Page<T> { private final List<T> content; private final long totalElements; private final int totalPages; private final int size; private final int number; private final int numberOfElements; // 构造方法,传入核心分页参数 public CustomPageImpl(List<T> content, long totalElements, int totalPages, int size, int number) { this.content = content; this.totalElements = totalElements; this.totalPages = totalPages; this.size = size; this.number = number; this.numberOfElements = content.size(); } // 实现Page接口的必要方法,按需返回逻辑,冗余字段返回默认值 @Override public int getNumber() { return number; } @Override public int getSize() { return size; } @Override public int getNumberOfElements() { return numberOfElements; } @Override public List<T> getContent() { return content; } @Override public boolean hasContent() { return !content.isEmpty(); } @Override public Sort getSort() { return Sort.unsorted(); } // 不需要排序字段时返回空Sort @Override public boolean isFirst() { return number == 0; } @Override public boolean isLast() { return number == totalPages - 1; } @Override public boolean hasNext() { return number < totalPages - 1; } @Override public boolean hasPrevious() { return number > 0; } @Override public Pageable nextPageable() { return hasNext() ? PageRequest.of(number + 1, size) : Pageable.unpaged(); } @Override public Pageable previousPageable() { return hasPrevious() ? PageRequest.of(number - 1, size) : Pageable.unpaged(); } @Override public Iterator<T> iterator() { return content.iterator(); } @Override public int getTotalPages() { return totalPages; } @Override public long getTotalElements() { return totalElements; } @Override public boolean isEmpty() { return content.isEmpty(); } }
使用方式
在Service层查询得到默认Page对象后,转换为自定义的CustomPageImpl:
Page<User> defaultPage = userRepository.findAll(pageable); CustomPageImpl<User> customPage = new CustomPageImpl<>( defaultPage.getContent(), defaultPage.getTotalElements(), defaultPage.getTotalPages(), defaultPage.getSize(), defaultPage.getNumber() ); return customPage;
二、使用DTO传输(推荐最佳实践)
这种方式更符合分层架构设计,彻底分离业务数据与传输数据,灵活度更高。
实现步骤
- 创建分页DTO类,仅保留需要的字段:
public class PageDTO<T> { private List<T> content; private long totalElements; private int totalPages; private int pageSize; private int pageNumber; private int numberOfElements; // 构造方法 public PageDTO(List<T> content, long totalElements, int totalPages, int pageSize, int pageNumber) { this.content = content; this.totalElements = totalElements; this.totalPages = totalPages; this.pageSize = pageSize; this.pageNumber = pageNumber; this.numberOfElements = content.size(); } // 按需生成getter/setter方法 }
- 转换逻辑(Service/Controller层):
Page<User> defaultPage = userRepository.findAll(pageable); // 若需要对content中的对象也做DTO转换,可结合MapStruct等工具简化 List<UserDTO> userDTOs = userMapper.toUserDTOList(defaultPage.getContent()); PageDTO<UserDTO> pageDTO = new PageDTO<>( userDTOs, defaultPage.getTotalElements(), defaultPage.getTotalPages(), defaultPage.getSize(), defaultPage.getNumber() ); return pageDTO;
三、方案对比与选择
- 自定义PageImpl:适合需要与Spring Data生态深度绑定的场景,但需要实现接口所有方法,代码冗余,灵活度有限。
- DTO传输:优先推荐,完全按需定义字段,降低业务层与传输层耦合,后续扩展、维护更方便,配合MapStruct等工具可大幅减少转换代码。
另外,也可以通过Jackson的@JsonIgnoreProperties注解直接忽略默认Page类中的冗余字段,但这种硬编码方式不够灵活,仅适合临时快速屏蔽字段,不推荐作为长期方案。
内容的提问来源于stack exchange,提问作者Mahesh
相关产品推荐
相关产品推荐

