概述

mybatis插件又被称为拦截器,mybaits采用责任链模式,通过动态代理组织多个插件,通过这些插件可以改变mybatis的默认行为。mybatis允许在映射语句执行的某一点进行拦截调用。默认情况下,mybatis使用允许使用插件来拦截方法的调用包括:

  • Executor是mybatis的执行器,它负责调用statementHandler操作数据库,并把结果集通过ResultSetHandler进行自动映射,另外,它还处理了二级缓存的操作。
  • StatementHandler是mybatis直接和数据库执行sql脚本的对象,另外它也实现了mybatis的一级缓存。
  • ParameterHandler是mybatis实现sql入参设置的对象。
  • ResultSetHandler是mybatis把ResultSet结果集映射成POJO的接口对象。

拦截器的原理是什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}

executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
//增强
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//增强
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}

public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
//增强
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}

通过这几段代码我们不难看出,四大接口皆通过调用interceptorChainpluginAll方法来对对象进行进一步的处理。

我们查看interceptorChain类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class InterceptorChain {

//一个拦截器集合
private final List<Interceptor> interceptors = new ArrayList<>();

public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
//调用所有的插件对target进行进一步的处理
target = interceptor.plugin(target);
}
return target;
}

public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}

public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}

}

那么对对象进行进一步加工的拦截器的结构又是怎样的呢。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
}

}

如何开发一个插件

一个官方推荐的插件开发方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Intercepts({@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class TestInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
Object target = invocation.getTarget(); //被代理对象
Method method = invocation.getMethod(); //代理方法
Object[] args = invocation.getArgs(); //方法参数
// do something ...... 方法拦截前执行代码块
Object result = invocation.proceed();
// do something .......方法拦截后执行代码块
return result;
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
}


编写一个简单的拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Intercepts({@Signature(type = Executor.class,method = "update",args = {MappedStatement.class, Object.class})})
public class MyPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("拦截到了");
return invocation.proceed();
}

@Override
public Object plugin(Object target) {
return Plugin.wrap(target,this);
}
@Override
public void setProperties(Properties properties) {

}


}

同时我们在全局配置文件中对我们编写的拦截器进行注册。

1
2
3
4
<plugins>
<plugin interceptor="example.MyPlugin"/>
</plugins>

这段代码就会拦截Executorint update(MappedStatement ms, Object parameter)方法。