背景
mapper xml 解析很复杂,因为其中的标签非常多,比如我们平时开发用到的select,delete,resultMap等都在此时解析。
xml解析模块
mybatis xml解析类都继承自 BaseBuilder。
classDiagram
BaseBuilder <|-- MapperBuilderAssistant
BaseBuilder <|-- SqlSourceBuilder
BaseBuilder <|-- XMLConfigBuilder
BaseBuilder <|-- XMLMapperBuilder
BaseBuilder <|-- XMLScriptBuilder
BaseBuilder <|-- XMLStatementBuilder
class MapperBuilderAssistant
class SqlSourceBuilder
class XMLConfigBuilder
class XMLMapperBuilder
class XMLScriptBuilder
class XMLStatementBuilder
上图中XmlConfigBuiler 负责解析mybatis 的配置文件,XmlMapperBuilder 负责解析mapper映射文件,XmlStatementBuilder 负责解析mapper文件中的部分标签比如select|insert|update|delete,SqlSourceBuilder 负责解析真正的sql语句,XMLScriptBuilder 负责解析sql 里面带有动态逻辑标签的语句比如trim,where,set,foreach,if,choose,when,otherwise,bind等,MapperBuilderAssistant 协助类,比如addMappedStatement()方法帮忙构建MappedStatement 对象。
MappedStatement 解析
解析的逻辑大同小异,这里我们选一个典型并且核心的标签类MappedStatement来跟一下解析逻辑,看看能不能从中学到新知识,入口在 org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
private void parseConfiguration(XNode root) {
//...
mapperElement(root.evalNode("mappers"));
}
#org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement()
private void mapperElement(XNode parent) throws Exception {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();//进入
}
}
}
}
//省略调用链路直接到 org.apache...XMLMapperBuilder#buildStatementFromContext
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
//解析所有的insert/update/delete/select 标签
for (XNode context : list) {
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
statementParser.parseStatementNode();
}
}
//org....xml.XMLStatementBuilder#parseStatementNode
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
//...
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
parseStatementNode() 方法将标签中的属性都取出来,然后通过builderAssistant 这个工具类,构建成MappedStatement对象,并放入configuration 的 map中key 为namespace+id,所以同一个mapper xml 中如果id一样会抛错,如图,这样一来 MappedStatement 就含有这个select中所有的配置,等到要执行具体sql的时候只需要从configuration 取出对应的 MappedStatement 对象即可,这篇文章就介绍到这。