首先附上一張簡單的MyBatis的配置文件xml圖,便于后續(xù)查看
在上篇中定位到源代碼XMLConfigBuilder類中的parse()方法
public Configuration parse() {
if(this.parsed) {
throw new BuilderException("Each MapperConfigParser can only be used once.");
} else {
this.parsed = true;
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
上篇了解到(this.parser.evalNode("/configuration")返回了MyBatis配置文件中的configuration節(jié)點下的所有內(nèi)容,那么繼續(xù)看下parseConfiguration()方法
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.settingsElement(root.evalNode("settings"));
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
在這個方法中接受傳來的XNode的congfiguration節(jié)點內(nèi)容刁品,可以看到有關(guān)解析配置文件里的一些節(jié)點方法,有plugins分冈,settings派哲,environments的,還有mappers就是包含我們所有mapper.xml標(biāo)簽(單獨的Mybatis志群,未整合spring),那進入到mapperElement方法中着绷,依舊是XMLCongfigBuilder類中的方法:
private void mapperElement(XNode parent) throws Exception {
if(parent != null) {
Iterator i$ = parent.getChildren().iterator();
while(true) {
while(i$.hasNext()) {
XNode child = (XNode)i$.next();
String resource;
if("package".equals(child.getName())) {
resource = child.getStringAttribute("name");
this.configuration.addMappers(resource);
} else {
resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
XMLMapperBuilder mapperParser;
InputStream inputStream;
if(resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
inputStream = Resources.getResourceAsStream(resource);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();
} else if(resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
inputStream = Resources.getUrlAsStream(url);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
mapperParser.parse();
} else {
if(resource != null || url != null || mapperClass == null) {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
那這個方法就越來越清楚了,首先判斷XNode不為空锌云,就是mappers,然后獲得XNode的getChildren().iterator();mappers下的子節(jié)點mapper.xml荠医,getChildren()方法返回的是List<XNode>所以可以調(diào)用iterator()來遍歷。
得到了 XNode child = (XNode)i$.next(); 子節(jié)點之后,根據(jù)child.getName()彬向,返回當(dāng)前節(jié)點名并且判斷是否等于package兼贡,顯然不等于(節(jié)點名是mapper),隨后開始在執(zhí)行else下的代碼
resource = child.getStringAttribute("resource");
通過getStringAttribute("resource")方法拿到遍歷的每個mapper節(jié)點的屬性名為"resource"的值,那就是這篇文章的上圖中
<mapper resource="/UserMapper.xml" />這里的xml路徑娃胆,下面的兩行代碼
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
通過配置文件看到我們暫時沒有用到遍希,所以走下面if判斷的第一個分支
if(resource != null && url == null && mapperClass == null)
第一行代碼先略過-_- ErrorContext.instance().resource(resource);
下面是很熟悉的代碼
inputStream = Resources.getResourceAsStream(resource);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();
這里呢依舊是之前那樣的步驟,和解析MyBatis配置文件一樣解析mapper.xml文件里烦,不多說凿蒜,很穩(wěn)!
那還是進到parse()方法看看
public void parse() {
if(!this.configuration.isResourceLoaded(this.resource)) {
this.configurationElement(this.parser.evalNode("/mapper"));
this.configuration.addLoadedResource(this.resource);
this.bindMapperForNamespace();
}
this.parsePendingResultMaps();
this.parsePendingChacheRefs();
this.parsePendingStatements();
}
首先還是判斷文件是否被解析胁黑,如果解析了的話废封,先放一下這個點,先看未解析的步驟丧蘸。
首先依舊是this.parser.evalNode("/mapper")漂洋,返回mapper節(jié)點下的所有內(nèi)容,是XNode類型力喷,在通過configurationElement()方法解析
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
this.builderAssistant.setCurrentNamespace(namespace);
this.cacheRefElement(context.evalNode("cache-ref"));
this.cacheElement(context.evalNode("cache"));
this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
this.resultMapElements(context.evalNodes("/mapper/resultMap"));
this.sqlElement(context.evalNodes("/mapper/sql"));
this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception var3) {
throw new RuntimeException("Error parsing Mapper XML. Cause: " + var3, var3);
}
}
看到方法里有獲得屬性getStringAttribute("")方法氮发,有常見的namespace,parameterMap等等比較重要的冗懦,
可以看下setCurrentNamespace()方法里代碼寫了爽冕,規(guī)定了namespace不能為空
其他的解析方法都能看懂,這里直接看下面的context.evalNodes("select|insert|update|delete")這行代碼披蕉,
這里就是返回所有select或ins,或up,或的節(jié)點內(nèi)容颈畸,返回值依舊是List<XNode>,現(xiàn)在進入到XMLMapperBuilder類的
buildStatementFromContext方法
private void buildStatementFromContext(List<XNode> list) {
if(this.configuration.getDatabaseId() != null) {
this.buildStatementFromContext(list, this.configuration.getDatabaseId());
}
this.buildStatementFromContext(list, (String)null);
}
this.configuration.getDatabaseId()為空没讲,繼續(xù)執(zhí)行他的重載方法
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
Iterator i$ = list.iterator();
while(i$.hasNext()) {
XNode context = (XNode)i$.next();
XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException var7) {
this.configuration.addIncompleteStatement(statementParser);
}
}
}
然后看到依然是眯娱,初始化,在調(diào)用初始化類的方法爬凑,發(fā)現(xiàn)整個程序的邏輯徙缴,以及代碼的編寫,基本都是這個樣子的嘁信,在看下通過初始化的類調(diào)用的他的parseStatementNode()的這個方法于样。
到現(xiàn)在,第二篇潘靖,發(fā)現(xiàn)了從一開始的返回整個Configuration后穿剖,再次獲得Mappers的內(nèi)容,然后遍歷Mappers中的mapper,
再次解析每一個mapper.xml卦溢。 在每個mapper標(biāo)簽中判斷是否有namespace屬性糊余,繼續(xù)解析xml方式得到select秀又,insert,update,delete的標(biāo)簽節(jié)點,得到List<XNode>,最后遍歷List<XNode>中的每一個的元素以及內(nèi)容贬芥,這里就到了如何具體的解析具體的sql 標(biāo)簽了吐辙。下一篇繼續(xù)在看吧。