使用MethodInterceptor和ResponseBodyAdvice做分页处理

目录

一、需求

二、代码实现

父pom文件

pom文件

配置文件

[手动注册SqlSessionFactory(MyBatisConfig )](#手动注册SqlSessionFactory(MyBatisConfig ))

对象

实体类Users

抽象类AbstractQuery

查询参数类UsersQuery

三层架构

UsersController

UsersServiceImpl

UsersMapper

UsersMapper.xml

[PageX 注解](#PageX 注解)

MyResponseBodyAdvice

构造返回对象泛型ResponseDto

拦截器PageMethodInterceptor

[拦截器注入(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;
    }
}

结果展示

相关推荐
liu_chunhai几秒前
设计模式(3)builder
java·开发语言·设计模式
姜学迁8 分钟前
Rust-枚举
开发语言·后端·rust
冷白白10 分钟前
【C++】C++对象初探及友元
c语言·开发语言·c++·算法
凌云行者14 分钟前
rust的迭代器方法——collect
开发语言·rust
It'sMyGo17 分钟前
Javascript数组研究09_Array.prototype[Symbol.unscopables]
开发语言·javascript·原型模式
睡觉然后上课28 分钟前
c基础面试题
c语言·开发语言·c++·面试
qing_04060335 分钟前
C++——继承
开发语言·c++·继承
武昌库里写JAVA35 分钟前
【Java】Java面试题笔试
c语言·开发语言·数据结构·算法·二维数组
ya888g36 分钟前
GESP C++四级样题卷
java·c++·算法
【D'accumulation】1 小时前
令牌主动失效机制范例(利用redis)注释分析
java·spring boot·redis·后端