✨✨个人主页:沫洺的主页
📚📚系列专栏: 📖 JavaWeb专栏📖 JavaSE专栏 📖 Java基础专栏📖vue3专栏
📖MyBatis专栏📖Spring专栏📖SpringMVC专栏📖SpringBoot专栏
💖💖如果文章对你有所帮助请留下三连✨✨
在后端向前端返回统一格式data里一般按照需求是要包含分页信息的,比如总条数和按照每页的条数去返回对应的总页数等
项目创建参考SpringBoot专栏里:
父项目scm-root的pom.xml
4.0.0 org.springframework.boot spring-boot-starter-parent 2.7.4 com.moming scm-root 14-SNAPSHOT pom 1.8 scm-authority scm-app scm-dao scm-dto scm-entity scm-service scm-api scm-core org.mybatis mybatis 3.5.6 compile cn.hutool hutool-all 5.8.5 数据库
首先实现简单的查询业务
scm-dto模块
pom.xml依赖
org.projectlombok lombok true 创建UserDto实体类
package com.moming.dto;import lombok.Data;@Data public class UserDto {private Integer id;private String username;private String password; }scm-entity模块
pom.xml依赖
org.projectlombok lombok true 创建UserEntity实体类
package com.moming.entity;import lombok.Data;@Data public class UserEntity {private Integer id;private String username;private String password; }scm-dao模块
pom.xml依赖
org.mybatis mybatis compile com.moming scm-entity 14-SNAPSHOT compile 创建UserDao接口
package com.moming.dao;import com.moming.entity.UserEntity; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper public interface UserDao {@Select("select * from tb_user order by id")Listselect();} scm-service模块
pom.xml依赖
org.springframework spring-context cn.hutool hutool-all com.moming scm-dao 14-SNAPSHOT compile com.moming scm-dto 14-SNAPSHOT compile 创建UserService业务类
package com.moming.service;import cn.hutool.core.bean.BeanUtil; import com.moming.dao.UserDao; import com.moming.dto.UserDto; import com.moming.entity.UserEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;import java.util.List;@Service public class UserService {@Autowiredprivate UserDao userDao;public Listselect(){List entityList = userDao.select();List userDtos = BeanUtil.copyToList(entityList, UserDto.class);return userDtos;} } scm-app模块
pom.xml依赖
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.data spring-data-jdbc mysql mysql-connector-java runtime com.alibaba druid-spring-boot-starter 1.2.9 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.4 com.moming scm-service 14-SNAPSHOT compile 创建controller/UserController
package com.moming.controller;import com.moming.dto.UserDto; import com.moming.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController @RequestMapping("/api/user") public class UserController {@Autowiredprivate UserService userService;@GetMapping("/select")public Listselect(){return userService.select();} } 创建resources/application.yml
#数据库连接信息配置 spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/db5?useSSL=false&useServerPrepStmts=trueusername: rootpassword: 123456druid:initial-size: 10 # 初始化时建立物理连接的个数min-idle: 10 # 最小连接池数量maxActive: 200 # 最大连接池数量maxWait: 60000 # 获取连接时最大等待时间,单位毫秒 #映射文件所在位置 mybatis:mapper-locations: classpath:mapper/*Mapper.xml#别名type-aliases-package: com.moming.entity#配置日志级别 logging:level:com.moming: debug启动类App运行后调用接口测试
scm-dto模块
创建ResponseDto实体类
package com.moming.dto;import lombok.Data;@Data public class ResponseDto {private int code;private String message;private Object data; }scm-app模块
创建advice/MyResponseAdvice
package com.moming.advice;import cn.hutool.core.lang.Dict; import cn.hutool.json.JSONUtil; import com.moming.dto.PageInfo; import com.moming.dto.ResponseDto; import com.moming.local.PageInfoLocal; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;@RestControllerAdvice(basePackages = MyResponseAdvice.BASEPACKAGES) public class MyResponseAdvice implements ResponseBodyAdvice接口测试查看效果
业务需求:
不改变原有的代码,在返回的data中呈现总页数(pages)和(total)
分析:
首先完整的查询业务在上面已经写好了,根据业务需求要在不改变原有的代码的基础之上进行业务增强,就要使用AOP技术来实现,另外要获取总页数,那么在url接口上就需要传递每页多少条的参数(pageSize),既然实现了每页条数,那么同样的就可以传递第几页(pageNum)去动态查询某页的数据,接下来就是怎么去获取url传递的参数,可以通过AOP中的环绕通知,在环绕前使用RequestContextHolder对象去获取ServletRequestAttributes对象,然后通过它去获取HttpServletRequest对象,最后调用getParameter获取参数,获取参数之后使用PageHelper调用startPage方法会将目标方法返回值的类型改为Page类型(底层实现了ArrayList),而Page底层提供了total,pages等属性的get方法,因为属性有很多所以这里需要处理以下,通过我们自定义的PageInfo的set方法获取total,pages的值,然后使用ThreadLocal(本地线程)的机制去实现线程安全问题,ThreadLocal中填充的的是当前线程的变量,该变量对其他线程而言是封闭且隔离的(也就是线程安全的),ThreadLocal为变量在每个线程中创建了一个副本,这样每个线程就可以访问自己内部的副本变量,通过这种机制我们给当前线程脑门上贴一个变量(pageInfo),然后再获取当前线程脑门上的变量(pageInfo),最后移除当前线程脑门上的变量(pageInfo)
scm-app模块
pom.xml添加依赖
AOP坐标依赖
org.springframework.boot spring-boot-starter-aop 第三方pagehelper分页坐标依赖
com.github.pagehelper pagehelper-spring-boot-starter 1.4.5 相关模块导入依赖
com.moming scm-core 14-SNAPSHOT compile 创建local/PageInfoLocal
package com.moming.local;import com.moming.dto.PageInfo;public class PageInfoLocal {private static ThreadLocalthreadLocal = new InheritableThreadLocal<>();//给当前线程头上贴一个变量 pageinfopublic static void set(PageInfo pageInfo){threadLocal.set(pageInfo);}//获取当前线程头上的变量 pageinfopublic static PageInfo get(){return threadLocal.get();}//移除当前线程头上的变量 pageinfopublic static void remove(){threadLocal.remove();} } 创建aop/DaoAop
package com.moming.aop;import cn.hutool.core.util.ObjectUtil; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; import com.moming.dto.PageInfo; import com.moming.local.PageInfoLocal; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;@Component @Aspect public class DaoAop {//通过注解方式定义切入点@Pointcut("@annotation(com.moming.annotation.PageCut)")public void point(){}//环绕通知@Around("point()")public Object around(ProceedingJoinPoint pjp) throws Throwable {//获取url传递的参数ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();String pageNum = request.getParameter("pageNum");String pageSize = request.getParameter("pageSize");//如果只传递pageSize,则默认pageNum为1if( ObjectUtil.isNotEmpty(pageSize)){int page_num =1;if(ObjectUtil.isNotEmpty(pageNum)){page_num = Integer.valueOf(pageNum);}int page_size = Integer.valueOf(pageSize);PageHelper.startPage(page_num,page_size);}//目标方法Listselect()---Object result = pjp.proceed();//如果目标方法的返回值类型为Page,则将pageInfo对象贴在当前线程脑门上if(result instanceof Page){Page page = (Page) result;//使用自定义的PageInfo对象获取需要的分页信息PageInfo pageInfo = new PageInfo();pageInfo.setPages(page.getPages());pageInfo.setTotal(page.getTotal());PageInfoLocal.set(pageInfo);}return result;} } 在通知类里对分页信息进行封装
//分页组件扩展开始PageInfo pageInfo = PageInfoLocal.get();try{if(pageInfo!=null){Dict dict = Dict.create().set("pages",pageInfo.getPages()).set("total", pageInfo.getTotal()).set("items",body);responseDto.setData(dict);}else {responseDto.setData(body);}//分页组件扩展结束}finally {//移除当前线程头上的变量标签if(pageInfo!=null){PageInfoLocal.remove();}}advice/MyResponseAdvice
package com.moming.advice;import cn.hutool.core.lang.Dict; import cn.hutool.json.JSONUtil; import com.moming.dto.PageInfo; import com.moming.dto.ResponseDto; import com.moming.local.PageInfoLocal; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;@RestControllerAdvice(basePackages = MyResponseAdvice.BASEPACKAGES) public class MyResponseAdvice implements ResponseBodyAdvicescm-core模块
pom.xml依赖
org.projectlombok lombok true 创建annotation/PageCut注解
package com.moming.annotation;import java.lang.annotation.*;@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface PageCut { }创建dto/PageInfo实体类
package com.moming.dto;import lombok.Data;@Data public class PageInfo {private int pages;private long total; }scm-dao模块
目标方法添加切入点注解
package com.moming.dao;import com.moming.annotation.PageCut; import com.moming.entity.UserEntity; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper public interface UserDao {@PageCut@Select("select * from tb_user order by id")Listselect();} 测试