mybatis源码解析(一)-开篇
mybatis源码解析(二)-加载过程
mybatis源码解析(三)-SqlSession.selectOne类似方法调用过程
mybatis源码解析(四)-Mapper方法调用过程
mybatis源码解析(五)-mybatis如何实现的事务控制
mybatis源码解析(六)-配合spring-tx实现事务的原理
mybatis源码解析(七)-当mybatis一级缓存遇上spring
转载请标明出处:
http://blog.csdn.net/bingospunky/article/details/79197665
本文出自马彬彬的博客
mybatis加载过程概览
mybatis的加载过程,最重要的就是基于xml配置文件的加载,基于xml配置文件的加载过程主要由org.apache.ibatis.builder.xml.XMLConfigBuilder类来完成,主要代码如下:
Code 1
org.apache.ibatis.builder.xml.XMLConfigBuilder
private void parseConfiguration(XNode root) {
try {
Properties settings = this.settingsAsPropertiess(root.evalNode("settings"));
this.propertiesElement(root.evalNode("properties"));
this.loadCustomVfs(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectionFactoryElement(root.evalNode("reflectionFactory"));
this.settingsElement(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);
}
}
上述代码针对mybatis xml主配置文件的内容一一做了解析处理。我们这里主要关注一下mybatis xxxMapper.xml相关的加载过程。
mybatis xxxMapper.xml加载过程
每一个xxxMapper.xml,都对应一个XMLMapperBuilder,由XMLMapperBuilder的parse方法解析,代码如下:
Code 2
org.apache.ibatis.builder.xml.XMLMapperBuilder
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();
}
解析xxxMapper.xml文件
Code 2第3行解析标签里的内容,处理过程如下:
Code 3
org.apache.ibatis.builder.xml.XMLMapperBuilder
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace != null && !namespace.equals("")) {
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"));
} else {
throw new BuilderException("Mapper's namespace cannot be empty");
}
} catch (Exception var3) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + var3, var3);
}
}
context对应的内容就是这个xxxMapper.xml的完整内容。第9行,把这个里的resultMap标签的内容都添加到protected final Map
解析xxxMapper.java文件
Code 2第5行的方法,代码如下:
Code 4
org.apache.ibatis.builder.xml.XMLMapperBuilder
private void bindMapperForNamespace() {
String namespace = this.builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException var4) {
;
}
if (boundType != null && !this.configuration.hasMapper(boundType)) {
this.configuration.addLoadedResource("namespace:" + namespace);
this.configuration.addMapper(boundType);
}
}
}
该方法获取xxxMapper.xml里的namespace的值,根据这个字符串获取Class类,如果该类存在,那么就解析这个类。解析这个类时,就是获取它的所有方法,如果某个方法包含特定的注解时,就把这个方法转化为对应的MappedStatement。
特定的注解如下:
Code 5
this.sqlAnnotationTypes.add(Select.class);
this.sqlAnnotationTypes.add(Insert.class);
this.sqlAnnotationTypes.add(Update.class);
this.sqlAnnotationTypes.add(Delete.class);
this.sqlProviderAnnotationTypes.add(SelectProvider.class);
this.sqlProviderAnnotationTypes.add(InsertProvider.class);
this.sqlProviderAnnotationTypes.add(UpdateProvider.class);
this.sqlProviderAnnotationTypes.add(DeleteProvider.class);
把某个方法转化为MappedStatement的过程是很复杂的,概括地说就是:对于符合条件的方法,通过方法上的注解生成SqlSource,这个SqlSource主要就包含sql语句和参数,再生成一些其他的信息,比如org.apache.ibatis.mapping.SqlCommandType。通过这些其他信息和SqlSource生成org.apache.ibatis.mapping.MappedStatement.Builder,然后通过Builder创建MappedStatement。
最后把MappedStatement添加到org.apache.ibatis.session.Configuration中,key为 package+ p a c k a g e + {className}+${methodName}
当mapper.xml里和mapper.java里都配置一个sql时,出现什么情况?
会抛出如下第3行异常。
Code 6
public V put(String key, V value) {
if (this.containsKey(key)) {
throw new IllegalArgumentException(this.name + " already contains value for " + key);
} else {
if (key.contains(".")) {
String shortKey = this.getShortName(key);
if (super.get(shortKey) == null) {
super.put(shortKey, value);
} else {
super.put(shortKey, new Configuration.StrictMap.Ambiguity(shortKey));
}
}
return super.put(key, value);
}
}
mybatis框架添加了一个StrictMap类,继承自HashMap,当put已经存在的key时,抛出异常。
从上面的过程中我们可以看到,在xml解析的key和方法注解解析出来的key是有可能重复的,如果重复了StrictMap的put方法就会抛出异常。
mybatis加载相关的类一览
mybatis加载相关的类主要都在builder这个包下,我们已经见识了多数的类,builder包结构如下: