1. 简介
一般情况下,开源框架会提供插件或者其它形式的扩展点,供开发者自行扩展。这样可以增加框架的灵活性,开发者可以结合实际需求,对框架进行扩展。Mybatis可以基于插件实现分页、分表、监控等功能。由于插件与业务无关,业务也无法感知插件的存在。因此可以无感植入插件,在无形中增强功能。
2. Mybatis插件介绍
- Mybatis支持插件对四大核心对象进行拦截
- Executor:执行器,update、query、commit、rollback等方法。
- StatementHandler:sql语法构建器,prepare、parameterize、batch、update、query等方法。
- ParameterHandler:参数处理器,getParameterObject、setParameterObject等方法。
- ResultSetHandler:结构集处理器,handleResultsets、handleOutputParameters等方法。
3. Mybatis插件原理
总结:使用jdk动态代理为四大对象创建代理对象
在四大对象创建的时候:
- 每个对象创建出来不是直接返回的,而是通过interceptorChain(parameterHandler)方法进行处理。
- 获取到所有的Interceptor(拦截器)(插件需要实现的接口),调用interceptor.plugin(target),返回target包装后的对象
- 插件机制,我们可以使用插件为目标创建一个代理对象,AOP我们的插件可以为四大对象创建出代理对象,代理对象就可以拦截到四大对象的每一个执行
- 以ParameterHandler为例看插件是如何进行代理的: ```java public ParameterHandler newParameterHandler(MappedStatement mappedStatement,Object object,BoundSql,boundSql,InterceptorChain interceptorChain) { ParameterHandler parameterHandler = mappedStatement.getLang.createParameterHandler(mappedStatement,object,sql); parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler); return parameterHandler; }
public Object pluginAll(Object target) { for(Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
5. interceptorChain保存了所有的拦截器(interceptors),是mybatis初始化的时候创建的。interceptor.plugin(target)中的target可以理解为mybatis的四大对象,返回的是被重重代理后的对象。
6. 自定义插件,比如拦截Executor中的query方法:
1. 定义一个类,实现Interceptor接口
```java
@Interceptors({
@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class}
)
})
public class ExamplePlugin implements Interceptor {
//省略逻辑
}
- 将插件配置到sqlMapperConfig中
<plugins>
<plugin interceptor = "com.test.plugin.ExamplePlugin"></plugin>
</plugins>
4. 自定义Mybatis插件
创建一个类
@Intercepts({
@Signature(
// 拦截哪个类
type = StatementHandler.class,
// 拦截哪个方法
method = "prepare",
// 方法的参数,如果有方法重载就看参数
args = {Connection.class,Integer.class}
)
})
public class MyPlugin implements Interceptor {
/**
* 拦截方法:只要目标对象的目标方法被执行时,每次都会执行intercept方法
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("对方法进行了增强...");
// 让原方法执行
return invocation.proceed();
}
/**
* 主要为了把当前的拦截器生成代理存到拦截链中
* @param target
* @return
*/
@Override
public Object plugin(Object target) {
// this表示当前类(也就是自定义的拦截器)
return Plugin.wrap(target,this);
}
/**
* 获取配置文件的参数
* @param properties
*/
@Override
public void setProperties(Properties properties) {
}
}
配置文件sqlMapConfig.xml
<plugins>
<plugin interceptor="com.lagou.plugin.MyPlugin"/>
</plugins>