深入理解MyBatis(三)
插件模块
插件是一种常见的扩展方式,Mybatis也提供了插件的功能。虽然叫插件,但实际上就是拦截器(Interceptor)
Interceptor
Mybatis允许用户使用自定义拦截器对SQL语句执行过程中的某一点进行拦截。默认情况下,Mybatis允许拦截Executor、ParameterHandler、ResultSetHandler以及StatementHandler方法,具体方法如下
- Executor中的update()、query()、flushStatements()、commit()、rollback()、getTransaction()、close()、isClosed()
- ParammeterHandler中的getParameterObject()、setParameters()
- ResultSetHandler中的handleResultSets()、handleOutputParameters()
- StatementHandler中的prepare()、parameterize()、batch()、update()
Mybatis中使用拦截器需要继承Interceptor接口,它的定义如下
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
default void setProperties(Properties properties) {
// NOP
}
}
除了继承Interceptor外,自定义的拦截器还需要使用@Intercepts和@Signature两个注解。
@Intercepts中指定@Signature注解列表。每个@Signature注解中都标识了该插件需要拦截的方法信息
具体如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
({
// type指定类名,method指定方法名,args指定参数。 一个@Signature注解通过三个属性确定唯一方法
"query", args = { (type = Executor.class, method =
MappedStatement.class,
Object.class,
RowBounds.class,
ResultHandler.class
}),
"update", args = { (type = Executor.class,method =
MappedStatement.class,
Object.class
})
})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return null;
}
}
定义完成以后,需要在配置中添加对该拦截器的配置
1
2
3
<plugins>
<plugin interceptor="mybatis.ExamplePlugin">
</plugins>
至此,一个自定义的拦截器就配置好了。在mybatis初始化过程中,会对plugin节点解析,最后将Interceptor添加到Configuration中的interceptorChain中,里面使用List保存了所有拦截器.采用责任链模式处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
应用场景
分页插件
用户可以添加自定义的拦截器,拦截Executor.query方法,从参数RowBounds中获取记录的开始位置,然后获取BoundSql参数,修改待执行的SQL语句,拼接”limit offset,length”,来实现分页功能
以PageHelper为例
1 | "rawtypes", "unchecked"}) ({ |