Mybatis 自定义拦截器与插件开发

本文介绍了Mybatis自定义拦截器的原理和实现方式,详细讲解了拦截器可以拦截的对象(Executor、StatementHandler、ParameterHandler、ResultSetHandler)以及拦截器的工作流程。通过示例代码展示了如何实现SQL监控、分页查询、数据脱敏和多租户查询等功能,强调了合理使用拦截器的重要性,以避免过度使用导致的性能影响。
摘要由CSDN通过智能技术生成

在 Spring 中我们经常会使用到拦截器,在登录验证、日志记录、性能监控等场景中,通过使用拦截器允许我们在不改动业务代码的情况下,执行拦截器的方法来增强现有的逻辑。在 mybatis 中,同样也有这样的业务场景,有时候需要我们在不侵入原有业务代码的情况下拦截 sql,执行特定的某些逻辑。那么这个过程应该怎么实现呢,同样,在 mybatis 中也为开发者预留了拦截器接口,通过实现自定义拦截器这一功能,可以实现我们自己的插件,允许用户在不改动 mybatis 的原有逻辑的条件下,实现自己的逻辑扩展。

那么,在实现拦截器之前,首先看一下拦截器的拦截目标对象是什么,以及拦截器的工作流程是怎样的?

拦截器核心对象

mybatis 拦截器可以对下面 4 种对象进行拦截:

1、Executor:mybatis 的内部执行器,作为调度核心负责调用StatementHandler操作数据库,并把结果集通过ResultSetHandler进行自动映射

2、StatementHandler: 封装了JDBC Statement操作,是 sql 语法的构建器,负责和数据库进行交互执行 sql 语句

3、ParameterHandler:作为处理 sql 参数设置的对象,主要实现读取参数和对PreparedStatement的参数进行赋值

4、ResultSetHandler:处理Statement执行完成后返回结果集的接口对象,mybatis 通过它把ResultSet集合映射成实体对象

工作流程

在 mybatis 中提供了一个Interceptor接口,通过实现该接口就能够自定义拦截器,接口中定义了 3 个方法:

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  }}

  • intercept:在拦截目标对象的方法时,实际执行的增强逻辑,我们一般在该方法中实现自定义逻辑

  • plugin:用于返回原生目标对象或它的代理对象,当返回的是代理对象的时候,会调用intercept方法

  • setProperties:可以用于读取配置文件中通过property标签配置的一些属性,设置一些属性变量

看一下plugin方法中的wrap方法源码:

public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);  Class<?> type = target.getClass();  Class<?>[] interfaces = getAllInterfaces(type, signatureMap);  if (interfaces.length > 0) {
      return Proxy.newProxyInstance(        type.getClassLoader(),        interfaces,        new Plugin(target, interceptor, signatureMap));  }  return target;}

可以看到,在wrap方法中,通过使用 jdk 动态代理的方式,生成了目标对象的代理对象,在执行实际方法前,先执行代理对象中的逻辑,来实现的逻辑增强。以拦截Executorquery方法为例,在实际执行前会执行拦截器中的intercept方法:

在 mybatis 中,不同类型的拦截器按照下面的顺序执行:

Executor -> StatementHandler -> ParameterHandler -> ResultSetHandler

以执行query 方法为例对流程进行梳理,整体流程如下:

1、Executor执行query()方法,创建一个StatementHandler对象

2、StatementHandler 调用ParameterHandler对象的setParameters()方法

3、StatementHandler 调用 Statement对象的execute()方法

4、StatementHandler 调用ResultSetHandler对象的handleResultSets()方法,返回最终结果

拦截器能实现什么

在对 mybatis 拦截器有了初步的认识后,来看一下拦截器被普遍应用在哪些方面:

  • sql 语句执行监控

  • 可以拦截执行的 sql 方法,可以打印执行的 sql 语句、参数等信息,并且还能够记录执行的总耗时,可供后期的 sql 分析时使用

  • sql 分页查询

  • mybatis 中使用的RowBounds使用的内存分页,在分页前会查询所有符合条件的数据,在数据量大的情况下性能较差。通过拦截器,可以做到在查询前修改 sql 语句,提前加上需要的分页参数

  • 公共字段的赋值

  • 在数据库中通常会有createTimeupdateTime等公共字段,这类字段可以通过拦截统一对参数进行的赋值,从而省去手工通过set方法赋值的繁琐过程

  • 数据权限过滤

  • 在很多系统中,不同的用户可能拥有不同的数据访问权限,例如在多租户的系统中,要做到租户间的数据隔离,每个租户只能访问到自己的数据,通过拦截器改写 sql 语句及参数,能够实现对数据的自动过滤

除此之外,拦截器通过对上述的 4 个阶段的介入,结合我们的实际业务场景,还能够实现很多其他功能。

插件定义与注册

在我们自定义的拦截器类实现了Interceptor接口后࿰

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值