目录
[手动注册SqlSessionFactory(MyBatisConfig )](#手动注册SqlSessionFactory(MyBatisConfig ))
[PageX 注解](#PageX 注解)
[拦截器注入(PageMethodInterceptorConfig )](#拦截器注入(PageMethodInterceptorConfig ))
一、需求
使用ResponseBodyAdvice做分页处理_骑着蜗牛打天下的博客-CSDN博客
在之前使用过用ResponseBodyAdvice来做分页处理,但是我们还可以结合着Aop中的MethodInterceptor拦截器对分页进一步封装;
使用ResponseBodyAdvice的做法:
这样的做法就是使用ResponseBodyAdvice去拦截controller层返回值,然后对返回的page类型的值进行封装成带有 total、pages、body等;
而本篇的目的是使用aop拦截器去拦截标注切点的方法然后对此方法进行拦截,拦截后就在拦截器内去做 PageHelper.startPage处理,然后在controller层返回时再使用ResponseBodyAdvice拦截,进一步的把数据给封装!
二、代码实现
父pom文件
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!-- <version>3.1.2</version>-->
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.chensir</groupId>
<artifactId>springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot</name>
<description>springboot</description>
<properties>
<java.version>8</java.version>
<hutool.version>5.8.3</hutool.version>
<lombok.version>1.18.24</lombok.version>
<alibaba-sdk.version>2.0.0</alibaba-sdk.version>
</properties>
<packaging>pom</packaging>
<modules>
<module>servlet</module>
<module>spring-interceptor</module>
<module>spring-aop</module>
<module>spring-united-reslut</module>
<module>spring-jdbc</module>
<module>spring-mybatis</module>
<module>spring-mybatis-pageX</module>
</modules>
<dependencyManagement>
<dependencies>
<!--钉钉机器人消息-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibaba-dingtalk-service-sdk</artifactId>
<version>${alibaba-sdk.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--父pom中不要加此依赖,否则依赖clean会报错,原因是父pom中并没指定版本-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-web</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-test</artifactId>-->
<!-- <scope>test</scope>-->
<!-- </dependency>-->
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
pom文件
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.chensir</groupId>
<artifactId>springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>spring-mybatis-pageX</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>2.1.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置文件
XML
spring.datasource.url=jdbc:mysql://localhost/db1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
aop.MethodInterceptor.point = @annotation(com.chensir.annotation.PageX)
手动注册SqlSessionFactory(MyBatisConfig )
java
package com.chensir.config;
import com.github.pagehelper.PageInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.CollectionUtils;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Configuration
@Slf4j
public class MyBatisConfig {
@Autowired
private ConfigurationCustomizer configurationCustomizer;
/**
* 自己注册SqlSessionFactory 目的是增强功能,添加分页插件
* @param ds 代表数据源
* @return
* @throws Exception
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource ds) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
// 设置数据源
factoryBean.setDataSource(ds);
// 把分页插件设置到SqlSessionFactory插件库
factoryBean.setPlugins(new PageInterceptor());
// 配置mapper.xml地址
factoryBean.setMapperLocations(resolveMapperLocations());
//驼峰映射注册到SqlSessionFactory中
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configurationCustomizer.customize(configuration);
factoryBean.setConfiguration(configuration);
// 返回具体实例对象
SqlSessionFactory bean = factoryBean.getObject();
return bean;
}
/**
* 开启驼峰映射
* @return
*/
@Bean
public ConfigurationCustomizer configurationCustomizer(){
return new ConfigurationCustomizer() {
@Override
public void customize(org.apache.ibatis.session.Configuration configuration) {
configuration.setMapUnderscoreToCamelCase(true);
}
};
}
/**
* 查找 xxxMapper.xml
* @return
*/
public Resource[] resolveMapperLocations() {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List<String> mapperLocations = new ArrayList<>();
mapperLocations.add("classpath*:mapper/*Mapper.xml");
// mapperLocations.add("classpath*:com/meiwei/ping/dao/**/*Mapper.xml");
List<Resource> resources = new ArrayList();
if (!CollectionUtils.isEmpty(mapperLocations)) {
for (String mapperLocation : mapperLocations) {
try {
Resource[] mappers = resourceResolver.getResources(mapperLocation);
resources.addAll(Arrays.asList(mappers));
} catch (IOException e) {
log.error("Get myBatis resources happened exception", e);
}
}
}
return resources.toArray(new Resource[resources.size()]);
}
}
对象
实体类Users
java
@Data
public class Users {
private Long id;
private String name;
private Integer age;
private String sex;
private String tel;
// 数据库timestamp类型时间转化java规定格式时间
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
// 接受前端表单传递过来的字符串类型的时间数
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date createTime;
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDateTime updateTime;
}
抽象类AbstractQuery
java
public abstract class AbstractQuery implements Serializable {
private Integer pageNum = 1;
private Integer pageSize = 10;
public Integer getPageNum() {
return pageNum;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
}
查询参数类UsersQuery
java
@Data
public class UsersQuery extends AbstractQuery {
private Long id;
private String name;
private Integer age;
private String sex;
private String tel;
}
三层架构
UsersController
java
@RestController
public class UsersController {
@Resource
private UsersService usersService;
@PostMapping("/pageQuery")
public List selectUsersByPageQuery(@RequestBody UsersQuery usersQuery){
// 使用MethodInterceptor进行分页 @PageX为切点,使用地方开启分页处理
List<Users> usersList = usersService.selectUsersByPageQuery(usersQuery);
return usersList;
}
@GetMapping("/demo1")
public List selectUsersList(Integer pageNum,Integer pageSize) {
// 启用分页,查询第一页,每页3条
PageHelper.startPage(pageNum,pageSize);
//-------------PageInfo方式---------------
// PageInfo<Users> usersPageInfo = new PageInfo<>(usersService.selectUsersList());
// return usersPageInfo;
//-------------PageInfo方式---------------
//-------------Page方式---------------
// List<Users> users = usersService.selectUsersList();
// Page page = (Page) users;
// int pages = page.getPages();
// long total = page.getTotal();
//
//
// Map<String,Object> pageMap = new HashMap<>();
// pageMap.put("pages",pages);
// pageMap.put("total",total);
// pageMap.put("items",users);
// return pageMap;
//-------------Page方式---------------
//-----------使用ResponseBodyAdvice------------
List<Users> users = usersService.selectUsersList();
return users;
//-----------使用ResponseBodyAdvice------------
}
@GetMapping("/demo2")
public Users selectUsers(){
Users users = usersService.selectUsers();
return users;
}
@GetMapping("/demo3")
public Map selectUsersToMap(){
return usersService.selectUsersToMap();
}
}
UsersServiceImpl
java
@Service
public class UsersServiceImpl implements UsersService {
@Resource
private UsersMapper usersMapper;
@Override
public List<Users> selectUsersList() {
return usersMapper.selectUsersList();
}
@Override
public Users selectUsers() {
return usersMapper.selectUsers();
}
@Override
public Map<String, Object> selectUsersToMap() {
return usersMapper.selectUsersToMap();
}
@Override
public List<Users> selectUsersByPageQuery(UsersQuery usersQuery) {
return usersMapper.selectUsersByPageQuery(usersQuery);
}
}
UsersMapper
java
@Mapper
public interface UsersMapper {
List<Users> selectUsersList();
Users selectUsers();
Map<String,Object> selectUsersToMap();
//此接口带有@PageX 拦截器会拦截它 然后做分页处理
@PageX
List<Users> selectUsersByPageQuery(UsersQuery usersQuery);
}
UsersMapper.xml
XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chensir.mapper.UsersMapper">
<select id="selectUsersList" resultType="com.chensir.domain.entity.Users">
select * from users
</select>
<select id="selectUsers" resultType="com.chensir.domain.entity.Users">
select * from users where id = 1
</select>
<select id="selectUsersToMap" resultType="java.util.Map">
select * from users where id = 1
</select>
<select id="selectUsersByPageQuery" resultType="com.chensir.domain.entity.Users">
select * from users
<where>
1=1
<if test="name != null and name != ''">
and name like concat('%', #{name},'%')
</if>
</where>
</select>
</mapper>
PageX 注解
注解来作为切点,使用时,在配置文件中先配置,注入@bean时使用@value取值,然后对带有此注解的方法进行扫描拦截;
java
@Deprecated
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PageX {
}
MyResponseBodyAdvice
java
package com.chensir.advice;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.github.pagehelper.Page;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
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;
import javax.servlet.http.HttpServletRequest;
// 拦截范围为 com.chensir.controller包下内容
@RestControllerAdvice(basePackages = {"com.chensir.controller"})
public class MyResponseBodyAdvice implements ResponseBodyAdvice {
// 是否开启拦截 true开启 false不开启
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
// 此处应该改为true,否则除了异常外 走到此处为false后就直接返回,也不再继续往下走了!
return true;
}
// 如果接口返回异常就在此处拦截 进行封装;value = Exception.class 对所有的异常均拦截!
@ExceptionHandler(value = Exception.class)
public Object defaultErrorHandler(HttpServletRequest req, Exception ex){
ResponseDto<Object> responseDto = new ResponseDto<>();
responseDto.setCode(501);
responseDto.setMessage(ex.getMessage());
return responseDto;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
ResponseDto<Object> responseDto = new ResponseDto<>();
// 先用hutool定义为null
String message = StrUtil.EMPTY;
responseDto.setCode(0);
responseDto.setMessage(message);
if (body instanceof Page){
Page page = (Page) body;
int pages = page.getPages();//总页数
long total = page.getTotal();//总条数
Dict dict = Dict.create()
.set("total",total)
.set("pages",pages)
.set("item",body);
responseDto.setData(dict);
}else {
responseDto.setData(body);
}
// 如果是string类型就用json封装一下;
if (aClass == StringHttpMessageConverter.class) {
return JSONUtil.toJsonStr(responseDto);
} else {
return responseDto;
}
}
}
构造返回对象泛型ResponseDto<T>
java
// 泛型
@Data
public class ResponseDto<T> implements Serializable {
// 返回码(内部拟定)
private int code;
// 返回信息
private String message;
private T data;
}
拦截器PageMethodInterceptor
java
package com.chensir.interceptor;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.chensir.domain.AbstractQuery;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import java.lang.reflect.Method;
@Slf4j
public class PageMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 获取方法
Method method = invocation.getMethod();
//获取参数
Object[] arguments = invocation.getArguments();
//获取第一个参数
Object arg0 = arguments[0];
if (arg0 instanceof AbstractQuery) {
AbstractQuery query = (AbstractQuery) arg0;
Integer pageNum = query.getPageNum();//参数 第几页
Integer pageSize = query.getPageSize();//参数 每页多少条
if (ObjectUtil.isNotEmpty(pageNum) && ObjectUtil.isNotEmpty(pageSize)) {
//走到这肯定要分页了
PageHelper.startPage(pageNum,pageSize);
}
}
//获取返回值
Object value = invocation.proceed();
log.info("方法:{},参数:{},返回值:{}", method.getName(), JSONUtil.toJsonStr(arguments), value);
return value;
}
}
拦截器注入(PageMethodInterceptorConfig )
advisor.setExpression(point); 拦截切点为@PageX的注解
java
@Configuration
public class PageMethodInterceptorConfig {
@Value("${aop.MethodInterceptor.point}")
private String point;
@Bean
public AspectJExpressionPointcutAdvisor aspectJExpressionPointcutAdvisor(){
AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
advisor.setAdvice(new PageMethodInterceptor());
advisor.setExpression(point);
return advisor;
}
}