本篇来聊一下mybatis的插件机制,基于myabtis 3.4.6版本。
知识点
- 什么是插件
- 如何自定义插件
- 插件实现原理
相信大家平时肯定使用过很多插件,比如eclipse插件、idea的插件、chrome浏览器插件等,简单地说,插件就是一种扩展,它不属于核心功能,没有它不影响我们正常使用,有了它能够让我们使用起来更方便,属于锦上添花。以下引用知乎一篇文章部分内容:
主程序如果希望自身的功能可以被扩展,其需要:
- 提供一组服务 (Service Interface)。其提供(约束)了插件对主体能力可控制的边界。服务定义的越精细,插件控制的粒度越小,能力越大,但耦合度和复杂度也越高。
- 定义一种扩展契约 (Plug-In Interface),其描述了插件应该如何设计以便于主程序发现。并通过插件管理模块 (Plug-In Manager) 来发现、维护插件。插件通过实现主程序规定的扩展契约(通常是一个接口),标明自己的身份,并接收来自主程序的事件响应。通过调动主程序提供的服务,实现和主程序的交互。这一过程,通常都是被主程序以 SDK (Software Development Kit) 的形式封装。
文章图片
一款好的产品必然有一套灵活的扩展机制,作为目前国内互联网公司用得最多的持久化框架,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 = https://www.it610.com/article/{
@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 = https://www.it610.com/article/{
@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标签之前
5)最后我们执行以下程序看是否输出对应的结果
文章图片
文章图片
可以看到拦截住了并且输出了我们期望的结果。
插件实现原理
插件机制如此灵活,那mybatis是如何实现的呢?下面我们来揭开mybatis插件机制的神秘面纱。首先我们来看一张图
文章图片
这里就是所有插件相关的代码逻辑,我们要实现的拦截器接口
Interceptor
文章图片
intercept 方法就是我们自己定义具体嵌入逻辑的点。所有插件都是通过
InterceptorChain
来进行统一注册管理的文章图片
interceptors 管理了所有的插件,pluginAll 方法对对象进行插件封装。
Intercepts
和Signature
就用来自定义需要拦截的类的方法。核心逻辑都在Plugin
文章图片
可以看到这里主要是基于jdk的动态代理做的。结合例子梳理一下整个流程
1)我们在打开session的时候会创建一个executor,在创建的时候,mybatis会给executor加一层插件代理
文章图片
2)在我们自己实现的拦截器的 plugin 函数中,我们调用了
Plugin.wrap
进行了一层封装文章图片
它会对我们的类签名进行解析,来确认具体要代理的类的方法有哪些,解析逻辑在
org.apache.ibatis.plugin.Plugin#getSignatureMap
,在我们的例子中,是对 Executor 的 query 方法进行了拦截文章图片
3)我们在执行查询逻辑的时候,就由代理走到了拦截方法中
文章图片
总结
【mybatis之插件机制】mybatis 的插件机制整体还是非常简单的,我们自己开发的时候也可以参照实现插件。
推荐阅读
- 框架|Mybatis的一级缓存和二级缓存
- Mybatis日志工厂
- MyBatis的功能架构是怎样的
- Mybatis入门之CRUD
- mybatis之缓存机制
- Mybatis练习(1)
- Java|MyBatis(五)——MyBatis中的缓存机制
- mybatis之脚本解析器
- mybatis|记mybatis查询null字段导致的NPE
- Mybatis 动态查询、插入、修改操作