目标
本文的主要目标是完成SqlSessionFactoryBuilder生成SqlSessionFactory对象的源码阅读,了解Mybatis对mapper.xml文件的查询过程。
1. SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
通过工厂模式可以生成SqlSessionFactory 对象,这个对象中封装了mappedStatements对象,这个对象中封装了mapper.xml文件解析的sql语句,因此需要分析这个对象的查询过程。
2. 创建SqlSessionFactory流程图
创建SqlSessionFactory的流程图是:
3. 创建SqlSessionFactory的具体流程
3.1 解析mybatis-config.xml文件创建Configuration对象
先利用Resources的方法生成mybatis-config.xml文件的输入流,然后针对每个标签进行解析。
3.1.1 Debug进入parseConfiguration方法:
对每个标签进行解析,其中settingsElement方法是设置默认执行器Executor是SimpleExecutor,environmentsElement方法是解析并设置dataSource数据源。
3.1.2 Debug进入mapperElement方法:
先解析有没有package标签,如果有会扫描这个包下所有的mapper接口并解析相应的mapper.xml文件,如果没有package标签,有resource标签的话会解析mapper.xml文件的位置是在类路径下的mybatis目录下的BlogMapper.xml,并生成输入流,接下来对BlogMapper.xml文件进行解析。
3.2 解析BlogMapper.xml文件
3.2.1 Debug进入configurationElement方法:
方法的入参已经解析得到mapper标签的内容,在这个方法中会解析mapper.xml的namespace和select等标签。
3.2.2 Debug进入parseStatementNode方法:
执行创建sql的createSqlSource方法。
3.3 接下来重点分析创建sql的过程
3.3.1 Debug进入parseScriptNode方法:
判断该sql语句是动态sql还是静态sql,这里是静态sql。
3.3.2 Debug进入parse方法:
将sql语句的#{author}中的参数名字author单独提取出来,接下来调用handler的handleToken方法将参数名字author添加到parameterMapping这个list集合中。
3.3.3 Debug进入handleToken方法:
将创建的ParameterMapping对象放入到parameterMappings这个list集合中,同时返回?替换sql的#{author}。其中,会先调用buildParameterMapping方法。
3.3.4 Debug进入buildParameterMapping方法:
将mapper.xml文件解析出来的接口方法的参数名字放到ParameterMapping中,同样是用工厂模式创建ParameterMapping对象。
3.3.5 Debug进入parseStatementNode方法:
在创建好sql之后会封装到RawSqlSource,这个对象是StaticSqlSource,这表示它是一个静态sql,这个对象中包含了占位符替换成?的sql语句和parameterMappings这个list集合,它的作用是匹配mapper.xml文件#{author}的参数名字和mapper接口方法的参数名字author然后得到参数值。
XMLStatementBuilder文件的parseStatementNode方法接着创建parameterMap对象为null,resultType对象为Blog对象,resultMap对象为null。
至此,创建sql的分析过程完成。
3.4 创建MappedStatement对象
创建好sql之后在XMLStatementBuilder文件的parseStatementNode方法继续执行。
3.4.1 创建MappedStatement对象
此时id是mapper.xml文件的id,即接口方法的名字。
3.4.2 Debug进入applyCurrentNamespace方法:
MapperBuilderAssistant文件的applyCurrentNamespace方法会更新id是mapper.xml文件的namespace.id。
3.4.3 Debug进入addMappedStatement方法:
更新id是mapper.xml文件的namespace.id之后,利用工厂模式创建MappedStatement对象。
3.4.4 Debug进入MappedStatement的Builder方法:
创建好sql并将sql封装到SqlSource之后,调用MappedStatement的Builder方法创建MappedStatement对象,其中id值是mapper.xml文件中的namespace.id。
3.4.5 MappedStatement对象放到Configuration中
mappedStatements是一个map,它是一个StrictMap,存放key是id值,value是MappedStatement对象。
mappedStatements是Configuration的属性,最终MappedStatement对象放到了Configuration对象中。
3.4.6 StrictMap的put源码
StrictMap如果put数据的时候发现key已经存在就会报错,因此id值必须唯一,并且id值是mapper.xml文件的namespace.id,所以mapper接口方法不能重载。
至此,MappedStatement对象创建完成。
3.5 MapperRegistry对象
MapperRegistry对象mapperRegistry是存放在Configuration对象中的。
3.5.1 Debug进入XMLMapperBuilder的bindMapperForNamespace方法:
接口的Class类对象存放到map中。
3.5.2 Debug进入MapperRegistry的addMapper方法:
knownMappers是一个HashMap,key存放接口的Class类对象,value存放MapperProxyFactory工厂类对象,这个工厂类对象的作用是可以生成基于接口的JDK动态代理对象。
至此,MapperRegistry对象创建完成。
3.6 创建SqlSessionFactory
4. 总结
4.1 关键组件
(1)SqlSessionFactory:SqlSession工厂,它包含了Configuration对象
(2)Configuration:所有数据都存放在Configuration对象中,它包含
-
mappedStatements:它是一个StrictMap,这个map判断如果有相同的key会报错,因此mapper的方法不能重载,mappedStatements的作用是完成mapper.xml文件方法和mapper接口方法的映射
- key:mapper.xml文件的namespace.id
- value:MappedStatement对象,它包括
- id:mapper.xml文件的namespace.id
- statementType:PREPARED
- SqlSource对象,它包括:
- sql:select * from blog where author = ?
- parameterMappings:parameterMapping是一个List集合,它存放了#{author}中的参数名字author,parameterMapping的作用是匹配mapper.xml文件#{}的参数名字和mapper接口方法的参数名字然后得到参数值
-
mapperRegistry:
- knownMappers:它是一个ConcurrentHashMap,knownMappers的作用是执行getMapper方法时会根据接口的Class类对象生成JDK动态代理对象
- key是接口的Class类对象
- value是MapperProxyRegistry对象
- knownMappers:它是一个ConcurrentHashMap,knownMappers的作用是执行getMapper方法时会根据接口的Class类对象生成JDK动态代理对象
(3)MappedStatement:包括SqlSource等,用来封装sql语句