MyBatis 源码学习6——SqlSession执行Mapper过程(上)
Mapper由两部分组成:Mapper接口和通过注解或者XML文件配置的SQL语句。
SqlSession执行Mapper过程主要分为4个阶段:
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的实例。
注意:接口中定义的方法必须通过某个类实现该接口,然后创建该类的实例,再通过实例调用方法,所以SqlSession对象的getMapper()方法返回的一定是一个动态代理对象。
2.然后通过该引用调用Mapper接口中定义的方法。
SqlSession对象getMapper()方法的实现:configuration.getMapper(type, this)
再查看Configuration对象的getMapper()方法,其实现:
mapperRegistry.getMapper(type, sqlSession);
要看懂上面的代码,先了解下Configuration类的属性类MapperRegistry mapperRegistry:
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动态代理对象了。
注意:
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注解。
先了解下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中Mapper SQL配置信息(MappedStatement对象)的注册过程,即MappedStatement对象的创建及注册过程:
Configuration对象创建过程中,通过XMLConfigBuilder对象来解析MyBatis主配置文件,其**parseConfiguration()**方法中会调用mapperElement(root.evalNode(“mappers”))解析<mappers>
标签。
详细看下mapperElement()方法的实现:
1.首先通过root.evalNode(“mappers”)获取<mappers>
所有子标签(<mapper>
标签或<package>
标签)。
2.因为<mappers>
标签配置Mapper信息有以下几种方式,所以mapperElement()方法中对这几种情形的配置分别做了处理,接下来以<mapper resource="……"/>
这种形式为例介绍Mapper SQL配置文件的解析过程。
3.根据resonrce中的地址找到Mapper接口,以Mapper接口的输入流作为参数,创建一个XMLMapperBuilder对象 。
4.调用XMLMapperBuilder对象的parse()方法完成解析
详细看下parse()方法的实现:
1.首先调用XPathParser对象的evalNode()方法获取根节点对应的XNode对象
2.接着调用configurationElement()方法对Mapper配置内容做进一步解析。
在configurationElement()方法中,对Mapper SQL配置文件的所有标签进行解析,这里重点关注<select|insert|update|delete>标签的解析过程:
1.获取<select|insert|update|delete>标签节点对应的XNode对象后,
2.调用XMLMapperBuilder类的buildStatementFromContext()方法做进一步解析处理。
buildStatementFromContext()方法的实现:
1.遍历所有XNode对象,为每个<select|insert|update|delete>标签对应的XNode对象创建一个XMLStatementBuilder对象,
2.接着调用XMLStatementBuilder对象的parseStatementNode()方法进行解析处理。
XMLStatementBuilder类的parseStatementNode()方法的实现:
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等。