Mybatis系列9-mybatis插件源码分析

mybatis的插件开发使用了责任链模式,老规矩先介绍设计模式。

1. 责任链模式

责任链模式(Chain of Responsibility Pattern)为请求创建了一个处理请求的链。这个请求由责任链中的处理器一个接一个的处理。
责任链中的角色:

  • Handler:处理请求的标准接口
  • ConcreteHandler:具体的处理者,并将请求转发给它的后继者
  • client :发起请求的客户端
    Mybatis系列9-mybatis插件源码分析_第1张图片

2. mybatis插件

实现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是个有序集合,所以配置文件中配置的顺序就代表了插件的顺序。

2.插件的调用

在我们从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的后置方法。

3.插件能拦截的类

mybatis能拦截的方法只能是Executor、StatementHandler、ParameterHandler、ResultSetHandler四个类。
为什么只能拦截这四个类,因为mybatis在实现的时候只在创建这四个类的地方调用了拦截器。这四个类创建的方法入口都在Configuration对象中。
前面我们看到了Executor的创建,下面看下剩下三个。

3.1StatementHandler

创建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;
  }

最后一步调用了拦截器。

3.2 ParameterHandler

创建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;
  }

最后一步调用了拦截器。

3.3 ResultSetHandler

创建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;
  }

最后一步调用了拦截器。

你可能感兴趣的:(mybatis)