当我们在使用 MyBatis 的时候,通常会先定义一个包含 SQL 语句的 XML 文件(也可以使用注解方式实现),这个文件中包含了 select、update、delete、insert 等与数据库操作相关的语句。
MyBatis 通过读取这个 XML 文件,将其中定义的 SQL 语句解析成对应的 MappedStatement 对象,并存储在 Configuration 对象中。
在使用 MyBatis 进行数据库操作时,会先通过 SqlSession 对象获取到对应的 Mapper 接口,然后通过该接口上的方法调用执行相应的 SQL 语句。但实际上这些方法定义时是一个个接口方法,并没有实现,因此在执行这些方法时,需要使用动态代理来实现这些接口方法的执行。
MyBatis 通过 Java 的 Proxy 类动态生成 Mapper 接口的代理对象,并将该代理对象返回给客户端使用。Mapper 接口的代理对象会拦截所有对接口方法的调用,并把这些调用转发给 MyBatis 的 SqlSession,SqlSession 会负责执行与调用相关的一些 SQL 语句,并返回结果给代理对象,最终返回给客户端。因此,从客户端来看,就好像是直接调用了接口方法一样,这就是动态代理的魔力所在。
具体来说,当 MyBatis 调用 Mapper 接口方法时,会将对应的 MapperMethod 对象封装成对应的 Invocation 对象传递给代理对象的 invoke 方法,从而通过代理对象回调 MapperProxy 的 invoke 方法。MapperProxy 的 invoke 方法会获取 SqlSession 并根据调用 Mapper 接口方法的相关信息获取对应的 MappedStatement,然后再通过 Executor 和 StatementHandler 将 MappedStatement 中封装的 SQL 语句交给 JDBC 执行,最后将 JDBC 执行结果转化为 Mapper 接口方法执行结果并返回。
代理类 MapperProxy 实现了 InvocationHandler 接口,该接口有一个 invoke() 方法,代理对象在调用某个方法时,都会通过 invoke() 方法回调到具体的处理类中进行处理,从而实现动态代理。具体的 MapperProxy 代码,示例如下:
public class MapperProxy<T> implements InvocationHandler {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取 MapperMethod
MapperMethod mapperMethod = sqlSession.getMapperMethod(mapperInterface, method);
// 执行 SQL 并返回结果
return mapperMethod.execute(sqlSession, args);
}
}
最终,MyBatis 会动态生成一个实现了 Mapper 接口的代理对象,并将该代理对象返回给调用方使用。这就是 Mapper 接口的工作原理。
MyBatis通过XML文件或注解定义SQL语句,解析成MappedStatement对象存储在Configuration中。使用SqlSession获取Mapper接口,通过动态代理实现接口方法的执行。MapperProxy作为代理类,拦截接口方法调用,执行SQL并通过SqlSession处理结果。MapperMethod负责执行SQL并返回结果。

1073

被折叠的 条评论
为什么被折叠?



