MyBatis支持配置多个插件动态添加新的功能,因为存在InterceptorChain,很多人认为Mybatis采用责任链模式,看了源码后我觉得更像是装饰器模式。
Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler进行拦截。下面依旧以StatementHandler类型的SQLStatsInterceptor为例:
package com.cuisea.mybatis;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.util.Properties;
/**
* Created by cuisea on 2017/7/18.
*/
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class}) })
public class SQLStatsInterceptor implements Interceptor{
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
String sql = boundSql.getSql();
logger.info("mybatis intercept sql:{}", sql);
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
String dialect = properties.getProperty("dialect");
logger.info("mybatis intercept dialect:{}", dialect);
}
}
MyBatis启动是会创建StatementHandler示例,见org.apache.ibatis.session.Configuration中的代码
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;
}
interceptorChain.pluginAll(statementHandler);即将配置的plugin应用到
statementHandler上。InterceptorChain代码:
public class InterceptorChain {
private final List interceptors = new ArrayList();
public InterceptorChain() {
}
public Object pluginAll(Object target) {
Interceptor interceptor;
for(Iterator var2 = this.interceptors.iterator(); var2.hasNext(); target = interceptor.plugin(target)) {
interceptor = (Interceptor)var2.next();
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
this.interceptors.add(interceptor);
}
public List getInterceptors() {
return Collections.unmodifiableList(this.interceptors);
}
}
可见,
pluginAll方法实际上循环调用了interceptor的plugin方法,改方法中只有一句代码Plugin.wrap(target, this); Plugin关键代码如下:
public class Plugin implements InvocationHandler {
private Object target;
private Interceptor interceptor;
private Map, Set> signatureMap;
private Plugin(Object target, Interceptor interceptor, Map, Set> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
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;
}
}
plugin的wrap方法通过自定义拦截器上的@Signature注解的type属性判断是否要对
statementHandler进行拦截,如果需要拦截则生成JDK动态代理,否则返回原生的statementHandler对象。所以,如果针对statementHandler配置多个拦截器的话会进行层层wrap。
由于Plugin实现了InvocationHandler接口,对statementHandler代理对象调用时会调用Plugin的invoke方法:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
invoke方法会创建一个Invocation对象包装target
public class Invocation {
private Object target;
private Method method;
private Object[] args;
public Invocation(Object target, Method method, Object[] args) {
this.target = target;
this.method = method;
this.args = args;
}
public Object getTarget() {
return target;
}
public Method getMethod() {
return method;
}
public Object[] getArgs() {
return args;
}
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}
自定义拦截器的intercept最后调用invocation.proceed()指向下一个拦截器或者最终的StatementHandler。