MyBatis 源码学习6——SqlSession执行Mapper过程(上)

Mapper由两部分组成:Mapper接口和通过注解或者XML文件配置的SQL语句。

SqlSession执行Mapper过程主要分为4个阶段
MyBatis 源码学习6——SqlSession执行Mapper过程(上)

1.Mapper接口的注册过程,
2.MappedStatement对象的注册过程,
3.Mapper方法的调用过程,
4.SqlSession执行Mapper的过程。

一、Mapper接口的注册过程

Mapper接口:定义执行SQL语句相关的方法,方法名一般和Mapper XML配置文件中<select|update|delete|insert>标签的id属性相同,接口的完全限定名一般对应Mapper XML配置文件的命名空间。

执行Mapper中定义的方法

1.在创建SqlSession实例后,需要调用SqlSession的getMapper()方法获取一个Mapper的引用,即一个MapperProxy的实例。
MyBatis 源码学习6——SqlSession执行Mapper过程(上)

注意:接口中定义的方法必须通过某个类实现该接口,然后创建该类的实例,再通过实例调用方法,所以SqlSession对象的getMapper()方法返回的一定是一个动态代理对象。

2.然后通过该引用调用Mapper接口中定义的方法。

SqlSession对象getMapper()方法的实现:configuration.getMapper(type, this)

再查看Configuration对象的getMapper()方法,其实现:
mapperRegistry.getMapper(type, sqlSession);

MyBatis 源码学习6——SqlSession执行Mapper过程(上)

要看懂上面的代码,先了解下Configuration类的属性类MapperRegistry mapperRegistry
MyBatis 源码学习6——SqlSession执行Mapper过程(上)

MapperRegistry类有一个变量Map<Class<?>, MapperProxyFactory<?>> knownMappers
用于注册Mapper接口对应的Class对象和MapperProxyFactory对象之间的关系。

MapperRegistry类提供了addMapper()方法
用于向knownMappers属性中注册Mapper接口信息,MyBatis框架在应用启动时会解析所有的Mapper接口,然后调用此方法。

在addMapper()方法中,每个Mapper接口为对应的Class对象创建一个MapperProxyFactory对象,然后添加到knownMappers属性中。

MapperRegistry类提供了getMapper()方法
能够根据Mapper接口的Class对象获取对应的MapperProxyFactory,然后就可以使用MapperProxyFactory对象调用方法newInstance(SqlSession sqlSession)创建Mapper动态代理对象了。
MyBatis 源码学习6——SqlSession执行Mapper过程(上)

注意:
Java语言中比较常用的实现动态代理的方式有两种,即JDK内置动态代理和CGLIB动态代理。

MyBatis中通过MapperProxy类实现动态代理,使用的是JDK内置的动态代理,MapperProxy类实现了InvocationHandler接口,invoke()方法中为通用的拦截逻辑。

使用JDK内置动态代理,通过MapperProxy类实现InvocationHandler接口,定义方法执行拦截逻辑后,还需要调用java.lang.reflect.Proxy类的newProxyInstance()方法创建代理对象。

二、Mapper SQL配置信息,即MappedStatement注册过程

MyBatis通过MappedStatement类描述Mapper的SQL配置信息,其中SQL配置有两种方式:
一种是通过XML文件配置;另一种是通过Java注解。
MyBatis 源码学习6——SqlSession执行Mapper过程(上)

先了解下Configuration类的属性Map<String, MappedStatement> mappedStatements
用于注册MyBatis中所有的MappedStatement对象,其中
Key:Mapper SQL配置的Id,如果SQL是通过XML配置的,则Id为命名空间加上<select|update|delete|insert>标签的Id,如果SQL通过Java注解配置,则Id为Mapper接口的完全限定名(包括包名)加上方法名称

Configuration类中提供了addMappedStatement()方法:将MappedStatement对象添加到mappedStatements属性中。
MyBatis 源码学习6——SqlSession执行Mapper过程(上)

下面开始详解MyBatis中Mapper SQL配置信息(MappedStatement对象)的注册过程,即MappedStatement对象的创建及注册过程:
Configuration对象创建过程中,通过XMLConfigBuilder对象来解析MyBatis主配置文件,其**parseConfiguration()**方法中会调用mapperElement(root.evalNode(“mappers”))解析<mappers>标签。

详细看下mapperElement()方法的实现:
MyBatis 源码学习6——SqlSession执行Mapper过程(上)
1.首先通过root.evalNode(“mappers”)获取<mappers>所有子标签(<mapper>标签或<package>标签)。

2.因为<mappers>标签配置Mapper信息有以下几种方式,所以mapperElement()方法中对这几种情形的配置分别做了处理,接下来以<mapper resource="……"/>这种形式为例介绍Mapper SQL配置文件的解析过程。

3.根据resonrce中的地址找到Mapper接口,以Mapper接口的输入流作为参数,创建一个XMLMapperBuilder对象 。

4.调用XMLMapperBuilder对象的parse()方法完成解析

详细看下parse()方法的实现:
MyBatis 源码学习6——SqlSession执行Mapper过程(上)

1.首先调用XPathParser对象的evalNode()方法获取根节点对应的XNode对象

2.接着调用configurationElement()方法对Mapper配置内容做进一步解析。

configurationElement()方法中,对Mapper SQL配置文件的所有标签进行解析,这里重点关注<select|insert|update|delete>标签的解析过程:
MyBatis 源码学习6——SqlSession执行Mapper过程(上)

1.获取<select|insert|update|delete>标签节点对应的XNode对象后,

2.调用XMLMapperBuilder类的buildStatementFromContext()方法做进一步解析处理。

buildStatementFromContext()方法的实现:
MyBatis 源码学习6——SqlSession执行Mapper过程(上)
1.遍历所有XNode对象,为每个<select|insert|update|delete>标签对应的XNode对象创建一个XMLStatementBuilder对象,

2.接着调用XMLStatementBuilder对象的parseStatementNode()方法进行解析处理。

XMLStatementBuilder类的parseStatementNode()方法的实现:

MyBatis 源码学习6——SqlSession执行Mapper过程(上)
MyBatis 源码学习6——SqlSession执行Mapper过程(上)

MyBatis 源码学习6——SqlSession执行Mapper过程(上)

1.获取<select|insert|delete|update>标签的所有属性信息。

2.将<include>标签引用的SQL片段替换为对应的<sql>标签中定义的内容。

3.获取lang属性指定的LanguageDriver,通过LanguageDriver创建SqlSource。MyBatis中的SqlSource表示一个SQL资源。

4.获取KeyGenerator对象。KeyGenerator的不同实例代表不同的主键生成策略。

5.所有解析工作完成后,使用MapperBuilderAssistant对象的addMappedStatement()方法创建MappedStatement对象。创建完成后,调用Configuration对象的addMappedStatement()方法将MappedStatement对象注册到Configuration对象中。

需要注意的是,MyBatis中的MapperBuilderAssistant是一个辅助工具类,用于构建Mapper相关的对象,例如Cache、ParameterMap、ResultMap等。