MyBatis插件

MyBatis插件



一、插件简介

⼀般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者⾃⾏拓展。,⼀是增加了框架的灵活性。⼆是开发者可以结合实际需求,对框架进⾏拓展,使其能够更好的⼯作。
以MyBatis为例,我们可基于MyBatis插件机制实现分⻚、分表,监控等功能。由于插件和业务⽆关,业务也⽆法感知插件的存在。因此可以⽆感植⼊插件,在⽆形中增强功能

二、mybatis插件介绍

Mybatis作为⼀个应⽤⼴泛的优秀的ORM开源框架,这个框架具有强⼤的灵活性,在四⼤组件(Executor、StatementHandler、ParameterHandler、ResultSetHandler)处提供了简单易⽤的插件扩展机制。Mybatis对持久层的操作就是借助于四⼤核⼼对象。MyBatis⽀持⽤插件对四⼤核⼼对象进⾏拦截,对mybatis来说插件就是拦截器,⽤来增强核⼼对象的功能,增强功能本质上是借助于底层的动
态代理实现的,换句话说,MyBatis中的四⼤对象都是代理对象
在这里插入图片描述
MyBatis所允许拦截的⽅法如下:

  • 执⾏器Executor (update、query、commit、rollback等⽅法);
  • SQL语法构建器StatementHandler (prepare、parameterize、batch、updates query等⽅ 法);
  • 参数处理器ParameterHandler (getParameterObject、setParameters⽅法);
  • 结果集处理器ResultSetHandler (handleResultSets、handleOutputParameters等⽅法);

三、mybatis插件原理

在四⼤对象创建的时候

  • 每个创建出来的对象不是直接返回的,⽽是interceptorChain.pluginAll(parameterHandler);
  • 获取到所有的Interceptor (拦截器)(插件需要实现的接⼝);调⽤ interceptor.plugin(target);返回 target 包装后的对象
  • 插件机制,我们可以使⽤插件为⽬标对象创建⼀个代理对象;AOP (⾯向切⾯)我们的插件可以为四⼤对象创建出代理对象,代理对象就可以拦截到四⼤对象的每⼀个执⾏
  • interceptorChain保存了所有的拦截器(interceptors),是mybatis初始化的时候创建的。调⽤拦截器链中的拦截器依次的对⽬标进⾏拦截或增强。interceptor.plugin(target)中的target就可以理解为mybatis中的四⼤对象。返回的target是被重重代理后的对象

在mybatis中configuration中,四大对象创建时都会被放入拦截器链中,返回代理对象。
在这里插入图片描述
在这里插入图片描述

四、自定义mybatis插件

插件接口:

  1. 通过注解 @Intercepts@Signature 确定拦截的类、方法。
  2. 实现 Interceptor 接口,将插件设置为拦截器
  3. 实现 Interceptor 中的方法。
  4. 在mybatis配置文件中配置插件properties属性

代码实现:

package zlt.example.plugin;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;

import java.sql.Connection;
import java.util.Arrays;
import java.util.Properties;

/**
 * 自定义mybatis插件类
 *
 */
@Intercepts({//设置拦截器,可以设置对多个方法进行处理
        @Signature(
                type = StatementHandler.class,// 拦截哪个接口
                method = "prepare",// 要拦截的方法
                args = {Connection.class, Integer.class}// 拦截方法的入参,由于可能出现重载方法的情况,所以参数要准备以确定对哪个方法进行拦截
        )
})
public class MyPlugin implements Interceptor {

    /**
     * 每次执行操作的时候,都会进行这个拦截器的方法内
     *
     * @param invocation 代理类中的执行
     * @return 原方法执行
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("这里增强了逻辑");
        System.out.println("获取invocation中的参数getArgs:" + Arrays.toString(invocation.getArgs()));
        System.out.println("获取invocation中的参数getTarget:" + invocation.getTarget());
        return invocation.proceed();
    }

    /**
     * 把当前这个拦截器生成一个代理放到拦截器链中
     *
     * @param target 为要拦截的对象
     * @return 代理对象
     */
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
//        return Interceptor.super.plugin(target);
    }

    /**
     * 获取配置文件中设置的属性,插件初始化的诶时候调用,也只调用一次,插件配置的属性从配置中获取
     *
     * @param properties 置文件中设置的属性
     */
    @Override
    public void setProperties(Properties properties) {
        System.out.println("获取对配置文件中设置的属性properties:" + properties);
        Interceptor.super.setProperties(properties);
    }
}

在这里插入图片描述

配置完插件后,mybatis在启动时就可以加载插件,并保存插件实例到相关对象(InterceptorChain,拦截器链)中。
待准备⼯作做完后,MyBatis处于就绪状态。我们在执⾏SQL时,需要先通过DefaultSqlSessionFactory创建 SqlSession。StatementHandler 实例会在创建 SqlSession 的过程中被创建, StatementHandler实例创建完毕后,MyBatis会通过JDK动态代理为实例⽣成代理类。这样,插件逻辑即可在 Executor相关⽅法被调⽤前执⾏。


五、源码分析

Plugin实现了 InvocationHandler接⼝,因此它的invoke⽅法会拦截所有的⽅法调⽤。invoke⽅法会对所拦截的⽅法进⾏检测,以决定是否执⾏插件逻辑。该⽅法的逻辑如下:

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      // 获取被拦截的方法列表,例如:signatureMap.get(Executor.class), 可能返回 [query, update,commit],如果当前执行的方法包含在拦截的方法列表中,那么进入判断
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      if (methods != null && methods.contains(method)) {
        // 检测方法列表如果包含被拦截的方法,那么执行插件逻辑.
        return interceptor.intercept(new Invocation(target, method, args));
      }
      // 执行被拦截的方法。
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }

⾸先,invoke⽅法会检测被拦截⽅法是否配置在插件的@Signature注解中,若是,则执⾏插件逻辑,否则执⾏被拦截⽅法。插件逻辑封装在intercept中,该⽅法的参数类型为Invocationo, Invocation主要⽤于存储⽬标类,⽅法以及⽅法参数列表。
在这里插入图片描述

六、pageHelper分页插件

MyBatis可以使⽤第三⽅的插件来对功能进⾏扩展,分⻚助⼿PageHelper是将分⻚的复杂操作进⾏封装,使⽤简单的⽅式即可获得分⻚的相关数据

1. 导入依赖

<!-- 分页插件 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>3.7.5</version>
</dependency>
<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>0.9.1</version>
</dependency>

2. mybatis核心配置文件配置PageHelper插件

在这里插入图片描述

3. 测试代码使用

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值