Mybatis源码解析之核心类分析
Mybatis源码解析之初始化分析
本篇文章将在前两篇文章的基础上解析Mybatis执行sql的流程。
一、 映射集mappers解析
对于在原生mybatis中的sql执行,需要在配置文件中通过标签引入mapper.xml文件。前文已经提到,对于xml配置文件,mybatis在XMLConfigBuilder#parseConfiguration(Xnode)方法中进行解析,对于mappers节点的解析在mapperElement(root.evalNode("mappers"));
进行处理。
1. XMLConfigBuilder#mapperElement(XNode)
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
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) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
可以看到,mybatis可以通过package属性批量引入,也可以通过url或resource或mapperClass来单个引入。虽然引入的方式不同,但实现方式大同小异,都是将文件处理成输入流后通过XMLMapperBuilder的parse方法进行解析。
2. XMLMapperBuilder#parse()
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
//从根节点mapper开始解析节点
configurationElement(parser.evalNode("/mapper"));
//标记该文件以及解析完成
configuration.addLoadedResource(resource);
//namespace处理
bindMapperForNamespace();
}
//处理抛出IncompleteElementException对应的节点
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
3. XMLMapperBuilder#bindMapperForNamespace()
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);
}
}
}
}
绑定xml对应的mapper接口类,并加入到mapperRegistry属性中。
3. XMLMappedBuilder#configurationElement(XNode)
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper