Mybatis执行SQL流程(四)之MyBatis中JDK动态代理
文章目录
- 动态代理
- 代理对象的创建过程
动态代理
Mybatis使用JDK动态代理为Mapper 接口创建代理对象。当调用Mapper接口方法时,实际上调用的是代理对象的方法,代理对象会将方法调用转发给Mybatis的核心执行器。
- MapperProxy:实现了 InvocationHandler 接口,是Mybatis动态代理的核心类
- MapperProxyFactory :负责创建Mapper 接口的代理对象
- MapperRegistry:管理所有Mapper 接口及其对应的代理工厂。
代理对象的创建过程
1、初始阶段:
- Springboot项目启动时解析所有的Mapper接口和映射文件
- 为每个Mapper接口创建MapperProxyFactory,并注册到 MapperRegistry:
Map<Class<?>, MapperProxyFactory<?>> knownMappers
2、获取Mapper 实例:
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);// 大致实现内容如下:
-
通过MapperRegistry 获取对应的 MapperProxyFactory
-
使用MapperProxyFactory 创建代理对象
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); // 获取对应的 MapperProxyFactoryif (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}try {return mapperProxyFactory.newInstance(sqlSession); // MapperProxyFactory 创建代理对象} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}}
// 其中newInstance:
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);// 构建代理类class,并实例化一个对象。JDK动态代理的具体流程见另一篇文章。
// 动态代理:所有 Mapper 方法调用会被拦截并转发给 MapperProxy.invoke()。 此处涉及到代理类的源码,见后续内容。
JDK动态代理的具体流程见另一篇文章。
此处做简单的说明:
为接口生成一个实现类的class文件的字节码,并加载到JVM中,在字节码文件中,重写了接口的方法:
public final void methodName() {super.h.invoke(this, MethodObject, // 对应方法的Method对象args); // 方法参数
}
所以当使用代理对象调用接口中的方法时,执行的其实是invocationHandler对象中的invoke方法,而此时invocationHandler对象是mapperProxy对象。具体执行内容见MapperProxy。
3、方法调用流程
当调用mapper接口方法时:
- 代理对象拦截方法调用
- 创建MapperMethod对象(封装了SQL命令和执行逻辑)
- 执行
MapperMethod.execute()
方法 - 根据方法类型(select/insert/update/delete)调用SqlSession的相应方法
- 返回执行结果