自己创一个demo,然后在:Student student = studentDao.queryById();打断点
...
public static void main(String[] args) throws IOException {
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = builder.build(Resources.getResourceAsReader("mybatis-config.xml"));
SqlSession sqlSession = build.openSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = studentDao.queryById();
System.out.println(student);
sqlSession.commit();
sqlSession.close();
}
...
1.可以看到它调用的是MapperProxy


2.进入到MapperProxy这里可以看到是invoke 该方法。
方法会先判断该方法是不是一个object类。如果为true直接调用。为false的话从缓存调用器里调用该方法。


3.我们可以看看它是为false的。

MethodProxy中的逻辑代理
MapperProxy.invoke() 方法是代理对象的执行入口,它会拦截所有非object类型的方法。而且针对每个方法都会调用cacheInvoked()方法获取对应的MapperMethod对象。并调用invoke()方法执行对应的代理逻辑及目标方法。

从上图可以看到DefaultMethodInvoker和PlainMethodInvoker都是MapperMethodInvoker的实现类。
通过对 MapperProxy 的分析我们知道,MapperMethod 是最终执行 SQL 语句的地方,同时也记录了 Mapper 接口中的对应方法,其核心字段也围绕这两方面的内容展开。
** SqlCommand**
MapperMethod 的第一个核心字段是 command(SqlCommand 类型),其中维护了关联 SQL 语句的相关信息。在 MapperMethod$SqlCommand 这个内部类中,通过 name 字段记录了关联 SQL 语句的唯一标识,通过 type 字段(SqlCommandType 类型)维护了 SQL 语句的操作类型,这里 SQL 语句的操作类型分为 INSERT、UPDATE、DELETE、SELECT 和 FLUSH 五种。
下面我们就来看看 SqlCommand 如何查找 Mapper 接口中一个方法对应的 SQL 语句的信息,该逻辑在 SqlCommand 的构造方法中实现,如下:

相对应的解析
//获取接口对应的名称
final String methodName = method.getName();
//获取Mapper接口的类型
final Class<?> declaringClass = method.getDeclaringClass();
// 将Mapper接口名称和方法名称拼接起来作为SQL语句唯一标识,
// 到Configuration这个全局配置对象中查找SQL语句
// MappedStatement对象就是Mapper.xml配置文件中一条SQL语句解析之后得到的对象
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
// 针对@Flush注解的处理
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
// 没有@Flush注解,会抛出异常
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
// 记录SQL语句唯一标识
name = ms.getId();
// 记录SQL语句的操作类型
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
这里调用的 resolveMappedStatement() 方法不仅会尝试根据 SQL 语句的唯一标识从 Configuration 全局配置对象中查找关联的 MappedStatement 对象,还会尝试顺着 Mapper 接口的继承树进行查找,直至查找成功为止。具体实现如下:

网友评论