Mybatis的sqlSession.getMapper()的原理

235 阅读1分钟

为什么sqlSession.getMapper()能够得到对象?

在mybatis开发的过程中,我们只是定义了接口,但是却可以调用接口的方法去完成功能,因为接口无法实例化,那么必然mybatis做了特殊处理。

mybatis通过getMapper()实际上生成了代理对象

首先是要了解代理对象使用的场景
1、无中生有 - 就是mybatis这种情况,没有对象,要根据接口生成对象
2、要为原始对象增加与主逻辑无关的功能,如事务、日志
3、远程代理 (RPC)

实现原理

实现原理非常简单,就是使用jdk的动态代理,动态字节码技术,在spring aop中也是同样的原理

// classLoader 类加载器
// interfaces 代理类要实现的接口
// invocation 额外功能
Proxy.newProxyInstance(classLoader, interfaces, invocation)

/**
classLoader作用 1、加载.class文件到JVM 2、根据.class文件创建对象
因为代理对象没有.class文件,又因为classLoader是与.class文件相关联的,所以代理对象是没有classLoader与其对应的。动态字节码技术就是直接把.class文件生成到JVM中。
所以此处的classLoader是可以借用任意类的加载器
**/

Mybatis源码

MapperProxyFactory 和 MapperProxy

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}
// 实现InvocationHandler就是提供的额外功能
// 要提供的额外功能无非就是sql的执行,执行就需要sqlSession,namespace.id,执行的sql类型
// 最后要根据CURD类型,也就是sql标签中的类型,决定是sqlSession.select()|.update|.delete
public class MapperProxy<T> implements InvocationHandler, Serializable {
}

总结

1. sqlSession.getMapper(UserDAO.class);底层是通过动态字节码也就是动态代理得到的代理对象。
2. 代理对象执行sql底层是通过sqlSession来实现的。