千家信息网

Springboot如何实现自定义mybatis拦截器

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,这篇文章将为大家详细讲解有关Springboot如何实现自定义mybatis拦截器,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。实践的准备 :整合mybatis ,
千家信息网最后更新 2026年11月02日Springboot如何实现自定义mybatis拦截器

这篇文章将为大家详细讲解有关Springboot如何实现自定义mybatis拦截器,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

实践的准备 :

整合mybatis ,然后故意写了3个查询方法, 1个是list 列表数据,2个是 单条数据 。

我们通过自己写一个MybatisInterceptor实现 mybatis框架的 Interceptor来做文章:

MybatisInterceptor.java :

import org.apache.ibatis.executor.Executor;import org.apache.ibatis.mapping.*;import org.apache.ibatis.plugin.*;import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;import org.springframework.stereotype.Component; import java.lang.reflect.Method;import java.util.*;   /** * @Author JCccc * @Description * @Date 2021/12/14 16:56 */@Component@Intercepts({        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})public class MybatisInterceptor implements Interceptor {     @Override    public Object intercept(Invocation invocation) throws Throwable {        //获取执行参数        Object[] objects = invocation.getArgs();        MappedStatement ms = (MappedStatement) objects[0];        //解析执行sql的map方法,开始自定义规则匹配逻辑        String mapperMethodAllName = ms.getId();        int lastIndex = mapperMethodAllName.lastIndexOf(".");        String mapperClassStr = mapperMethodAllName.substring(0, lastIndex);        String mapperClassMethodStr = mapperMethodAllName.substring((lastIndex + 1));        Class mapperClass = Class.forName(mapperClassStr);        Method[] methods = mapperClass.getMethods();        Class returnType;        for (Method method : methods) {            if (method.getName().equals(mapperClassMethodStr)) {                returnType = method.getReturnType();                if (returnType.isAssignableFrom(List.class)) {                    System.out.println("返回类型是 List");                    System.out.println("针对List 做一些操作");                } else if (returnType.isAssignableFrom(Set.class)) {                    System.out.println("返回类型是 Set");                    System.out.println("针对Set 做一些操作");                } else{                    BoundSql boundSql = ms.getSqlSource().getBoundSql(objects[1]);                    String oldSql = boundSql.getSql().toLowerCase(Locale.CHINA).replace("[\\t\\n\\r]", " ");                    if (!oldSql.contains("LIMIT")){                        String newSql = boundSql.getSql().toLowerCase(Locale.CHINA).replace("[\\t\\n\\r]", " ") + " LIMIT 1";                        BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), newSql,                                boundSql.getParameterMappings(), boundSql.getParameterObject());                        MappedStatement newMs = newMappedStatement(ms, new MyBoundSqlSqlSource(newBoundSql));                        for (ParameterMapping mapping : boundSql.getParameterMappings()) {                            String prop = mapping.getProperty();                            if (boundSql.hasAdditionalParameter(prop)) {                                newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));                            }                        }                        Object[] queryArgs = invocation.getArgs();                        queryArgs[0] = newMs;                        System.out.println("打印新SQL语句" + newSql);                    }                 }            }        }        //继续执行逻辑        return invocation.proceed();    }      @Override    public Object plugin(Object o) {        //获取代理权        if (o instanceof Executor) {            //如果是Executor(执行增删改查操作),则拦截下来            return Plugin.wrap(o, this);        } else {            return o;        }    }     /**     * 定义一个内部辅助类,作用是包装 SQL     */    class MyBoundSqlSqlSource implements SqlSource {        private BoundSql boundSql;         public MyBoundSqlSqlSource(BoundSql boundSql) {            this.boundSql = boundSql;        }         @Override        public BoundSql getBoundSql(Object parameterObject) {            return boundSql;        }     }     private MappedStatement newMappedStatement(MappedStatement ms, SqlSource newSqlSource) {        MappedStatement.Builder builder = new                MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());        builder.resource(ms.getResource());        builder.fetchSize(ms.getFetchSize());        builder.statementType(ms.getStatementType());        builder.keyGenerator(ms.getKeyGenerator());        if (ms.getKeyProperties() != null && ms.getKeyProperties().length > 0) {            builder.keyProperty(ms.getKeyProperties()[0]);        }        builder.timeout(ms.getTimeout());        builder.parameterMap(ms.getParameterMap());        builder.resultMaps(ms.getResultMaps());        builder.resultSetType(ms.getResultSetType());        builder.cache(ms.getCache());        builder.flushCacheRequired(ms.isFlushCacheRequired());        builder.useCache(ms.isUseCache());        return builder.build();    }      @Override    public void setProperties(Properties properties) {        //读取mybatis配置文件中属性    }  }

简单代码端解析:

① 拿出执行参数,里面有很多东西给我们用的,我本篇主要是用MappedStatement。大家可以发挥自己的想法。 打debug可以自己看看里面的东西。

//获取执行参数Object[] objects = invocation.getArgs();MappedStatement ms = (MappedStatement) objects[0];

② 这里我主要是使用了从MappedStatement里面拿出来的id,做切割分别切出来 目前执行的sql的mapper是哪个,然后方法是哪个。

String mapperMethodAllName = ms.getId();int lastIndex = mapperMethodAllName.lastIndexOf(".");String mapperClassStr = mapperMethodAllName.substring(0, lastIndex);String mapperClassMethodStr = mapperMethodAllName.substring((lastIndex + 1));

③ 这就是我自己随便写的一下,我直接根据切割出来的mapper类找出里面的方法,主要是为了拿出每个方法的返回类型,因为 我这篇实践内容就是,判断出单个pojo接受的mapper方法,加个LIMIT 1 , 其他的 LIST 、SET 、 Page 等等这些,我暂时不做扩展。

Class mapperClass = Class.forName(mapperClassStr);Method[] methods = mapperClass.getMethods();Class returnType;

④这一段代码就是该篇的拓展逻辑了,从MappedStatement里面拿出 SqlSource,然后再拿出BoundSql,然后一顿操作 给加上 LIMIT 1 , 然后new 一个新的 MappedStatement,一顿操作塞回去invocation 里面

BoundSql boundSql = ms.getSqlSource().getBoundSql(objects[1]);String oldSql = boundSql.getSql().toLowerCase(Locale.CHINA).replace("[\\t\\n\\r]", " ");if (!oldSql.contains("LIMIT")){    String newSql = boundSql.getSql().toLowerCase(Locale.CHINA).replace("[\\t\\n\\r]", " ") + " LIMIT 1";    BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), newSql,            boundSql.getParameterMappings(), boundSql.getParameterObject());    MappedStatement newMs = newMappedStatement(ms, new MyBoundSqlSqlSource(newBoundSql));    for (ParameterMapping mapping : boundSql.getParameterMappings()) {        String prop = mapping.getProperty();        if (boundSql.hasAdditionalParameter(prop)) {            newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));        }    }    Object[] queryArgs = invocation.getArgs();    queryArgs[0] = newMs;    System.out.println("打印新SQL语句:" + newSql);}

OK,最后简单来试试效果 :

最后再重申一下,本篇文章内容只是一个抛砖引玉,随便想的一些实践效果,大家可以按照自己的想法发挥。

最后再补充一个 解决 mybatis自定义拦截器和 pagehelper 拦截器 冲突导致失效的问题出现的

解决方案:

加上一个拦截器配置类

MyDataSourceInterceptorConfig.java :

import org.apache.ibatis.session.SqlSessionFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.ApplicationListener;import org.springframework.context.event.ContextRefreshedEvent;import org.springframework.stereotype.Component; import java.util.List; /** * @Author JCccc * @Description * @Date 2021/12/14 16:56 */@Componentpublic class MyDataSourceInterceptorConfig implements ApplicationListener {     @Autowired    private MybatisInterceptor mybatisInterceptor;     @Autowired    private List sqlSessionFactories;     @Override    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {        for (SqlSessionFactory factory : sqlSessionFactories) {            factory.getConfiguration().addInterceptor(mybatisInterceptor);        }    }}

最后最后再补充多一些,

文中 针对拦截的是Executor 这种接口的插件,

其实 还可以使用ParameterHandler、ResultSetHandler、StatementHandler。

每当执行这四种接口对象的方法时,就会进入拦截方法,然后我们可以根据不同的插件去拿不同的参数。

类似:

@Signature(method = "prepare", type = StatementHandler.class, args = {Connection.class,Integer.class})

然后可以做个转换,也是可以取出相关的 BoundSql 等等 :

if (!(invocation.getTarget() instanceof RoutingStatementHandler)){                return invocation.proceed();            }            RoutingStatementHandler statementHandler = (RoutingStatementHandler) invocation.getTarget();            BoundSql boundSql = statementHandler.getBoundSql();            String sql = boundSql.getSql().toUpperCase();

ParameterHandler、ResultSetHandler、StatementHandler、Executor ,不同的插件拦截,有不同的使用场景,想深入的看客们,可以深一下。

关于"Springboot如何实现自定义mybatis拦截器"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

方法 拦截器 不同 参数 内容 就是 插件 篇文章 类型 逻辑 实践 东西 代码 想法 接口 效果 数据 文章 更多 语句 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技图片 互联网科技公司经营范围 美国互联网保险保险科技 科技互联网项目 互联网金融与金融科技 互联网科技英文 奇酷互联网科技公司 互联网金融和金融科技 科技伦理 互联网公司 无锡溥仪互联网科技有限公司 创业天下互联网科技有限公司 乌鲁木齐新华智原互联网科技学校 西安小鹰速贷互联网科技有限公司 浙南科技城互联网大厦招聘 九思连强互联网电子科技有限公司 新氧科技和美国互联网有关系吗 陕西九四连强互联网科技有限公司 河南商务圈互联网科技有限公司 互联网的黑科技实验室 武汉亿视互联网科技有限公司 互联网新科技 麻工互联网科技 美国互联网黑科技 互联网女皇 腾讯科技 北京禾田雨橡互联网科技 小米互联网科技园设置 浙江互联网科技研学营 互联网科技公司营业执照怎么办理 雨阳互联网卡科技有限公司 互联网科技接地气
0