使用mybatis的流程中整体分2大块:解析过程和使用过程
解析过程
- 首先需要一个Mybatis的配置文件:mybatis-config.xml
- 通过
SqlSessionFactoryBuilder来解析config.xml来创建SqlSessionFactoryXMLConfigBuilder.parseConfiguration()方法先去解析mybatis-config.xml文件,将所有的配置封装到Configuration类中。parseConfiguration方法会按照一定顺序来解析mybatis-config.xml中所有的配置项(具体配置可以看官网)主要几个常用的:plugins,typeHandlers,mappers。- 解析plugins:会按照顺序将所有的插件添加到
configuration的interceptorChain中,interceptorChain是个list。 - 也是解析typeHandlers然后注册到
configuration的typeHandlerRegistry中。 - 最终要的就是解析这个mappers:
- 针对配置的每一个Mapper.xml,使用
XMLMapperBuilder来构建,会将Mapper.xml里面所有的信息添加到configuration中 - 针对增删改查的没一条SQL语句:会使用
XMLStatementBuilder来创建MappedStatement,封装了这条SQL从XML中获得的所有相关的信息。 - 每条SQL会被解析成
MappedStatement然后添加到configuration的mappedStatements这个map中。(这个我们debug的时候应该注意到过),同时Sql也会被XMLScriptBuilder进一步解析到sqlSource中,#{}这些都会变成?占位符。 - 在Mapper.xml解析完成,并且每个SQL的
MappedStatement都添加到configuration之后,会调用bindMapperForNamespace()方法来找Mapper.xml对应接扣进行绑定。 - 通过反射读取XML中
namespace指定的Mapper全限定类名,加载这个接口然后添加到configuration的MapperRegistry中 - 重点看这个
MapperRegistry,他其实内部就是一个Map:knownMappers类型为Map<Class<?>, MapperProxyFactory<?>>。所有的Mapper接口都会被MapperProxyFactory包装并放入到knownMappers中(key是接口的类)。所以我们使用接口,其实使用的都是被这个Proxy代理的类。
- 针对配置的每一个Mapper.xml,使用
- 解析plugins:会按照顺序将所有的插件添加到
- mappers下所有的Mapper都被解析完成,解析差不多就完成了。所有解析出来的配置都放到了
configuration中。而里面最重要的2个就是:MapperRegistry和mappedStatements一个对应着接口类,一个对应的xml配置的sql信息。
- 此时我们就有了
SqlSessionFactory对象(默认就是DefaultSqlSessionFactory),也就是说一个SqlSessionFactory对象其实对应着一个mybatis-config.xml
使用过程
- 然后我们可以通过
DefaultSqlSessionFactory对象的openSession()方法来开启一个SqlSession(默认的是DefaultSqlSession)。 - 创建SqlSession的时候内部会创建一个Executor对象,所有的sql操作其实都是这个Executor来执行的。到这里,我们创建的SqlSession里面有2个主要的元素:
executor和configuration。一个负责之前的各种配置,一个负责sql的执行。 - 然后我们需要获取需要使用的Mapper类,是通过SqlSession的getMapper方法来获取的。getMapper通过JDK的动态代理创建代理对象:
MapperProxy。(configuration之前加载的mapperRegistry的key是类名,value是MapperProxy的工厂类,通过这个可以直接创建对应的代理对象) - 此时我们调用代理类的方法的时候,就会进入
MapperProxy的代理逻辑。这部分的逻辑也分了好几步- 首先一个Mapper接口会对应一个MapperProxy的代理对象,这个对象里面有个map
methodCache来缓存所有的方法信息。 - 当调用Mapper接口方法会执行代理invoke方法,代理逻辑会对执行的Method方法包装为
MapperMethodInvoker对象(默认类型是PlainMethodInvoker)放入methodCache缓存中。MapperMethodInvoker其实内部包含了MapperMethod对象,这个才是真正的方法信息载体。 - 也就是代理类触发接口方法的时候,会创建该方法的
MapperMethod然后放到这个代理类的缓存中。 - MapperMethod类很重要!他会将方法解析为2个属性:
SqlCommand和MethodSignature。并且真正去执行sql的类就是该类的execute方法。 SqlCommand和MethodSignature的信息主要来自于接口传入的时候反射获取信息以及configuration中配置信息。
- 首先一个Mapper接口会对应一个MapperProxy的代理对象,这个对象里面有个map
- 所以调用mapper接口最终调用的是
MapperMethod的execute()方法 - execute方法会依据sql的类型去执行对应的SqlSession的方法如
selectOne,selectList等(要到底了) - SqlSession里面封装的一些列方法的最终其实都是通过调用
executor来执行sql的。而executor就比较少了,只有query和update等这些很能标记sql属性的方法。 - 下面主要看看executor的调用
- 首先就会获取
BoundSql对象,之前的处理中其实已经将sql信息,参数信息都解析好了(虽然我没写^_^),我们会将这些信息全部封装到BoundSql对象中。(所以,如果你想获取mybatis执行的sql时,可以尝试获取这个对象) - 然后对方法及参数信息创建CacheKey,用来下面的一级缓存用的key
- 快到JDBC了,走的queryFromDatabase()方法,该方法会去判断缓存啊什么的,然后如果都没有就会去执行JDBC代码了
- 万事具备了就可以用JDBC的方式去查数据库了,mybatis的底层入口在doQuery方法,就是创建
PreparedStatement然后调用execute去查询数据库,这些都是JDBC的代码了。 - 后面还有对接口使用
resultSetHandler去解析等等步骤,将JDBC返回的ResultSet结果映射为成我们想要的Object。
- 首先就会获取
- 分析完毕。