Mybatis 插件实现原理及优化

Mybatis 插件实现原理

org.apache.ibatis.plugin.InterceptorChain

public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<>();

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }
}
  1. 在初始化时加载xml中配置的拦截器,添加到InterceptorChain的interceptors集合中,
  2. 在创建ParameterHandler, ResultSetHandler, StatementHandler, Executor对象时调用pluginAll(Object target)方法,遍历Interceptor 执行org.apache.ibatis.plugin.Interceptor#plugin
  3. 该方法内部调用org.apache.ibatis.plugin.Plugin#wrap(Object target, Interceptor interceptor)解析Interceptor上定义的@Intercepts@Signature注解来解析需要被拦截的方法,对其使用JDK动态代理,代理类的java.lang.reflect.InvocationHandler实现即为org.apache.ibatis.plugin.Plugintarget做为Plugin的成员变量保存。
  4. 在遍历interceptors集合创建代理类时,上一个Interceptor返回的target代理会做为下一个Interceptor的目标类。
  5. 在执行时判断被拦截的方法是否需要执行interceptor,需要则执行。不需要则直接调用target目标类的方法。
public class Plugin implements InvocationHandler {

  private final Object target;
  private final Interceptor interceptor;
  private final 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;
  }

  @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);
    }
  }
  ......
}

Mybatis 插件实现弊端

在循环调用interceptor.plugin(target);时, 如果存在多个Interceptor拦截的即使是同一个接口(ParameterHandler, ResultSetHandler, StatementHandler, Executor其中一个),因为每次传入的是上一个target = interceptor.plugin(target)返回的对象,会导致目标类被代理多次。这实际上形成了一个动态代理的拦截器链

ProxyA(Hnadler)
       --> ProxyB(Hnadler)
              --> ProxyC(Hnadler)
                     --> ProxyD(Hnadler)
                            ......

在这里插入图片描述

  1. 首先代理类的方法调用在性能上不如直接调用目标类的方法高效,并且代理越多性能越差。@See PR
  2. 多次代理同一个类,会导致对象实例结构复杂,不利于分析插件的执行逻辑。

Mybatis 插件实现优化

com.bruce.mybatis.plugin.optimize.OptimizedPlugin

import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.reflection.ExceptionUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author bruce lwl 2023/4/6 23:43
 * @see Plugin
 */
public class OptimizedPlugin implements InvocationHandler {

    private final Object target;
    private final Map<Method, List<Interceptor>> methodInterceptors;

    public OptimizedPlugin(Object target) {
        this.target = target;
        this.methodInterceptors = new HashMap<>();
    }

    public void addMethodInterceptors(Map<Method, List<Interceptor>> interceptors) {
        for (Map.Entry<Method, List<Interceptor>> entry : interceptors.entrySet()) {
            List<Interceptor> interceptors1 = methodInterceptors.computeIfAbsent(entry.getKey(), key -> new ArrayList<>());
            interceptors1.addAll(entry.getValue());
        }
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            List<Interceptor> interceptors = methodInterceptors.get(method);
            if (interceptors != null) {
                return new InvocationChain(interceptors, target, method, args).proceed();
            }
            return method.invoke(target, args);
        } catch (Exception e) {
            throw ExceptionUtil.unwrapThrowable(e);
        }
    }


    /**
     * @see Plugin#wrap(Object, Interceptor)
     */
    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) {
            if (Proxy.isProxyClass(type) && Proxy.getInvocationHandler(target) instanceof OptimizedPlugin) {
                OptimizedPlugin optimizedPlugin = (OptimizedPlugin) Proxy.getInvocationHandler(target);
                Map<Method, List<Interceptor>> methodInterceptors = getMethodInterceptors(interfaces, signatureMap, interceptor);
                optimizedPlugin.addMethodInterceptors(methodInterceptors);
                return target;
            }
            OptimizedPlugin optimizedPlugin = new OptimizedPlugin(target);
            Map<Method, List<Interceptor>> methodInterceptors = getMethodInterceptors(interfaces, signatureMap, interceptor);
            optimizedPlugin.addMethodInterceptors(methodInterceptors);
            return Proxy.newProxyInstance(type.getClassLoader(), interfaces, optimizedPlugin);
        }
        return target;
    }

    /**
     * @param interfaces   对正在对哪些接口代理
     * @param signatureMap 指定哪些接口有哪些方法是被拦截的
     * @param interceptor  Interceptor实例对象, 由用户定义拦截时的具体逻辑
     * @return
     */
    private static Map<Method, List<Interceptor>> getMethodInterceptors(Class<?>[] interfaces, Map<Class<?>, Set<Method>> signatureMap, Interceptor interceptor) {
        HashMap<Method, List<Interceptor>> methodInterceptors = new HashMap<>();
        for (Class<?> anInterface : interfaces) {
            Set<Method> methods = signatureMap.get(anInterface);
            for (Method method : methods) {
                methodInterceptors.computeIfAbsent(method, (key) -> new ArrayList<>()).add(interceptor);
            }
        }
        return methodInterceptors;
    }

    /**
     * 返回目标类,哪些方法是被拦截的
     */
    @SuppressWarnings("unchecked")
    private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
        try {
            Method getSignatureMapMethod = Plugin.class.getDeclaredMethod("getSignatureMap", Interceptor.class);
            getSignatureMapMethod.setAccessible(true);
            return (Map<Class<?>, Set<Method>>) getSignatureMapMethod.invoke(Plugin.class, interceptor);
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new RuntimeException();
    }

    private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
        try {
            Method getAllInterfacesMethod = Plugin.class.getDeclaredMethod("getAllInterfaces", Class.class, Map.class);
            getAllInterfacesMethod.setAccessible(true);
            return (Class<?>[]) getAllInterfacesMethod.invoke(Plugin.class, type, signatureMap);
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new RuntimeException();
    }

}

com.bruce.mybatis.plugin.optimize.OptimizedInterceptor

public interface OptimizedInterceptor extends Interceptor {

    @Override
    default Object plugin(Object target) {
        return OptimizedPlugin.wrap(target, this);
    }

}

InvocationList

package org.apache.ibatis.plugin;

import org.apache.ibatis.reflection.ExceptionUtil;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

public class InvocationList extends Invocation {

    private final List<Interceptor> interceptors;

    int index = 0;

    public InvocationList(List<Interceptor> interceptors, Object target, Method method, Object[] args) {
        super(target, method, args);
        this.interceptors = interceptors;
        index = interceptors.size();
    }

    @Override
    public Object proceed() throws InvocationTargetException, IllegalAccessException {
        if ((index--) == 0) {
            return super.proceed();
        }
        Interceptor interceptor = interceptors.get(index);
        Object result = null;
        try {
            result = interceptor.intercept(this);
        } catch (Throwable throwable) {
            try {
                throw ExceptionUtil.unwrapThrowable(throwable);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}

优化后效果
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值