美文网首页
Mybatis plugs (6)2018-08-21

Mybatis plugs (6)2018-08-21

作者: Seetheworl_6675 | 来源:发表于2018-08-21 20:27 被阅读0次

     Mybatis为我们提供插件技术,在我们sql执行流程过程中创建SqlSession的四大对象进行自定义代码处理的分装,实现一些特殊的需求。

    接口定义:

    我们在mybatis中使用插件都要实现Interceptor接口:

    public interface Interceptor {
      //它将拦截对象的原有方法,因此他是插件的核心方法,可以通过invocation参数反射调用原来的方法
      Object intercept(Invocation invocation) throws Throwable;
      //target是被拦截的对象,作用是给拦截对象生成一个代理对象,并返回代理对象;
      Object plugin(Object target);
      //在plugin中配置所需要的参数,方法在初始化的时候被调用一次。
      void setProperties(Properties properties);
    }
    
    插件的初始化:

    插件的初始化是在Mybatis初始化的时候完成的,代码在XMLConfigBuilder中的pluginElement(XNode parent):

     public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        try {
          XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
          //执行
          return build(parser.parse());
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
          ErrorContext.instance().reset();
          try {
            reader.close();
          } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
          }
        }
      }
    //执行
     public Configuration parse() {
        if (parsed) {
          throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
      //执行
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
      }
    //执行
    private void parseConfiguration(XNode root) {
        try {
          //issue #117 read properties first
          propertiesElement(root.evalNode("properties"));
          Properties settings = settingsAsProperties(root.evalNode("settings"));
          loadCustomVfs(settings);
          typeAliasesElement(root.evalNode("typeAliases"));
          //解析插件
          pluginElement(root.evalNode("plugins"));
          objectFactoryElement(root.evalNode("objectFactory"));
          objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
          reflectorFactoryElement(root.evalNode("reflectorFactory"));
          settingsElement(settings);
          // read it after objectFactory and objectWrapperFactory issue #631
          environmentsElement(root.evalNode("environments"));
          databaseIdProviderElement(root.evalNode("databaseIdProvider"));
          typeHandlerElement(root.evalNode("typeHandlers"));
          mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
      }
     //插件的初始化
    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);
          }
        }
      }
    
    插件的使用:

    从Sql执行流程中我们很容易明白插件的调用位置和代码:

    executor = interceptorChain.pluginAll(executor);
    
    public class InterceptorChain {
    
      private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
    //调用plugs链
      public Object pluginAll(Object target) {
        for (Interceptor interceptor : interceptors) {
          //可以根据Interceptor接口的定义返回的是代理对象
          target = interceptor.plugin(target);
        }
        return target;
      }
      //添加plugs链
      public void addInterceptor(Interceptor interceptor) {
        interceptors.add(interceptor);
      }
     //获取plugs链
      public List<Interceptor> getInterceptors() {
        return Collections.unmodifiableList(interceptors);
      }
    }
    

    这里的重点还是interceptor.plugin(target)创建代理的过程,这个创建代理的过程Mybatis给我们提供一个工具类org.apache.ibatis.plugin.Plugin可以为我们提供非常便利的创建代理对象:

    public class Plugin implements InvocationHandler {
    
      private Object target;
      private Interceptor interceptor;
      private 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;//Signature的注解信息
      }
      //静态方法获取代理
      public static Object wrap(Object target, Interceptor interceptor) {
        //解析plug实现类中的注解配置
        Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
        //获取目标对象的Class对象
        Class<?> type = target.getClass();
        //从signatureMap获取匹配的interfaces对象
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        if (interfaces.length > 0) {
          //创建代理
          return Proxy.newProxyInstance(
              type.getClassLoader(),
              interfaces,
              new Plugin(target, interceptor, signatureMap));
        }
        return target;
      }
    //InvocationHandler 额外功能方法
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
          //signatureMap中获取匹配的方法
          Set<Method> methods = signatureMap.get(method.getDeclaringClass());
          if (methods != null && methods.contains(method)) {
            //调用插件interceptor中的拦截方法
            return interceptor.intercept(new Invocation(target, method, args));
          }
          //直接待用目标方法
          return method.invoke(target, args);
        } catch (Exception e) {
          throw ExceptionUtil.unwrapThrowable(e);
        }
      }
      //解析plug实现类中的注解配置
      private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
        //获取Interceptes注解
        Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
        // plug实现类不存在Intercepts注解 则抛出异常
        if (interceptsAnnotation == null) {
          throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());      
        }
         //获取配置的Singature数组
        Signature[] sigs = interceptsAnnotation.value();
        //容器用于保持目标对象和注解匹配成功信息
        //key :interceptor中要拦截的目标类(sqlSession的四大对象)
        //value:要拦截的方法
        Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
        //便利注解中配置的Signature数组
        for (Signature sig : sigs) {
          Set<Method> methods = signatureMap.get(sig.type());
          if (methods == null) {
            methods = new HashSet<Method>();
            signatureMap.put(sig.type(), methods);
          }
          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<Class<?>>();
        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()]);
      }
    
    }
    

    插件工具类注解案例:

    //Intercepts 注解
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface Intercepts {
      Signature[] value();
    }
    //Signature注解
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Signature {
      Class<?> type();
    
      String method();
    
      Class<?>[] args();
    }
    
    //案例
    @Intercepts({
    @Signature(type=Executor.class,
    method= "query", 
    args = { MappedStatement.class, Object.class,RowBounds.class, 
    ResultHandler.class }),
    @Signature(type = Executor.class, 
    method = "update", 
    args = { MappedStatement.class, Object.class }) })
    

    相关文章

      网友评论

          本文标题:Mybatis plugs (6)2018-08-21

          本文链接:https://www.haomeiwen.com/subject/tspriftx.html