Mybatis插件原理

如果对Mabatis运行流程不熟悉的,可以参考Mybatis执行流程,源码分析

pluginAll方法

在以下四大对象的创建过程中,出现了pluginAll方法

  • Excutor
    在Configuration的newExecutor方法中
  • StatementHandler
    在Configuration的newStatementHandler方法中
  • ParameterHandler
    在BaseStatementHandler的构造方法中
  • ResultSetHandler
    在BaseStatementHandler的构造方法中
    其中,
  • ParameterHandler作为Statementhandler的成员,用以设置statement参数
  • ResutSetHandler作为StatementHandler的成员,用以处理statement执行后的ResultSet
    pluginAll方法代码也比较简单
public Object pluginAll(Object target) {
  for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
  }
  return target;
}

看看Interceprtor拦截器接口
inteceptor.png
看到这里,应该很明确了,编写Mybatis的插件,也是通过代理实现的,Mybatis甚至为我们写好了代理类的创建过程

Plugin的wrap方法

public static Object wrap(Object target, Interceptor interceptor) {
    /* 解析注解,获取拦截器需要拦截的方法 */
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    /* JDK动态代理,必须实现接口 */
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      /* 返回代理对象 */
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
   return target;
}

所以,我们得到的是一个Plugin对象,实现了invocationHandler接口,其中包含如下信息
Plugin.png

Plugin类的invoke方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        Set<Method> methods = signatureMap.get(method.getDeclaringClass());
	  /* 我们的interceptor拦截目标方法,通过Invocation */
	  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);
	}
}

再次感叹,动态代理真无处不在!

Q.E.D.


一切很好,不缺烦恼。