mybatis之插件机制

本篇来聊一下mybatis的插件机制,基于myabtis 3.4.6版本。

知识点

  • 什么是插件
  • 如何自定义插件
  • 插件实现原理

什么是插件

相信大家平时肯定使用过很多插件,比如eclipse插件、idea的插件、chrome浏览器插件等,简单地说,插件就是一种扩展,它不属于核心功能,没有它不影响我们正常使用,有了它能够让我们使用起来更方便,属于锦上添花。以下引用知乎一篇文章部分内容:

主程序如果希望自身的功能可以被扩展,其需要:

  1. 提供一组服务 (Service Interface)。其提供(约束)了插件对主体能力可控制的边界。服务定义的越精细,插件控制的粒度越小,能力越大,但耦合度和复杂度也越高。
  2. 定义一种扩展契约 (Plug-In Interface),其描述了插件应该如何设计以便于主程序发现。并通过插件管理模块 (Plug-In Manager) 来发现、维护插件。插件通过实现主程序规定的扩展契约(通常是一个接口),标明自己的身份,并接收来自主程序的事件响应。通过调动主程序提供的服务,实现和主程序的交互。这一过程,通常都是被主程序以 SDK (Software Development Kit) 的形式封装。

image.png

一款好的产品必然有一套灵活的扩展机制,作为目前国内互联网公司用得最多的持久化框架,mybatis 自然也提供了一套扩展机制,也就是插件。

如何自定义插件

在介绍完插件的概念之后,我们来看一下如何在 mybatis 中自定义插件。目前 mybatis 支持4种类型的插件,分别为 Executor、StatementHandler、ResultSetHandler、ParameterHandler。什么意思呢?就是说我们目前自定义插件,只能对这4种类型的方法做拦截,这里基本上涵盖了参数化、sql执行前后、结果集处理,mybatis核心逻辑也就这么多了,所以是足够用的。下面我们来自定义一个插件,实现对Executor的拦截,在查询之前打印"hello"。

1) 先定义一个类,实现拦截器功能

public class MyPlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        return null;
    }

    @Override
    public Object plugin(Object o) {
        return null;
    }

    @Override
    public void setProperties(Properties properties) {
    }
}
复制代码

2)由于我们要对 Executor 的查询进行拦截,所以在其上添加对应的拦截注解说明,当然这里是可以拦截多种接口的

@Intercepts(
        value = {
                @Signature(type = Executor.class, method = "query", args={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
        }
)
public class MyPlugin implements Interceptor {
   ...
}
复制代码

3) 加一个Properties类型成员用来接收配置中设置的值,并在执行具体路基之前输出配置中对应的内容,当然你也可以直接写死输出hello

@Intercepts(
        value = {
                @Signature(type = Executor.class, method = "query", args={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
        }
)
public class MyPlugin implements Interceptor {
    private Properties properties = new Properties();

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println(properties.getProperty("output"));
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }

    @Override
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
}
复制代码

4)在xml配置文件中添加拦截器,目前只支持xml方式来配置拦截器,这个还得需要mybatis-spring后续版本做支持,注意这里是有顺序要求的,plugins标签要放到mappers标签之前

    <plugins>
        <plugin interceptor="com.example.mybatisanalyze.plugin.MyPlugin">
            <property name="output" value="hello"/>
        </plugin>
    </plugins>
复制代码

5)最后我们执行以下程序看是否输出对应的结果

image.png

image.png

可以看到拦截住了并且输出了我们期望的结果。

插件实现原理

插件机制如此灵活,那mybatis是如何实现的呢?下面我们来揭开mybatis插件机制的神秘面纱。首先我们来看一张图

image.png

这里就是所有插件相关的代码逻辑,我们要实现的拦截器接口Interceptor

image.png

intercept 方法就是我们自己定义具体嵌入逻辑的点。所有插件都是通过InterceptorChain来进行统一注册管理的

image.png

interceptors 管理了所有的插件,pluginAll 方法对对象进行插件封装。InterceptsSignature就用来自定义需要拦截的类的方法。核心逻辑都在Plugin

image.png

可以看到这里主要是基于jdk的动态代理做的。结合例子梳理一下整个流程

1)我们在打开session的时候会创建一个executor,在创建的时候,mybatis会给executor加一层插件代理

image.png

2)在我们自己实现的拦截器的 plugin 函数中,我们调用了 Plugin.wrap进行了一层封装

image.png

它会对我们的类签名进行解析,来确认具体要代理的类的方法有哪些,解析逻辑在org.apache.ibatis.plugin.Plugin#getSignatureMap,在我们的例子中,是对 Executor 的 query 方法进行了拦截

image.png

3)我们在执行查询逻辑的时候,就由代理走到了拦截方法中

image.png

总结

mybatis 的插件机制整体还是非常简单的,我们自己开发的时候也可以参照实现插件。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值