MyBatis支持配置多个插件动态添加新的功能,由于存在InterceptorChain,不少人认为Mybatis采用责任链模式,看了源码后我以为更像是装饰器模式。java
Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler进行拦截。下面依旧以StatementHandler类型的SQLStatsInterceptor为例:sql
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<Interceptor> 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<Interceptor> 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<Class<?>, Set<Method>> signatureMap; private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) { this.target = target; this.interceptor = interceptor; this.signatureMap = signatureMap; } public static Object wrap(Object target, Interceptor interceptor) { Map<Class<?>, Set<Method>> 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方法:apache
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Set<Method> 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。