<返回更多

聊聊Mybatis的binding模块

2022-08-12    Java那点事儿
加入收藏

聊聊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);    }  }
  1. 先从knownMappers集合中找到对应的MapperProxyFactory实例
  2. 然后调用newInstance()方法
  3. 映射代理工厂类MapperProxyFactory
  4. 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;    }  }
  1. 从methodCache中获取对应的MapperMethodInvoker
  2. 如果缓存没有,如果是方法是default方法就创建DefaultMethodInvoker对象,否则创建PlainMethodInvoker对象
  3. 默认方法调用类DefaultMethodInvoker
  4. 对于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类

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>