mybatis源码流程图
2022-05-14 15:26:00 22 举报
mybatis源码解析
作者其他创作
大纲/内容
节点id拼接namespace拿到一个新的idorg.example.mapper.UserMapper.selectById
将子节点信息封装到ResultMapping中然后添加到List<ResultMapping>集合中
判断textSqlNode.isDynamic()
拿到select|insert...这些节点的属性值
namespace:org.example.mapper.UserMapper
是
<include>节点进来(Node source就是<sql>)且当前节点Node source的节点类型是TEXT或CDATA_SECTION
parseDynamicTags(XNode)解析动态节点拿到MixedSqlNode
解析lang属性,如果自定义了sql脚本语言驱动,那么就拿自己定义的否则就拿默认的XMLLanguageDriver
reflectorFactoryElement
创建对应的SqlNode(MixedSqlNode)
虽然不同的动态节点有不同的处理方式但其实套路是一样的。1. 都要先执行parseDynamicTags(XNode)递归解析得到MixedSqlNode。2. 创建对应的SqlNode。3. 添加到最开始的<select>节点的List<SqlNode>中。
根据节点名称获取对应的NodeHandler
子节点类型是CDATA_SECTION_NODE或者TEXT_NODE
new SqlSessionFactoryBuilder().build(reader)
resultMap解析子流程(构建ResultMap对象添加到Configuration中)
这个也是在settings节点设置的属性value值:用别名设置了可以打印sql日志
cacheElement解析<cache>(二级缓存)
build创建一个DefaultSqlSessionFactory(Configuration)
否
parsed:mybatis.xml是否解析过的标记。parser:XPatheParser对象
XmlMapperBuilder和Configuration共同指向同一个sqlFragments
handleNode()子流程
Node source当前节点类型是ELEMENT_NODE
创建XPathPaser对象
以这个新的id为key,<sql>节点(已经解析成XNode了)为value添加到sqlFragments这个Map中
<select>select <include />from </select>
新建一个StaticTextSqlNode(sql文本)对象添加到List<SqlNode>
递归,子节点传给source
typeHandlerElement
MapperRegistry.addMapper()子流程
拼接keyStatementId
namespace拼接上前缀添加到configuration的loadedResources中
mybatis.xml会在创建该对象的时候解析为Document
parseDynamicTags(XNode)子流程
<mapper>节点使用url属性(不推荐)
handler.handleNode()不同的子节点有不同的处理方式
创建XMLStatementBuilder extends BaseBuilder对象
拿到mapper.xml的namespace所对应的Class类型
SqlSource
mapper.xml中<mapper>是根节点
替换Includes标签为对应的sql标签里面的值
typealiases
List<SqlNode> contents = new ArrayList<>();一开始会创建一个List用来存放SqlNode
loadXmlResource
new MixedSqlNode(List<SqlNode>)
configurationElement
创建静态SqlSourceRawSqlSource
addMappedStatement创建MappedStatement并添加到Configuration
settings
resource资源是否加载过
构建一个cache对象(这个Cache对象包装了很多层Cache)存入Configuration的caches属性以及MapperBuilderAssistant的currentCache对象。LruCache的LinkedHashMap数据结构要注意一下
<mapper>节点使用class属性
properties第一个解析
logImpl
解析select|insert|update|delete
有时间再去补充吧核心应该就是找接口中有注解的方法,将注解的信息构建成MappedStatement对象
sql脚本语言驱动是用来创建SqlSource的
拿<mapper>节点的namespace属性设置到XmlMapperBuilder封装的MapperBuilderAssistant属性
获取子节点的sql文本然后
cacheRefElement解析<cache-ref>
vfs(基本不用)
遍历这些select|insert|update|delete节点
子流程select|insert...节点的属性都会传进来
子流程(构建MappedStatement对象并添加到Configuration中)
pluginElement
递归
getStatementParameterMap
设置XMLScriptBuilder的isDynamic属性为true
objectWrapperFactoryElement
sqlElement解析<sql>节点形成sql片段(sqlFragments)
子流程
静态
parser.parse()XmlConfigBuilder的parsed属性会在该方法一开始就设置为true
configuration.addMapper()
创建一个MappedStatement的Builder对象
resultMaps的key:当前mapper的namespace属性拼接<resultMap>节点的id属性(org.example.mapper.UserMapper.resultMapId)
transactionManagerElementdataSourceElement最后构建一个Environment对象放入Configuration的environment属性中
这个MixedSqlNode包了很多层。MixedSqlNode - select StaticTextSqlNode - 文本 WhereSqlNode - where MixedSqlNode - where StaticTextSqlNode - 空文本 IfSqlNode - if MixedSqlNode - if StaticSqlNode - 文本 StaticSqlNode - 空文本 StaticTextSqlNode - 空文本
解析注解子流程
objectFactoryElement
bindMapperForNamespace
给XmlConfigBuilder属性赋值
创建一个XMLScriptBuilder extends BaseBuilder对象
是接口
将包中的类解析到一个Setr<Class>集合中遍历
parameterMapElement解析<parameterMap>该节点在3.5开始貌似不怎么推荐使用
创建TextSqlNode对象
mapper的namespace拼接该<sql>节点的id属性生成一个新的id
mapper.xml之间的缓存引用,被引用的那个xml如果没设置cache则会报错
mapper.xml没解析过的话就回去解析,前提是接口名和xml名相同且在同一个包下。UserMapper和UserMapper.xml在同一个包下。
mybatis.xml的<mapper>节点使用resource属性
parseStatementNode解析节点
递归,传给source的是节点
databaseIdProviderElement
package优先级高resource、url、class三个属性只能有一个
所以注册class和package的方式其实走的是注解的路子。但是走注解的路子也是会去先加载mappder.xml的,前提是接口名和xml名相同且在同一个包下,否则mapper.xml就没法解析到。UserMapper和UserMapper.xml在同一个包下。
最后构建ResultMap对象并将它添加到Configuration的resultMaps属性中
处理我们的parameterMap(该方式基本不用了)和parameterType
<include>解析子流程
ResultMapResolver.addResultMap()
子节点类型是ELEMENT_NODE
trimwheresetforeachifchoosewhenotherwisebind
将settings节点的key-value设置进Configuration
整个<select>是否是动态sql
TextSqlNode对象添加到List<SqlNode>
替换sql片段中的 ${<properties解析到的内容>}
<include>父节点将<include>替换为<sql>节点的firstChildNode(第一个节点肯定是#Text)
XMLScriptBuilder.parseScriptNode()真正去解析SqlNode了
<select id=\"selectById\" resultMap=\"result\
这几个基本不用对象工厂:用于反射实例化对象对象包装工厂反射工厂:用于属性和setter/getter的获取
Configuration设置databaseId(environment和databaseIdProvider都存在的情况下)
Node source当前节点是<include>
加载资源构建XMLMapperBuilder对象mapperParser
拿到一个主键生成器
namespace所对应的类型是否是接口
获取当前XNode的所有子节点遍历
创建XMLIncludeTransformer对象解析<include>
动态
拿到<select>|<insert>...节点的name来判断SqlCommandType值
将这个resource资源放到Configuration的loadedResources属性中,下次再加载这个资源的时候就不需要再去解析了
SqlNode添加到<select>节点的List<SqlNode>
构建MappedStatement对象-> 添加到Configuration的mappedStatements属性中
<mapper>节点使用package属性
遍历source的子节点
解析mapper.xml子流程
调XmlConfigBuilder的抽象父类BaseBuilder的构造函数进行属性赋值
environmentsElement
1. packages包下的类注册别名:别名为简单类型名。2. typealiastype:类型全名alias:别名,没设置的话会默认是简单类型名。3. if(packages){...} else{...}两种方式都有,packages优先级高
解析parameterType拿到Class
遍历所有resultMap节点
子节点的各个属性都会封装进去,像property、column、javaType、jdbcType、typeHandler ...,如果是<id>子节点,还会设置一个ID标记进来说明是一个主键。
settingsElement
创建动态SqlSourceDynamicSqlSource
如果在mybatis.xml中配置了databaseprovider这个节点且<property>设置了数据库厂商,那么在增删改查这几个节点需要设置databaseId的属性一致。如果没配置数据库厂商那么增删改查这几个节点也不要去设置databaseId属性
resultMapElements解析<resultMap>
XmlLanguageDriver.createSqlSource()创建SqlSource
解析属性type拿到类全名
sql文本中有没有${},有的话就是动态,没有就是静态
遍历resultMap的子节点(id、result ...)
configurationtypeAliasRegistrytypeHandlerRegistryBaseBuilder这个抽象类属性是protected
如果设置了extends属性,那么会从Configuration中拿它对应的ResultMap对象 -> 拿到父ResultMap中的List<ResultMappings> -> 合并到当前子resultMap的List<ResultMapping>中 (extendResutMappings.removeAll(resultMappings) -> resultMappings.addAll(extendResultMappings) )
mybatis是这样实现的:1. 父节点的<include>替换为<sql>2. <sql>节点前插入<sql>的第一个子节点(#text)3. 删除<sql>节点4. 最终<include>实际就是转成了<sql>的文本信息(#text)
获取子节点name
主要是用来解析注解的构建一个MappedStatement对象添加到Configuration中
构建ResultMapResolver对象
会将<resultMap>节点的属性(id、type、extendsautoMapping)都封装进来
parseScriptNode子流程
这个也是在settings节点设置的属性访问一些虚拟文件系统的把(ftp之类的)
解析StatementNode
TextSqlNode textSqlNode = new TextSqlNode(data);将sql文本传进该对象
mapperElement
删除因异常没有完成解析的数据
XMLConfigBuilderXMLMapperBuilderMapperBuilderAssistantXMLStatementBuilderXMLScriptBuilderSqlSourceBuilder
1. <property>节点的key-value添加到Properties defaults。2. <properties>的属性resource或url进行资源加载成Properties,最后添加到defaults中。3. 加上Configuration的variables属性值。4. defaults设置到Configuration和XPathPaser的variables属性中。
XMLMapperBuilder extentds BaseBuilder是用来解析mapper.xml文件的,构建该对象的时候会new XPathParser将mapper.xml解析到document。XmlMapperBuilder构建的时候会把Configuration的sqlFragments属性传进来,也就是XmlMapperBuilder和Configuration共同引用同一个sqlFragments。
解析我们<insert 语句的的selectKey节点
创建MapperAnnotationBuilder对象
拿到所有的<sql>节点遍历
org.example.mapper.UserMapper.selectById!selectKey
创建XMLConfigBuilder extends BaseBuilder parser用来解析全局配置文件
创建SqlSource子流程
去Configuration的sqlFragement属性中根据refid找到<sql>节点数据
就是mapper.xml有没有解析过
buildStatementFromContext
解析xml的信息都放在了Configuration对象中了
mapperParser.parse()解析mapper.xml
setting节点的name属性看XmlConfigBuilder的settingElements方法(这里有大部分的属性),只要是Configuration中的set方法其实都行
0 条评论
回复 删除
下一页