一、mybatis 插件介绍
mybatis 插件在四大组件 (Executor、StatementHandler、ParameterHandler、ResultSetHandler) 处提供了简单易用的插件扩展机制。
mybatis 对持久层的操作就是借助于四大核心对象。
myBatis 支持用插件对四大核心对象进行拦截,对 mybatis 来说插件就是拦截器,用来增强核心对象的功能,增强功能本质上是借助于底层的动态代理实现的,换句话说,myBatis 中的四大对象都是代理对象。
myBatis 所允许拦截的方法如下:
- 执行器 Executor (update、query、commit、rollback 等方法); SQL语法构建器 StatementHandler (prepare、parameterize、batch、update、query 等方法);
- 参数处理器 ParameterHandler (getParameterObject、setParameters 方法);
- 结果集处理器 ResultSetHandler (handleResultSets、handleOutputParameters 等方法);
二、mybatis 插件原理
1、创建
在四大对象创建的时候:
- 每个创建出来的对象不是直接返回的,而是 interceptorChain.pluginAll(parameterHandler);
- 获取到所有的 Interceptor(拦截器)(插件需要实现的接口),调用 interceptor.plugin(target),返回 target 包装后的对象;
- 插件机制,我们可以使用插件为目标对象创建一个代理对象,AOP(面向切面)我们的插件可以为四大对象创建出代理对象,代理对象就可以拦截到四大对象的每一个执行。
2、拦截
插件具体是如何拦截并附加额外的功能的呢?以 ParameterHandler 来说:
1)首先是 ParameterHandler 的创建,在 Configuration 类当中:
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
2)interceptorChain 保存了所有的拦截器(interceptors),是 mybatis 初始化的时候创建的。
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
调用拦截器链中的拦截器依次的对目标进行拦截或增强。
interceptor.plugin(target) 中的 target 就可以理解为 mybatis 中的四大对象。返回的 target 是被重重代理后的对象。
3、应用
如果我们想要拦截 Executor 的 query 方法,那么可以这样定义插件:
1)类相关代码
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args = {
MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class}
)
})
public class ExeunplePlugin implements Interceptor {
//省略逻辑
}
2)插件配置,在 sqlMapConfig.xml 中
<plugins>
<plugin interceptor="com.lagou.plugin.ExamplePlugin">
</plugin>
</plugins>
这样 MyBatis 在启动时可以加载插件,并保存插件实例到相关对象(InterceptorChain,拦截器链)中。
待准备工作做完后,MyBatis 处于就绪状态。
我们在执行 SQL 时,需要先通过 DefaultSqlSessionFactory 创建 SqlSession。
Executor 实例会在创建 SqlSession 的过程中被创建, Executor 实例创建完毕后, MyBatis 会通过 JDK 动态代理为实例生成代理类。
这样,插件逻辑即可在 Executor 相关方法被调用前执行。
以上就是 MyBatis 插件机制的基本原理。
三、自定义插件
1、插件接口
Mybatis 插件接口 —— Interceptor
- Intercept 方法,插件的核心方法
- plugin 方法,生成 target 的代理对象
- setProperties 方法,传递插件所需参数
2、自定义插件
设计实现一个自定义插件。
1)相关类代码
@Intercepts(@Signature( // 注意看这个大花括号,也就这说这里可以定义多个@Signature对多个地方拦截,都用这个拦截器
type = StatementHandler.class, // 这是指拦截哪个接口
method = "prepare", // 这个接口内的哪个方法名,不要拼错了
args = {
Connection.class,Integer.class}
// 这是拦截的方法的入参,按顺序写到这,不要多也不要少,如果方法重载,可是要通过方法名和入参来确定唯一的
))
public class MyPlugin implements Interceptor {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
// 这里是每次执行操作的时候,都会进行这个拦截器的方法内
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 增强逻辑
System.out.println("增强了参数功能。。");