聊聊 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 类