MyBatis插件原理-源码解读

来源:互联网 发布:redis查询所有数据库 编辑:程序博客网 时间:2024/06/07 02:29

MyBatis支持配置多个插件动态添加新的功能,因为存在InterceptorChain,很多人认为Mybatis采用责任链模式,看了源码后我觉得更像是装饰器模式。

Mybatis支持对ExecutorStatementHandlerPameterHandlerResultSetHandler进行拦截。下面依旧以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<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方法:

  @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。



原创粉丝点击