O princípio de realização e otimização do plug-in Mybatis

Princípio de implementação do plug-in 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. Carregue o interceptor configurado no xml durante a inicialização e adicione-o à coleção de interceptores do InterceptorChain,
  2. Na criação ParameterHandler, ResultSetHandler, StatementHandler, Executorchamado quando um objeto pluginAll(Object target)método, atravessando a Interceptorexecução org.apache.ibatis.plugin.Interceptor#plugin,
  3. Esse método é chamado de org.apache.ibatis.plugin.Plugin#wrap(Object target, Interceptor interceptor)analítica interna Interceptordefinida em @Interceptse as @Signatureanotações para analisar os métodos precisam ser interceptadas, seu proxy dinâmico JDK, a classe de proxy com a java.lang.reflect.InvocationHandlerrealização, ou seja org.apache.ibatis.plugin.Plugin, targetcomo uma preservação de plug-in de variável membro.
  4. Ao percorrer a coleção de interceptores para criar uma classe de proxy, o proxy de destino retornado pelo Interceptor anterior será usado como a classe de destino do próximo Interceptor.
  5. No momento da execução, é julgado se o método interceptado precisa ser executado interceptore, se necessário , é executado. Se você não precisar disso, chame o método da classe de destino de destino diretamente.
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);
    }
  }
  ......
}

Desvantagens da implementação do plug-in Mybatis

Na chamada de ciclo interceptor.plugin(target);, se houver vários Interceptorintercepção do mesmo a mesma interface ( ParameterHandler, ResultSetHandler, StatementHandler, Executorum), porque cada vez que uma passagem é o target = interceptor.plugin(target)objecto devolvido, irá resultar na classe de destino é o agente várias vezes. Isso realmente forma uma cadeia de interceptor proxy dinâmico

ProxyA(Hnadler)
       --> ProxyB(Hnadler)
              --> ProxyC(Hnadler)
                     --> ProxyD(Hnadler)
                            ......
  1. Em primeiro lugar, o desempenho da chamada do método da classe proxy não é tão eficiente quanto o método de chamar diretamente a classe de destino e, quanto mais proxy, pior é o desempenho. @See PR
  2. Fazer proxy da mesma classe várias vezes resultará em uma estrutura de instância de objeto complexa, que não conduz à análise da lógica de execução do plug-in.

O plug-in Mybatis realiza otimização

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

Acho que você gosta

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