文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

mybatis实战之拦截器解读

2023-03-20 15:57

关注

mybatis实战之拦截器

在服务的开发过程中,往往存在这样的需求,针对业务,实现对数据库操作语句做统一的处理。

比如对某些敏感数据如用户姓名、手机号等坐脱敏处理保存和查询、对未实现权限的查询通过添加关联查询实现权限控制查询结果等等。

这时,mybatis框架提供了拦截器的方式,允许在映射语句执行过程中的某一点进行拦截调用,进行自己的业务处理。

1、使用方法

这里参考了官网的使用说明,只需实现 Interceptor 接口,并在类中指定想要拦截的方法签名即可。

比如:

@Intercepts({@Signature(
 type= Executor.class,
 method = "update",
 args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
 private Properties properties = new Properties();
 public Object intercept(Invocation invocation) throws Throwable {
  // implement pre processing if need
  Object returnObject = invocation.proceed();
  // implement post processing if need
  return returnObject;
 }
 public void setProperties(Properties properties) {
  this.properties = properties;
 }
}

然后在mybatis的配置文件中,添加插件的对应配置即可。

<!-- mybatis-config.XML -->
<plugins>
 <plugin interceptor="org.mybatis.example.ExamplePlugin">
  <property name="someProperty" value="100"/>
 </plugin>
</plugins>

我们也可以在代码中添加,下面给出在spring中

//通过spring查找SqlSessionFactory对象的逻辑在此省略
SqlSessionFactory sqlSessionFactory = (SqlSessionFactory) var3;
org.apache.ibatis.session.Configuration c = sqlSessionFactory.getConfiguration();
c.addInterceptor(interceptor);

分别加上处理的业务逻辑,这个拦截器就可以使用了。

2、需要注意的地方

第一节简单介绍了,拦截器的使用方法,但在实际项目中这样还远远不够。

笔者在本节列举了一些需要注意的地方,供大家思考讨论。

拦截器的执行顺序

拦截器的调用顺序分为两大种,第一种是拦截的不同对象,第二种是指拦截同一种对象的同一个方法。

第一种情况,例如拦截 Executor 和 拦截 StatementHandler 就属于不同的拦截对象, 这两类的拦截器在整体执行的逻辑上是不同的。

StatementHandler 属于 Executor 执行过程中的一个子过程。

所以这两种不同类别的插件在配置时,一定是先执行 Executor 的拦截器,然后才会轮到 StatementHandler。

所以这种情况下配置拦截器的顺序就不重要了,在 MyBatis 逻辑上就已经控制了先后顺序。

第二种情况,例如都拦截 Executor 的 query 方法,这时你配置拦截器的顺序就会对这里有影响了。比如配置如下。

<plugins>
  <plugin interceptor="com.github.pagehelper.ExecutorQueryInterceptor1"/>
  <plugin interceptor="com.github.pagehelper.ExecutorQueryInterceptor2"/>
  <plugin interceptor="com.github.pagehelper.ExecutorQueryInterceptor3"/>
</plugins>

前面我们配置拦截器的顺序是1,2,3。在这里也会按照 1,2,3 的顺序被层层代理,代理后的结构如下:

Interceptor3:{
  Interceptor2: {
    Interceptor1: {
      target: Executor
    }
  }
}

然后到执行的逻辑:

Interceptor3 前置处理
Interceptor2 前置处理
Interceptor1 前置处理  
Object result = executor.query(4个参数方法);     
Interceptor1 后续处理   
Interceptor2 后续处理  
Interceptor3 后续处理   
return result;

顺序就是 3>2>1>Executor>1>2>3。MyBatis的拦截器采用责任链设计模式,多个拦截器之间的责任链是通过动态代理组织的。

我们一般都会在拦截器中的intercept方法中往往会有invocation.proceed()语句,其作用是将拦截器责任链向后传递,本质上便是动态代理的invoke。

与常用插件的整合遇到的问题

pageHelper造成分页失效的问题

通过查看pagehelper源码,可以看到其inercept方法直接获取了excutor然后开始分页查询,当查询到结果时,便返回了。

就是pagehelper的intercept方法中没有invocation.proceed(),这意味着什么?

//com.github.pagehelper.PageInterceptor#intercept
.....
        resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
      }
      return dialect.afterPage(resultList, parameter, rowBounds);
    } finally {
      dialect.afterAll();
    }

这意味着pagehelper没有继续向后传递责任链,而是自行处理直接返回。

由此,我们可以猜出该问题大概率与拦截器的执行顺序有关。

通过断点调试,验证了该猜想,当遇到分页查询时,执行到pagehelper就结束了,没有进入我们的自定义拦截器。这就可能造成我们自定义拦截器失效。

解决方案

因为PageHelper是Excetor类型的拦截器,所以我们如果想要在PageHelper拦截器前面执行,就必须要将我们自己的拦截器添加到他的拦截器后面。

这里只介绍最简单最优雅的一种方式:

注册一个ApplicationListener监听器,监听 ContextRefreshedEvent 事件,当所有的bean都初始化完成后(即PageHelper也已经注册好了),再把我们的自定义 MyBatis 拦截器注册到 SqlSessionFactory 中。

可以提升的点

Interceptor接口提供了三个方法分别是拦截器处理逻辑的主要方法、判断是否要进行拦截,然后做出决定是否生成一个代理的方法及设置参数的方法。

package org.apache.ibatis.plugin;

import Java.util.Properties;

public interface Interceptor {

 Object intercept(Invocation invocation) throws Throwable;

 default Object plugin(Object target) {
  return Plugin.wrap(target, this);
 }

 default void setProperties(Properties properties) {
  // NOP
 }
}

这里说的提升点,就是在实现接口的实现类中,我们可以在plugin方法里加上一个判断,因为默认情况下,拦截器根据顺序拦截后,就可以去处理对应逻辑了,这里加上一个判断拦截的条件,可以减少代理类的创建。

  @Override
  public Object plugin(Object target) {
    if (target instanceof StatementHandler && checkIfNeeded((StatementHandler) target)) {
      return Plugin.wrap(target, this);
    } else {
      return target;
    }
  }

参考文档:

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯