乐趣区

关于前端:聊聊Mybatis的binding模块

聊聊 Mybatis 的 binding 模块

为什么咱们在应用 Mybatis 的时候只须要写接口和 xml 文件就能执行 sql 呢?这就是 Mybatis 的 binding 模块须要做的事件了,明天咱们剖析一下 Mybatis 的 binding 模块,binding 包下的类次要有四个 MapperRegistry、MapperProxyFactory、MapperProxy 和 MapperMethod
映射注册类 MapperRegistry

MapperRegistry 是个注册类,它的 knownMappers 汇合保留着 Mapper 接口和 MapperProxyFactory 实例的映射关系

它的 addMapper() 办法就是增加映射关系的办法:

public <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException("Type" + type + "is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {knownMappers.put(type, new MapperProxyFactory<>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {if (!loadCompleted) {knownMappers.remove(type);
        }
      }
    }
  }

当传入的 type 是个接口并 knownMappers 中没有,就把 type 和对应的 MapperProxyFactory 实例放入 knownMappers 中

而后在执行 sql 的时候,Mybatis 会调用 MapperRegistry.getMapper() 办法

@SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {throw new BindingException("Type" + type + "is not known to the MapperRegistry.");
    }
    try {return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause:" + e, e);
    }
  }
 先从 knownMappers 汇合中找到对应的 MapperProxyFactory 实例

而后调用 newInstance() 办法
映射代理工厂类 MapperProxyFactory

MapperProxyFactory 的 newInstance() 办法:
public T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface}, mapperProxy);
  }

生成 mapperInterface 接口 的代理对象的实例,代理类是 MapperProxy,它实现了 InvocationHandler,利用 jdk 动静代理生成代理对象

看一下它重写的 invoke() 办法:
映射代理类 MapperProxy

MapperProxy 的 invoke() 办法:

@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);
      } else {return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);
    }
  }

不是 Object 的类会被拦挡,拦挡调用 cachedInvoker() 办法,其余执行 invoke() 办法,

private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
      return MapUtil.computeIfAbsent(methodCache, method, m -> {if (m.isDefault()) {
          try {if (privateLookupInMethod == null) {return new DefaultMethodInvoker(getMethodHandleJava8(method));
            } else {return new DefaultMethodInvoker(getMethodHandleJava9(method));
            }
          } catch (IllegalAccessException | InstantiationException | InvocationTargetException
              | NoSuchMethodException e) {throw new RuntimeException(e);
          }
        } else {return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
        }
      });
    } catch (RuntimeException re) {Throwable cause = re.getCause();
      throw cause == null ? re : cause;
    }
  }
 从 methodCache 中获取对应的 MapperMethodInvoker

如果缓存没有,如果是办法是 default 办法就创立 DefaultMethodInvoker 对象,否则创立 PlainMethodInvoker 对象
默认办法调用类 DefaultMethodInvoker

对于 DefaultMethodInvoker 类通过 MethodHandle 实现调用 
private static class DefaultMethodInvoker implements MapperMethodInvoker {
    private final MethodHandle methodHandle;

    public DefaultMethodInvoker(MethodHandle methodHandle) {super();
      this.methodHandle = methodHandle;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {return methodHandle.bindTo(proxy).invokeWithArguments(args);
    }
  }

对于一般办法对应的类 PlainMethodInvoker 通过 MapperMethod 来实现调用,

private static class PlainMethodInvoker implements MapperMethodInvoker {
    private final MapperMethod mapperMethod;

    public PlainMethodInvoker(MapperMethod mapperMethod) {super();
      this.mapperMethod = mapperMethod;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {return mapperMethod.execute(sqlSession, args);
    }
  }

总结

这篇文章次要介绍了聊聊 Mybatis 的 binding 模块,它的 MapperRegistry 保留着 Mapper 接口和 MapperProxyFactory 实例的映射关系,而 MapperProxyFactory 是一个的工厂,生成的是 Mapper 接口的代理类,MapperProxy 实现 InvocationHandler,重写 invoke() 办法进行拦挡解决,依据办法判断是不是 default 类型创立不同的 MethodInvoker 类,而后调用 MapperMethod 执行 sql,下篇文章咱们将介绍 MapperMethod 类

退出移动版