上一篇我们讲了Configuration的addMappers方法,可以看出,这个方法的最终目的,是将我们定义的mapper接口最终转化为MappedStatement,然后加入Configuration中,并且我们从上一篇文章中可以看出,即使我们是使用了class去配置mapper,也会去默认路径下加载xml文件.
本篇我们来看看当mapper标签加载的是一个xml文件时使用的XMLMapperBuilder.parse:
public void parse() {
// 看是否已经加载过该配置文件
if (!configuration.isResourceLoaded(resource)) {
// 解析mapper标签
configurationElement(parser.evalNode("/mapper"));
// 标识该xml文件已经被加载过
configuration.addLoadedResource(resource);
// 去加载对应的namespace对应的class
bindMapperForNamespace();
}
// 这三个方法是重新加载一遍之前加载出错的标签
parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
}
我们先来看bindMapperForNamespace方法:
// 在这个方法里,我们看到了熟悉的configuration.addMapper()方法,这说明在加载xml文件时,也会去解析一下namespace对应的class(如果该class存在的话)
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
}
接下来,我们再来看解析标签的configurationElement方法:
// 从该方法中,我们可以看出,会逐步解析mapper标签下的子标签,具体解析过程,这里就不慢慢分析了,因为涉及到的细节过多,但是最终肯定是会生成一个MappedStatement的
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
至此,我们Configuration就分析完了,也成功的构建出了DefaultSqlSessionFactory,因为最终DefaultSqlSessionFactory中就只有一个Configuration:
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
由于最后的mapper解析过程实在是复杂,所以本系列文章就简化了,之后如果有机会,博主会单开章节来写.