El principio de realización y optimización del complemento Mybatis

Principio de implementación del complemento 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. Cargue el interceptor configurado en xml durante la inicialización y agréguelo a la colección de interceptores de InterceptorChain,
  2. En la creación ParameterHandler, ResultSetHandler, StatementHandler, Executorllamada cuando un objeto pluginAll(Object target)método, que atraviesa la Interceptorejecución org.apache.ibatis.plugin.Interceptor#plugin,
  3. Este método se llama org.apache.ibatis.plugin.Plugin#wrap(Object target, Interceptor interceptor)analítico interno Interceptordefinido en @Interceptsy las @Signatureanotaciones para analizar los métodos deben ser interceptadas, su proxy dinámico JDK, la clase de proxy con la java.lang.reflect.InvocationHandlerrealización org.apache.ibatis.plugin.Plugin, es decir , targetcomo una preservación del complemento de variable miembro.
  4. Al atravesar la colección de interceptores para crear una clase de proxy, el proxy de destino devuelto por el Interceptor anterior se utilizará como la clase de destino del siguiente Interceptor.
  5. En el momento de la ejecución, se juzga si es necesario ejecutar el método interceptado interceptory, si es necesario , se ejecuta. Si no lo necesita, llame directamente al método de la clase de destino objetivo.
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);
    }
  }
  ......
}

Inconvenientes de implementación del complemento Mybatis

En la llamada del ciclo interceptor.plugin(target);, si hay múltiples Interceptorinterceptación de incluso la misma interfaz ( ParameterHandler, ResultSetHandler, StatementHandler, Executoruno), porque cada vez que se la un pase target = interceptor.plugin(target)volvió objeto, dará lugar a la clase objetivo es el agente varias veces. Esto en realidad forma una cadena de interceptores proxy dinámica

ProxyA(Hnadler)
       --> ProxyB(Hnadler)
              --> ProxyC(Hnadler)
                     --> ProxyD(Hnadler)
                            ......
  1. En primer lugar, el rendimiento de la llamada al método de la clase de proxy no es tan eficiente como el método de llamar directamente a la clase de destino, y cuanto más proxy, peor es el rendimiento. @Ver PR
  2. El proxy de la misma clase varias veces dará como resultado una estructura de instancia de objeto compleja, que no es propicia para analizar la lógica de ejecución del complemento.

El complemento Mybatis realiza la optimización

org.apache.ibatis.plugin.Plugin

package org.apache.ibatis.plugin;

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.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.ibatis.reflection.ExceptionUtil;

/**
 * @author bruce lwl
 */
public class Plugin implements InvocationHandler {
    
    

    private final Object target;
    //private final Interceptor interceptor;
    //private final Map<Class<?>, Set<Method>> signatureMap;
    private final Map<Method, List<Interceptor>> interceptorMap;

    //private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
    
    
    private Plugin(Object target, Interceptor interceptor, Map<Method, List<Interceptor>> interceptorMap) {
    
    
        this.target = target;
        //this.interceptor = interceptor;
        //this.signatureMap = signatureMap;
        this.interceptorMap = interceptorMap;
    }

    public Map<Method, List<Interceptor>> getInterceptorMap() {
    
    
        return interceptorMap;
    }

    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(target.getClass())) {
    
    
                InvocationHandler invocationHandler = Proxy.getInvocationHandler(target);
                if (invocationHandler instanceof Plugin){
    
    
                    Map<Method, List<Interceptor>> interceptorMap = ((Plugin) invocationHandler).getInterceptorMap();
                    mapping(interceptor, signatureMap, interceptorMap);
                    return target;
                }
            }

            Map<Method, List<Interceptor>> interceptorMap = new HashMap<>();
            mapping(interceptor, signatureMap, interceptorMap);
            return Proxy.newProxyInstance(
                    type.getClassLoader(),
                    interfaces,
                    new Plugin(target, interceptor, interceptorMap));
        }
        return target;
    }

    /**
     * @param interceptor Interceptor实例对象
     * @param signatureMap 被拦截的接口有哪些方法是被拦截的
     * @param interceptorMap 被拦截的方法,对应哪些Interceptor实例对象
     */
    private static void mapping(Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap, Map<Method, List<Interceptor>> interceptorMap) {
    
    
        for (Set<Method> methods : signatureMap.values()) {
    
    
            for (Method method : methods) {
    
    
                interceptorMap.computeIfAbsent(method, (key) -> new ArrayList<>()).add(interceptor);
            }
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        try {
    
    
            List<Interceptor> interceptors = interceptorMap.get(method);
            if (interceptors != null) {
    
    
                return new InvocationList(interceptors, target, method, args).proceed();
            }
            //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);
        }
    }

    private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
    
    
        Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
        // issue #251
        if (interceptsAnnotation == null) {
    
    
            throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
        }
        Signature[] sigs = interceptsAnnotation.value();
        Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
        for (Signature sig : sigs) {
    
    
            Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
            try {
    
    
                Method method = sig.type().getMethod(sig.method(), sig.args());
                methods.add(method);
            } catch (NoSuchMethodException e) {
    
    
                throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
            }
        }
        return signatureMap;
    }

    private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
    
    
        Set<Class<?>> interfaces = new HashSet<>();
        while (type != null) {
    
    
            for (Class<?> c : type.getInterfaces()) {
    
    
                if (signatureMap.containsKey(c)) {
    
    
                    interfaces.add(c);
                }
            }
            type = type.getSuperclass();
        }
        return interfaces.toArray(new Class<?>[interfaces.size()]);
    }

}

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

Supongo que te gusta

Origin blog.csdn.net/u013202238/article/details/107743698
Recomendado
Clasificación