mybatis的插件开发使用了责任链模式,老规矩先介绍设计模式。
责任链模式(Chain of Responsibility Pattern)为请求创建了一个处理请求的链。这个请求由责任链中的处理器一个接一个的处理。
责任链中的角色:
实现mybatis插件的方式就是集成Interceptor接口。
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
看一个样例:
@Intercepts({
//type是指拦截mybatis的那个类,method是拦截的方法,args是拦截方法的参数
@Signature(type=StatementHandler.class,method="query",args={Statement.class, ResultHandler.class})
// @Signature(type=StatementHandler.class,method="query",args={MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})
})
public class MyInterceptor implements Interceptor {
private long threshold;
@Override
public Object intercept(Invocation invocation) throws Throwable {
//前置处理
......
//目标方法
Object ret = invocation.proceed();
//后置处理
......
return ret;
}
@Override
public Object plugin(Object target) {
//插件的调用
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
//读取属性配置
}
(1)插件的加载
在我们之前配置文件的解析中,XMLConfigBuilder的parse方法中有一步就是用来解析plugins插件的。
这一步就是调用方法pluginElement。
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);
}
}
}
这个方法就是将所有配置的插件放入configuration的InterceptorChain属性,这个类中有个List集合,里面放的就是Interceptor的实现类。List是个有序集合,所以配置文件中配置的顺序就代表了插件的顺序。
在我们从SqlSessionFactory获得SqlSession时,调用了configuration.newExecutor方法获得执行器Executor。
(1)newExecutor方法
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;
}
这里最后一步调用的前面创建的责任链的pluginAll方法。
(2)pluginAll方法
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
这个方法就是循环调用拦截器的plugin方法
(3)plugin方法
这个方法就是我们拦截器实现的方法。
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public static Object wrap(Object target, Interceptor interceptor) {
Map, Set> signatureMap = getSignatureMap(interceptor);
Class> type = target.getClass();
Class>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
这个方法就是用JDK动态代理封装被拦截的对象。这里的Plugin就是InvocationHandler。真正调用的是它的invoke方法。
(4)invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
//调用了拦截器的intercept方法。
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
如果配置了多个拦截器,配置的顺序是拦截器1,拦截器2,拦截器3。
那么最后生成的代理对象就是最外层到最内层是拦截器3,拦截器2,拦截器1,实际对象。调用的方法顺序是拦截器3的前置方法,拦截器2的前置方法,拦截器1的前置方法,目标方法,拦截器1的后置方法,拦截器2的后置方法,拦截器3的后置方法。
mybatis能拦截的方法只能是Executor、StatementHandler、ParameterHandler、ResultSetHandler四个类。
为什么只能拦截这四个类,因为mybatis在实现的时候只在创建这四个类的地方调用了拦截器。这四个类创建的方法入口都在Configuration对象中。
前面我们看到了Executor的创建,下面看下剩下三个。
创建StatementHandler对象的入口在Configuration对象的newStatementHandler方法。
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;
}
最后一步调用了拦截器。
创建ParameterHandler对象的入口在Configuration对象的newParameterHandler方法。
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
最后一步调用了拦截器。
创建ResultSetHandler对象的入口在Configuration对象的newResultSetHandler方法。
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;
}
最后一步调用了拦截器。