MyBatis 源码分析 - 映射文件解析过程 (2)

如上所示,重点语句我用黑体标注了出来。限于 Java 注解的表达力和灵活性,通过注解的方式并不能完全发挥 MyBatis 的能力。所以,对于一些较为复杂的配置信息,我们还是应该通过 XML 的方式进行配置。正因此,在接下的章节中,我会重点分析基于 XML 的映射文件的解析过程。如果能弄懂此种配置方式的解析过程,那么基于注解的解析过程也不在话下。

下面开始分析映射文件的解析过程,在展开分析之前,先来看一下映射文件解析入口。如下:

// -☆- XMLMapperBuilder public void parse() { // 检测映射文件是否已经被解析过 if (!configuration.isResourceLoaded(resource)) { // 解析 mapper 节点 configurationElement(parser.evalNode("/mapper")); // 添加资源路径到“已解析资源集合”中 configuration.addLoadedResource(resource); // 通过命名空间绑定 Mapper 接口 bindMapperForNamespace(); } // 处理未完成解析的节点 parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }

如上,映射文件解析入口逻辑包含三个核心操作,分别如下:

解析 mapper 节点

通过命名空间绑定 Mapper 接口

处理未完成解析的节点

这三个操作对应的逻辑,我将会在随后的章节中依次进行分析。下面,先来分析第一个操作对应的逻辑。

2.1 解析映射文件

在 MyBatis 映射文件中,可以配置多种节点。比如 <cache>,<resultMap>,<sql> 以及 <select | insert | update | delete> 等。下面我们来看一个映射文件配置示例。

<mapper namespace="xyz.coolblog.dao.AuthorDao"> <cache/> <resultMap type="Author"> <id property="id" column="id"/> <result property="name" column="name"/> <!-- ... --> </resultMap> <sql> author </sql> <select resultMap="authorResult"> SELECT id, name, age, sex, email FROM <include refid="table"/> WHERE id = #{id} </select> <!-- <insert|update|delete/> --> </mapper>

上面是一个比较简单的映射文件,还有一些的节点没有出现在上面。以上每种配置中的每种节点的解析逻辑都封装在了相应的方法中,这些方法由 XMLMapperBuilder 类的 configurationElement 方法统一调用。该方法的逻辑如下:

private void configurationElement(XNode context) { try { // 获取 mapper 命名空间 String namespace = context.getStringAttribute("namespace"); if (namespace == null || namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } // 设置命名空间到 builderAssistant 中 builderAssistant.setCurrentNamespace(namespace); // 解析 <cache-ref> 节点 cacheRefElement(context.evalNode("cache-ref")); // 解析 <cache> 节点 cacheElement(context.evalNode("cache")); // 已废弃配置,这里不做分析 parameterMapElement(context.evalNodes("/mapper/parameterMap")); // 解析 <resultMap> 节点 resultMapElements(context.evalNodes("/mapper/resultMap")); // 解析 <sql> 节点 sqlElement(context.evalNodes("/mapper/sql")); // 解析 <select>、...、<delete> 等节点 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); } }

上面代码的执行流程清晰明了。在阅读源码时,我们可以按部就班的分析每个方法调用即可。不过在写文章进行叙述时,需要做一些调整。下面我将会先分析 <cache> 节点的解析过程,然后再分析 <cache-ref> 节点,之后会按照顺序分析其他节点的解析过程。接下来,我们来看看 <cache> 节点的解析过程。

2.1.1 解析 <cache> 节点

MyBatis 提供了一、二级缓存,其中一级缓存是 SqlSession 级别的,默认为开启状态。二级缓存配置在映射文件中,使用者需要显示配置才能开启。如果没有特殊要求,二级缓存的配置很容易。如下:

<cache/>

如果我们想修改缓存的一些属性,可以像下面这样配置。

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

根据上面的配置创建出的缓存有以下特点:

按先进先出的策略淘汰缓存项

缓存的容量为 512 个对象引用

缓存每隔60秒刷新一次

缓存返回的对象是写安全的,即在外部修改对象不会影响到缓存内部存储对象

除了上面两种配置方式,我们还可以给 MyBatis 配置第三方缓存或者自己实现的缓存等。比如,我们将 Ehcache 缓存整合到 MyBatis 中,可以这样配置。

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/> <property value="3600"/> <property value="3600"/> <property value="1000"/> <property value="10000000"/> <property value="LRU"/> </cache>

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpszjz.html