写在前面
在这篇文章中我们分析了解析全局配置文件的过程,在其中的"5.1.10:mappers"分析了解析<mappers/>
标签时解析对应mapper xml的过程,会解析成XMLMapperBuilder对象,但是并没有详细分析解析过程,本文一起来看下,作为补充,所做的工作其实就是加载mapper xml映射配置文件
。
想要系统学习的,可以参考这篇文章,重要!!!。
1:入口
源码如下:
org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
private void parseConfiguration(XNode root) {
try {
...snip...
// <202107161610>
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
<202107161610>
处源码如下:
org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement
private void mapperElement(XNode parent) throws Exception {
if(parent != null) {
for (XNode child : parent.getChildren()) {
// 有package,如配置:
/*
<mappers>
<package name="yudaosourcecode.mybatis.mapper"/>
</mappers>
*/
if ("package".equals(child.getName())) {
// 获取name的值。即包名
String mapperPackage = child.getStringAttribute("name");
// <202108101735>
configuration.addMappers(mapperPackage);
} else {
// 获取resource,如<mapper resource="mybatis/mapper/TestTypehandlerMapper.xml"/>
String resource = child.getStringAttribute("resource");
// 获取url
String url = child.getStringAttribute("url");
// 获取class
String mapperClass = child.getStringAttribute("class");
// 优先处理resource,此时为类路径资源
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
// 获取资源
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创建XMLMapperBuilder
// <202107161617>
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 执行解析,解析完毕后,就知道mapper xml对应的接口类是哪个了
// <202107161724>
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) { // 处理url情况
ErrorContext.instance().resource(url);
// 根据url获取资源
InputStream inputStream = Resources.getUrlAsStream(url);
// 创建XMLMapperBuilder
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
// 执行解析
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) { // 直接指定了class
// 获取映射的mapper接口的class
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.");
}
}
}
}
}
<202107161617>
处构造函数函数源码如下:
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
// XPathParser:是一个基于xpath解析xml的工具,在mybatis的parsing包中,属于解析模块的一个功能
// configuration.getVariables():在全局配置文件中通过<properties/>标签配置的信息
// <202107161626>
this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}
<202107161626>
处源码如下:
/*
parser:基于xpath解析xml的工具类
configuration:解析全局config配置文件得到的配置类
resource:mapper xml的位置
sqlFragments:存储<sql>标签配置的sql语句段的集合
*/
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
super(configuration);
// 创建MapperBuilderAssistant对象
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
this.parser = parser;
this.sqlFragments = sqlFragments;
this.resource = resource;
}
<202107161724>
处是执行解析,具体参考2:XMLMapperBuilder#parse
。<202108101735>
是在全局配置文件中配置通过<package>
直接配置要扫描的包路径,可能如下配置:
<mappers>
<package name="yudaosourcecode.mybatis.annotation"/>
</mappers>
这种方式配置不仅仅会解析到mapper xml,也会解析到使用注解方式配置的mapper 接口此时就不需要mapper xml了
,本文会分析到前者,关于后者可以参考这篇文章。
2:XMLMapperBuilder#parse
源码如下:
org.apache.ibatis.builder.xml.XMLMapperBuilder#parse
public void parse() {
// <202107161747>
if (!configuration.isResourceLoaded(resource)) {
// <202107161755>
// 解析<mapper/>节点
configurationElement(parser.evalNode("/mapper"));
// 标记资源已经加载过
/*
org.apache.ibatis.session.Configuration#addLoadedResource
public void addLoadedResource(String resource) {
// protected final Set<String> loadedResources = new HashSet<String>();
loadedResources.add(resource);
}
*/
configuration.addLoadedResource(resource);
// 绑定命名空间到mapper接口的jdk动态代理实现类
// <202107301547>
bindMapperForNamespace();
}
// 注意以下待定的意思是,在前面的解析中存在解析到一半失败的节点,可能是因为有些依赖节点没有解析到导致
// 所以这里再次重复解析
// 解析待定的resultMap节点
parsePendingResultMaps();
// 解析待定的cache-ref节点
parsePendingChacheRefs();
// 解析待定的statement节点
parsePendingStatements();
}
<202107161747>
处是判断mapp xml资源是否已经加载过,源码如下:
org.apache.ibatis.session.Configuration#isResourceLoaded
public boolean isResourceLoaded(String resource) {
// protected final Set<String> loadedResources = new HashSet<String>();
// 通过set集合存储已经加载的资源对应的位置信息
return loadedResources.contains(resource);
}
<202107161755>
处是解析mapper节点,具体参考2.1:mapper节点解析
,<202107301547>
处是绑定mapper xml的对应的命名空间到mapper接口具体的jdk动态代理,源码如下:
org.apache.ibatis.builder.xml.XMLMapperBuilder#bindMapperForNamespace
private void bindMapperForNamespace() {
// 获取命名空间,其实就是mapper的接口名称,正常都是这样配置的
Stringnamespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
// 反射获取绑定的class类型
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
}
if (boundType != null) {
// 如果不存在当前mapper信息
// protected MapperRegistry mapperRegistry = new MapperRegistry(this);
if (!configuration.hasMapper(boundType)) {
// 防止重复加载
// protected final Set<String> loadedResources = new HashSet<String>();
configuration.addLoadedResource("namespace:" + namespace);
// 设置映射的类型
// protected MapperRegistry mapperRegistry = new MapperRegistry(this);
// 这里的boundType就是mapper接口的全限定名称,后续基于此来生成jdk动态代理实现类
// 2021-09-09 17:47:57
configuration.addMapper(boundType);
}
}
}
}
2021-09-09 17:47:57
处是生成mapper的jdk动态代理实现类,然后添加到映射注册器中,源码如下:
org.apache.ibatis.session.Configuration#addMapper
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
org.apache.ibatis.binding.MapperRegistry#addMapper
public <T> void addMapper(Class<T> type) {
// 是接口才处理
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 创建接口的动态代理工厂类并添加映射信息
// private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
// 2021-09-09 17:47:57
knownMappers.put(type, new MapperProxyFactory<T>(type));
...snip...
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
2021-09-09 17:47:57
处MapperProxyFactory是用来生成mapper动态代理的工厂类,具体参考3:MapperProxyFactory
。
2.1:mapper节点解析
源码如下:
org.apache.ibatis.builder.xml.XMLMapperBuilder#parse
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
// <202107171110>
configurationElement(parser.evalNode("/mapper"));
...snip...
}
...snip...
}
<202107171110>
处源码如下:
private void configurationElement(XNode context) {
try {
// 获取命名空间
// <mapper namespace="yudaosourcecode.mybatis.mapper.TestResultmapAndParametermapMapper"><mapper/>
String namespace = context.getStringAttribute("namespace");
// 强制要求有namespace,如果是没有设置则异常
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
// 设置当前的命名空间值到映射构建帮助类MapperBuilderAssistant中
builderAssistant.setCurrentNamespace(namespace);
// <202107171125>
cacheRefElement(context.evalNode("cache-ref"));
// 解析<cache/>标签
// <202107181024>
cacheElement(context.evalNode("cache"));
// 解析parameterMap的参数映射
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// <202107181124>
// 解析返回结果的参数配置resultMap
resultMapElements(context.evalNodes("/mapper/resultMap"));
// <202107301052>
// 解析mapper下的sql标签
sqlElement(context.evalNodes("/mapper/sql"));
// <202107301409>
// 解析增insert删delete改update查select标签
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
<202107181024>
处是解析<cache>
标签,具体参考2.1.2:解析cache标签
。<202107181124>
处是解析resultMap,具体参考2.1.4:解析resultMap节点
。<202107301052>
处是解析sql标签,具体参考2.1.5:解析sql标签
。<202107301409>
处是解析增删改查标签,具体参考2.1.6:解析增删改查标签
。
<202107171125>
是解析<cache-ref>
处源码如下:
org.apache.ibatis.builder.xml.XMLMapperBuilder#cacheRefElement
private void cacheRefElement(XNode context) {
if (context != null) {
// builderAssistant.getCurrentNamespace():当前mapper xml的namespace
// context.getStringAttribute("namespace"),<cache-ref namepsace="xxx.yyy">
// 节点中配置的namepsace
// 将这个cache的引用关系添加到configuration的map
// protected final Map<String, String> cacheRefMap = new HashMap<String, String>();中
configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
// 使用参数builderAssistant,和当前的mapper xml的namespace创建
// CacheRefResolver解析器对象
CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
try {
// 解析cache-ref
// <202107180955>
cacheRefResolver.resolveCacheRef();
} catch (IncompleteElementException e) {
configuration.addIncompleteCacheRef(cacheRefResolver);
}
}
}
<202107180955>
处参考2.1.1:解析cache-ref
。
2.1.1:解析cache-ref
源码如下:
org.apache.ibatis.builder.CacheRefResolver#resolveCacheRef
public Cache resolveCacheRef() {
return assistant.useCacheRef(cacheRefNamespace);
}
org.apache.ibatis.builder.MapperBuilderAssistant#useCacheRef
public Cache useCacheRef(String namespace) {
// 如果是引用cache的命名空间为null,则抛出异常,并告知需要namepsace
// 属性的异常信息
// 如写<cache-ref>但是没有指定namespace属性,就会触发这里的异常
if (namespace== null) {
throw new BuilderException("cache-ref element requires a namespace attribute.");
}
try {
unresolvedCacheRef = true;
// 从配置中通过命名空间获取cache,Cache对象都是以命名空间为单位的
Cache cache = configuration.getCache(namespace);
// 如果是根据命名空间没有获取到引用的缓存则抛出异常
if (cache == null) {
throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
}
currentCache = cache;
unresolvedCacheRef = false;
return cache;
} catch (IllegalArgumentException e) {
throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
}
}
2.1.2:解析cache标签
源码如下:
org.apache.ibatis.builder.xml.XMLMapperBuilder#cacheElement
private void cacheElement(XNode context) throws Exception {
if (context != null) {
// 获取二级缓存使用的缓存类型,默认是PERPETUAL、即org.apache.ibatis.cache.impl.PerpetualCache(系统默认提供)
// 也可以通过实现接口org.apache.ibatis.cache.Cache自定义
String type = context.getStringAttribute("type", "PERPETUAL");
// 解析class
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
// 获取缓存的淘汰算法,默认LRU,也可以是FIFO,WEAK,SOFT
String eviction = context.getStringAttribute("eviction", "LRU");
// 获取淘汰算法对应的class
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
// 获取缓存的刷新间隔,默认不刷新
Long flushInterval = context.getLongAttribute("flushInterval");
// 获取缓存的大小
Integer size = context.getIntAttribute("size");
// 获取缓存是否只读,只读则总是返回相同实例,但不安全,非只读返回不同实例
// 但是安全,因此默认值给false,即非只读(注意:这里的只读并非真的只读,而是说总是一个对象,只能只读)
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
// 获取所有的子节点信息,name->value
Properties props = context.getChildrenAsProperties();
// <202107181039>
// 添加新缓存
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, props);
}
}
<202107181039>
处是添加新缓存,具体参考2.1.3:添加命名空间缓存
.
2.1.3:添加命名空间缓存
源码如下:
org.apache.ibatis.builder.MapperBuilderAssistant#useNewCache
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
Properties props) {
// 获取缓存类型的class,提供了则使用提供的,否则使用PerpetualCache
typeClass = valueOrDefault(typeClass, PerpetualCache.class);
// 淘汰算法的class,提供了则使用提供的,否则使用LruCache.class
evictionClass = valueOrDefault(evictionClass, LruCache.class);
// 通过cache构造器构造缓存对象
Cache cache = new CacheBuilder(currentNamespace)
.implementation(typeClass)
.addDecorator(evictionClass)
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.properties(props)
.build();
// 添加奥configuration配置类的换集合中
/*
org.apache.ibatis.session.Configuration#addCache
public void addCache(Cache cache) {
caches.put(cache.getId(), cache);
}
*/
configuration.addCache(cache);
// 赋值到当前缓存
currentCache = cache;
// 返回缓存
return cache;
}
2.1.4:解析resultMap节点
源码如下;
org.apache.ibatis.builder.xml.XMLMapperBuilder#resultMapElements
private void resultMapElements(List<XNode> list) throws Exception {
// 循环处理所有的<resultMap/>节点
for (XNode resultMapNode : list) {
try {
// <202107181410>
resultMapElement(resultMapNode);
} catch (IncompleteElementException e) {
// ignore, it will be retried
}
}
}
<202107181410>
处源码如下:
org.apache.ibatis.builder.xml.XMLMapperBuilder#resultMapElement(org.apache.ibatis.parsing.XNode)
private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList());
}
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
// 获得id属性的值
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
// 获得type属性的值
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
// 获取extend的值,即继承的resultMap
String extend = resultMapNode.getStringAttribute("extends");
// 获取autoMapping的值,即resultMap中没有配置result的结果列是否自动映射
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
// 创建resultMap中配置的type对应的类的class类型对象
Class<?> typeClass = resolveClass(type);
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
resultMappings.addAll(additionalResultMappings);
List<XNode> resultChildren = resultMapNode.getChildren();
// 获取resultMap的子节点并循环处理子节点
for (XNode resultChild : resultChildren) {
// <202107191613>
// 如果是<constructor>标签
if ("constructor".equals(resultChild.getName())) {
processConstructorElement(resultChild, typeClass, resultMappings);
} else if ("discriminator".equals(resultChild.getName())) {
// 解析<discriminator>标签,鉴别器,用于依赖某个列的值,来动态为属性赋值
// <202107211750>
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else { // 到这里,说明就是<id>,<result>,<collection>,<association>等标签
ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
// 如果是<id>标签,则增加ResultFlag.ID枚举到flags中
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
// <202107211806>
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
// 构造函数如下:
/*
public ResultMapResolver(MapperBuilderAssistant assistant, String id, Class<?> type, String extend, Discriminator discriminator, List<ResultMapping> resultMappings, Boolean autoMapping) {
// 映射帮助类
this.assistant = assistant;
this.id = id;
// 实体类型
this.type = type;
// 继承的resultMap信息
this.extend = extend;
// 鉴别器
this.discriminator = discriminator;
// 内部所有的节点信息
this.resultMappings = resultMappings;
// 是否自动映射没有配置的属性
this.autoMapping = autoMapping;
}
*/
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
// <202107211821>
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
<202107191613>
处是解析resultMap的constructor子标签,源码如下:
org.apache.ibatis.builder.xml.XMLMapperBuilder#processConstructorElement
private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
// 获取所有的子节点,如下可能配置:
/*
<resultMap id="myResultMap" type="yudaosourcecode.mybatis.model.TestResultmapConstructor">
<constructor>
<idArg javaType="integer" jdbcType="INTEGER" column="id"/>
<arg javaType="string" jdbcType="VARCHAR" column="myname"/>
<arg javaType="integer" jdbcType="INTEGER" column="myage"/>
</constructor>
</resultMap>
*/
List<XNode> argChildren = resultChild.getChildren();
for (XNode argChild : argChildren) {
// ResultFlag是一个枚举类,如下:
/*
org.apache.ibatis.mapping.ResultFlag
public enum ResultFlag {
ID, CONSTRUCTOR
}
*/
ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
// 先添加ResultFlag.CONSTRUCTOR
flags.add(ResultFlag.CONSTRUCTOR);
// 如果时idArg,再添加ResultFlag.ID
if ("idArg".equals(argChild.getName())) {
flags.add(ResultFlag.ID);
}
// <202107191636>
// 将当前子节点构造成ReusltMapping对象,并添加到resultMappings集合中
resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags));
}
}
<202107191636>
处buildResultMappingFromContext
用于构建ResultMapping对象,具体参考2.2:构建ResultMapping
。<202107211750>
处鉴别器,源码如下:
org.apache.ibatis.builder.xml.XMLMapperBuilder#processDiscriminatorElement
private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
// 获取各种属性
String column = context.getStringAttribute("column");
String javaType = context.getStringAttribute("javaType");
String jdbcType = context.getStringAttribute("jdbcType");
String typeHandler = context.getStringAttribute("typeHandler");
Class<?> javaTypeClass = resolveClass(javaType);
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
Map<String, String> discriminatorMap = new HashMap<String, String>();
for (XNode caseChild : context.getChildren()) {
// 获取case 的处理的对应column的value
String value = caseChild.getStringAttribute("value");
// 获取使用的resultMap
String resultMap = caseChild.getStringAttribute("resultMap", processNestedResultMappings(caseChild, resultMappings));
discriminatorMap.put(value, resultMap);
}
// 构造Discriminator
return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass, discriminatorMap);
}
<202107211806>
处buildResultMappingFromContext是构建ResultMapping对象,具体参考2.2:构建ResultMapping
。<202107211821>
处具体参考2.3:解析resultMap标签为ResultMap对象
。
2.1.5:解析sql标签
源码如下:
org.apache.ibatis.builder.xml.XMLMapperBuilder#sqlElement
// list参数是所有sql节点信息,因为在一个命名空间的的mapper中可以配置多个,所以是个集合
private void sqlElement(List<XNode> list) throws Exception {
// 这里的datbaseid一般是在mybatis通过数据库的连接信息获取的
// 另外在sql节点内也可以配置databaseId属性
if (configuration.getDatabaseId() != null) {
sqlElement(list, configuration.getDatabaseId());
}
// <202107301107>
sqlElement(list, null);
}
<202107301107>
处源码如下:
org.apache.ibatis.builder.xml.XMLMapperBuilder#sqlElement(java.util.List<org.apache.ibatis.parsing.XNode>, java.lang.String)
private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
// 循环所有节点,依次处理
for (XNode context : list) {
// 获取节点配置的databaseId属性的值
String databaseId = context.getStringAttribute("databaseId");
// 获取节点id属性的值
String id = context.getStringAttribute("id");
// 添加命名空间前缀,生成完整的id信息,因为不同命名空间的id是可以重复的,所以这里需要添加命名空间前缀
id = builderAssistant.applyCurrentNamespace(id, false);
// <202107301123>
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) sqlFragments.put(id, context);
}
}
<202107301123>
处databaseIdMatchesCurrent
源码如下:
org.apache.ibatis.builder.xml.XMLMapperBuilder#databaseIdMatchesCurrent
// 方法整体逻辑比较复杂,知道逻辑即可
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
// 如果是全局的databseId有值
if (requiredDatabaseId != null) {
// 如果是和databseId属性配置的不相等,则return false
if (!requiredDatabaseId.equals(databaseId)) {
return false;
}
} else {
// 如果是没有全局databaseId,但是设置了databseId属性,return false
if (databaseId != null) {
return false;
}
// 如果是已经包含该sql
if (this.sqlFragments.containsKey(id)) {
// 获取已经包含的sql
XNode context = this.sqlFragments.get(id);
// 从已经包含的sql节点信息获取配置的databaseId属性,如果配置了也返回false
if (context.getStringAttribute("databaseId") != null) {
return false;
}
}
}
// 最后直接返回true
return true;
}
<202107301123>
处sqlFragments
定义如下:
// key:带有命名空间的sql节点id,value:sql节点对应的节点对象
private Map<String, XNode> sqlFragments;
2.1.6:解析增删改查标签
源码如下:
org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>)
private void buildStatementFromContext(List<XNode> list) {
// 如果有要求的databaseId
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
// <202107301506>
// 构建表达式
buildStatementFromContext(list, null);
}
<202107301506>
处是构建statement,源码如下:
org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>, java.lang.String)
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
// 依次遍历<insert><delete><update><select>节点,执行解析
for (XNode context : list) {
// <202107301526>
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
// 如果是解析失败了,则添加到不完备的语句集合中,后续会再次尝试解析
// protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
configuration.addIncompleteStatement(statementParser);
}
}
}
<202107301526>
处是创建XMLStatementBuilder并执行具体的解析,详细参考这篇文章。
2.2:构建ResultMapping
源码如下:
org.apache.ibatis.builder.xml.XMLMapperBuilder#buildResultMappingFromContext
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, ArrayList<ResultFlag> flags) throws Exception {
// 获取各种各样的属性
String property = context.getStringAttribute("property");
String column = context.getStringAttribute("column");
String javaType = context.getStringAttribute("javaType");
String jdbcType = context.getStringAttribute("jdbcType");
String nestedSelect = context.getStringAttribute("select");
String nestedResultMap = context.getStringAttribute("resultMap",
processNestedResultMappings(context, Collections.<ResultMapping> emptyList()));
String notNullColumn = context.getStringAttribute("notNullColumn");
String columnPrefix = context.getStringAttribute("columnPrefix");
String typeHandler = context.getStringAttribute("typeHandler");
String resulSet = context.getStringAttribute("resultSet");
String foreignColumn = context.getStringAttribute("foreignColumn");
boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
// 解析javaType为对应的class
Class<?> javaTypeClass = resolveClass(javaType);
// 解析typeHandler为对应的class
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
// 解析jdbcType为对应的org.apache.itabis.type.JdbcType枚举类
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
// <202107191647>
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resulSet, foreignColumn, lazy);
}
<202107191647>
处是通过映射构建帮助类构造ResultMapping,源码如下:
org.apache.ibatis.builder.MapperBuilderAssistant#buildResultMapping
public ResultMapping buildResultMapping(
Class<?> resultType,
String property,
String column,
Class<?> javaType,
JdbcType jdbcType,
String nestedSelect,
String nestedResultMap,
String notNullColumn,
String columnPrefix,
Class<? extends TypeHandler<?>> typeHandler,
List<ResultFlag> flags,
String resultSet,
String foreignColumn,
boolean lazy) {
// 解析java类型的class
// <202107191656>
Class<?> javaTypeClass = resolveResultJavaType(resultType, property, javaType);
// 解析对一个的类型处理器类(数据库<->pojo属性的处理类)
// <202107191704>
TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);
// 解析<constructor>标签中的column属性
// <202107211419>
List<ResultMapping> composites = parseCompositeColumnName(column);
// 如果是配置column,则将column赋值为null
if (composites.size() > 0) column = null;
// configuration:配置对象
// property:当前标签的property属性值
// column:当前标签的column属性值
// javaTypeClass:当前标签的javaType对应的class
// 创建结果映射构造器对象
ResultMapping.Builder builder = new ResultMapping.Builder(configuration, property, column, javaTypeClass);
// 设置各种属性
builder.jdbcType(jdbcType);
// 设置内嵌的查询id,即其他的statement的id,最终是生成带有命名空间的id
// 解析select属性,即引用的其他或者是自己的命名空间的statement
// 如<collection ... select="yudaosourcecode.mybatis.mapper.TestResultmapCollectionManyMapper.getById1" .../>
// 我认为既然是引用其他的statement,那么不管是否和当前命名空间一致,写上全路径会非常清晰,代码如下:
/*
org.apache.ibatis.builder.MapperBuilderAssistant#applyCurrentNamespace
public String applyCurrentNamespace(String base, boolean isReference) {
if (base == null) return null;
if (isReference) {
// is it qualified with any namespace yet?
if (base.contains(".")) return base;
} else {
// is it qualified with this namespace yet?
if (base.startsWith(currentNamespace + ".")) return base;
if (base.contains(".")) throw new BuilderException("Dots are not allowed in element names, please remove it from " + base);
}
return currentNamespace + "." + base;
}
*/
builder.nestedQueryId(applyCurrentNamespace(nestedSelect, true));
// 设置内嵌的resultMap id(带有命名空间)
// 如:<collection .. resultMap="xxx.yyy.zzz.someOtherResultMapId" .../>
builder.nestedResultMapId(applyCurrentNamespace(nestedResultMap, true));
builder.resultSet(resultSet);
builder.typeHandler(typeHandlerInstance);
builder.flags(flags == null ? new ArrayList<ResultFlag>() : flags);
builder.composites(composites);
builder.notNullColumns(parseMultipleColumnNames(notNullColumn));
builder.columnPrefix(columnPrefix);
builder.foreignColumn(foreignColumn);
builder.lazy(lazy);
// 构造ResultMapping对象,并返回
return builder.build();
}
<202107191656>
处事解析对应的java类型,源码如下:
org.apache.ibatis.builder.MapperBuilderAssistant#resolveResultJavaType
private Class<?> resolveResultJavaType(Class<?> resultType, String property, Class<?> javaType) {
// 如果是传入的javaType和property不为null
if (javaType == null && property != null) {
try {
// 通过反射包工具类,获取property的class类型
MetaClass metaResultType = MetaClass.forClass(resultType);
javaType = metaResultType.getSetterType(property);
} catch (Exception e) {
//ignore, following null check statement will deal with the situation
}
}
// 如果是到这javaType还为null,则赋值为Object.class
if (javaType == null) {
javaType = Object.class;
}
return javaType;
}
<202107191704>
处是解析获取对应的类型处理器实例,源码如下:
org.apache.ibatis.builder.BaseBuilder#resolveTypeHandler
protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) {
// 为null,代表用户没有为该映射设置类型处理器,直接返回null
if (typeHandlerType == null) return null;
// 从类型处理器注册器中获取类型处理器
TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
if (handler == null) {
// 如果是在类型处理器注册机中没有,则直接实例化出来一个
handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);
}
return handler;
}
<202107211419>
处是解析<resultMap>中<collection>中column属性
,源码如下:
org.apache.ibatis.builder.MapperBuilderAssistant#parseCompositeColumnName
private List<ResultMapping> parseCompositeColumnName(String columnName) {
List<ResultMapping> composites = new ArrayList<ResultMapping>();
// 关联查询配置多个映射,可能值"{oneid=id,outerManyName=manyname}"
// 如果包含"=",并且包含","
if (columnName != null && (columnName.indexOf('=') > -1 || columnName.indexOf(',') > -1)) {
// 分别解析处配置的属性映射关系
StringTokenizer parser = new StringTokenizer(columnName, "{}=, ", false);
while (parser.hasMoreTokens()) {
String property = parser.nextToken();
String column = parser.nextToken();
ResultMapping.Builder complexBuilder = new ResultMapping.Builder(configuration, property, column, configuration.getTypeHandlerRegistry().getUnknownTypeHandler());
// 创建结果映射对象并添加到要返回的集合中
composites.add(complexBuilder.build());
}
}
return composites;
}
2.3:解析resultMap标签为ResultMap对象
源码如下:
org.apache.ibatis.builder.ResultMapResolver#resolve
public ResultMap resolve() {
return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);
}
org.apache.ibatis.builder.MapperBuilderAssistant#addResultMap
public ResultMap addResultMap(
String id,
Class<?> type,
String extend,
Discriminator discriminator,
List<ResultMapping> resultMappings,
Boolean autoMapping) {
// 添加mapper xml的namespace前缀
id =applyCurrentNamespace(id, false);
// 给extend添加mapper xml的namespace前缀
extend = applyCurrentNamespace(extend, true);
// ResultMap构造器
ResultMap.Builder resultMapBuilder = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping);
// 有extend
if (extend != null) {
// 检查是否有该extend的resultMap,没有则异常
if (!configuration.hasResultMap(extend)) {
throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
}
// 从configuration获取extend对应的ResultMap
ResultMap resultMap = configuration.getResultMap(extend);
// 获取继承的ResultMap内的所有的ResultMappings
List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings());
// 删除继承的在当前已有的
extendedResultMappings.removeAll(resultMappings);
// 判断当前ResultMap是声明了<construcotr>
boolean declaresConstructor = false;
for (ResultMapping resultMapping : resultMappings) {
// 声明了,则将标记设置为true
if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
declaresConstructor = true;
break;
}
}
// 包含<constructor>声明,循环继承的,删除里面的声明
if (declaresConstructor) {
Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator();
while (extendedResultMappingsIter.hasNext()) {
if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {
extendedResultMappingsIter.remove();
}
}
}
// 添加继承的ResultMapping信息到当前
resultMappings.addAll(extendedResultMappings);
}
resultMapBuilder.discriminator(discriminator);
// 构造ResultMap
ResultMap resultMap = resultMapBuilder.build();
// 添加<resultMap/>标签对应的ResultMap对象到configuration中
configuration.addResultMap(resultMap);
// 返回解析出的ResultMap对象
return resultMap;
}
3:MapperProxyFactory
这是一个用来生成mapper接口jdk动态代理的工厂类,源码如下:
org.apache.ibatis.binding.MapperProxyFactory
public class MapperProxyFactory<T> {
// mapper接口
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
// 获取代理的mapper接口
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
// 创建mapper接口代理类
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
// 生成jdk动态代理中用于执行方法的java.lang.reflect.InvocationHandler类MapperProxy
//, 该类是核心类,在执行数据库操作时会执行到这里的invoke方法
// 2021-09-09 17:58:56
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
2021-09-09 17:58:56
处是创建org.apache.ibatis.bingding.MapperProxy
,这是生成mapper接口动态代理类要使用的java.lang.reflect.InvocationHandler
实现类。我们重点来看其对invoke
方法的实现,具体参考3.1:MaperProxy#invoke
。
3.1:MaperProxy#invoke
当我们执行如下代码:
String resource = "mybatis/mybatis-config-1.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
this.sqlSessionFactory = sqlSessionFactory;
this.sqlSession = this.sqlSessionFactory.openSession();
this.testExtendAndAutoMappingMapper =
this.sqlSession.getMapper(TestExtendAndAutoMappingMapper.class);
获取mapper接口的实现类时,返回的其实就是基于jdk的生成动态代理类,通过前面分析,我们知道其InvocationHandler使用的是MaperProxy
,因此调用mapper方法时,会首先执行org.apache.ibatis.binding.MapperProxy#invoke
,源码如下:
org.apache.ibatis.binding.MapperProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// toString,equals,hashCode等Object类定义的方法
if (Object.class.equals(method.getDeclaringClass())) {
try {
// 调用的是Object类的方法,直接调用并返回
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
// 创建MapperMethod,该类封装接口信息,方法信息,以及全局配置文件的configuration对象信息
// 源码如下:
/*
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
*/
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 通过MapperMethod对象执行
// 2021-09-10 15:12:30
return mapperMethod.execute(sqlSession, args);
}
2021-09-10 15:12:30
处是通过org.apache.ibatis.bingding.MaperMethod
对象执行,具体参考3.2::MapperMethod#execute
。
3.2::MapperMethod#execute
源码如下:
org.apache.ibatis.binding.MapperMethod#execute
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// 根据对应的statement类型,调用会话对象org.apache.ibatis.session.SqlSession的API执行
// 具体的数据库操作
// 2021-09-10 15:17:17
if (SqlCommandType.INSERT == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) {
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else if (SqlCommandType.FLUSH == command.getType()) {
result = sqlSession.flushStatements();
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
2021-09-10 15:17:17
是根据用户命令的类型来调用会话对象org.apache.ibatis.session.SqlSession
的不同方法,方法内部会调用执行器的子类org.apache.ibatis.executor.CachingExecutor
来执行后续的操作,关于这个调用过程,可以参考这篇文章。
写在后面
看源码真的是好无聊!!!不过,耐得住寂寞的人,才又有所成就吧!!