意见箱
恒创运营部门将仔细参阅您的意见和建议,必要时将通过预留邮箱与您保持联络。感谢您的支持!
意见/建议
提交建议

聊聊Mybatis的binding模块

来源:恒创科技 编辑:恒创科技编辑部
2024-01-23 06:39:59

聊聊Mybatis的binding模块

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

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


聊聊Mybatis的binding模块

它的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

当传入的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: "
先从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

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

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

MapperProxy的invoke()方法:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws{
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

不是Object的类会被拦截,拦截调用cachedInvoker()方法,其他执行invoke()方法,

private MapperMethodInvoker cachedInvoker(Method method) throws{
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
从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{
return

对于普通方法对应的类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{
return

总结

这篇文章主要介绍了聊聊Mybatis的binding模块,它的MapperRegistry保存着Mapper接口和MapperProxyFactory实例的映射关系,而MapperProxyFactory是一个的工厂,生成的是Mapper接口的代理类,MapperProxy实现InvocationHandler,重写invoke()方法进行拦截处理,根据方法判断是不是default类型创建不同的MethodInvoker类,然后调用MapperMethod执行sql,下篇文章我们将介绍MapperMethod类


上一篇: 数据分表Mybatis Plus动态表名最优方案的探索 下一篇: 手机怎么远程登录云服务器?