概述
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; }
|
通过这几段代码我们不难看出,四大接口皆通过调用interceptorChain的pluginAll方法来对对象进行进一步的处理。
我们查看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 = 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) { }
}
|
如何开发一个插件
一个官方推荐的插件开发方式:
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(); Object result = invocation.proceed(); 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>
|
这段代码就会拦截Executor的int update(MappedStatement ms, Object parameter)方法。