MyBatis基础支持层位于 Mybatis 整体架构的最底层,支撑着 Mybatis 的核心处理层,是整个框架的基石。基础支持层中封装了多个较为通用的、独立的模块,不仅仅为 Mybatis 提供基础支撑,也可以在合适的场景中直接复用。
这篇文章介绍MyBatis的资源加载模块
类加载器
Java虚 拟 机中的类 加载 器(ClassLoader)负 责 加载 来 自文件系统 、网 络 或其他来 源的类 文 件。Java虚 拟 机中的类 加载 器默认 使用的是双 亲 委派模式,如图所示,其中有三种 默认 使 用 的 类 加 载 器 ,分 别 是 Bootstrap ClassLoader
、Extension ClassLoader
和 System ClassLoader
(也 被称 为 ApplicationClassLoader
),每种 类 加载 器都己经 确 定从 哪 个 位置加载 类 文件。
BootstrapClassLoader
负 责 加载 JDK自带 的rt.jar
包中的类 文件,它 是所有类 加载 器的父加载 器,Bootstrap ClassLoader
没 有任何父类 加载 器。Extension ClassLoader
负 责 加 载 Java的扩 展类 库 ,也就是从 jre/lib/ext
目录 下或者java.ext.dirs
系统 属 性指定的目录 下加载 类 。
SystemClassLoader
负 责 从 classpath
环 境变 量中加载 类 文件,classpath
环 境变 量通常由-classpath
或 -cp
命令行选 项 来 定义 ,或 是 由 JAR中 Manifest文 件 的 classpath属 性指定。System ClassLoader
是ExtensionClassLoader
的子加载 器。
根据双亲委派模式,在加载类文件时,子加载器首先会将加载请求委托给它的父加载器。
父加载器会检测自己是否已经加载过该类,如果己加载则加载过程结束;如果未加载则请求继 续 向上传 递 ,直到BootstrapClassLoader。
如果在请 求向上委托的过 程中,始终 未检 测 到该 类 己 加载 ,则 从 BootstrapClassLoader开 始尝 试 从 其对 应 路径 中加载 该 类 文件,如果加载 失败 则 由 子加载器继续尝试加载,直至发起加载请求的子加载器位为止。
双 亲 委派模式可以保证 两 点:
一是子加载 器可以使用父加载 器己加载 的类 ,而父加载 器无 法使用子加载 器已加载 的类 ;
二是父加载 器已加载 过 的类 无法被子加载 器再次加载 。
这 样 就可以保证 JVM的安全性和稳 定性。
ClassLoaderWrapper
上上一小节中了解了类 加载 器的常见 使用方式。在 MyBatis的 IO
包中封装 了 ClassLoader以及读 取资 源文件的相关 API。
在 IO
包 中 提 供 的 ClassLoaderWrapper
是 一 个 ClassLoader
的包装 器,其中包含了多个ClassLoader
对 象。
通过 调 整多个 类 加载 器的使用顺 序,ClassLoaderWrapper
可以确 保返回给 系 统 使用的是正确 的类 加载 器。
使 用 ClassLoaderWrapper
就如同使用一个 ClassLoader
对 象, ClassLoaderWrapper
会 按照指定的顺 序依次检 测 其中封装 的ClassLoader
对 象,并 从 中选 取第一 个 可用的ClassLoader
完成相关 功能。
ClassLoaderWrapper
的 主 要 功 能 可 以 分 为 三 类 , 分 别 是 getResourceAsURL()方 法
、 classForName()方法
、getResourceAsStream()方法
,这 三个 方法都有多个 重载 ,这 三类 方法最终 都会 调 用参 数 为 String
和ClassLoader[]
的重载 。
getResourceAsURL()
代码如下,其他的类似:
public URL getResourceAsURL(String resource, ClassLoader classLoader) { |
Resources是一个 提供了多个 静 态 方法的工具类 ,其中封装 了一个 ClassLoaderWrapper类 型 的静 态 字段,Resources提供的这 些静 态 工具都是通过 调 用该 ClassLoaderWrapper对 象的相应 方 法实 现 的。
ResolverUtil
ResolverUtil
可以根据指定的条件查找指定包下的类 ,其中使用的条件由Test接口表示。
ResolverUtil
中使用classLoader
字 段 (ClassLoader
类 型)记 录 了当 前使用的类 加载 器,默认 情 况 下,使用的是当 前线 程上下文绑 定的ClassLoader
,我们 可以通过 setClassLoader()
方法修改使 用类加载器。
MyBatis提供了两 个 常用的Test
接口实 现 ,分别 是IsA
和AnnotatedWith
,如图 所示。 IsA
用于检 测 类 是否继 承了指定的类 或接口,AnnotatedWith
用于检 测 类 是否添加了指定的注解。 开 发 人员 也可以自己实 现 Test
接口,实 现 指定条 件的检 测 。
Test接口中定义 了 matches()方法,它 用于检 测 指定类 是否符合条 件:
public interface Test { |
IsA
和 AnnotatedWith
的具体 实 现 如下
public static class IsA implements Test { |
默认 情况 下,使用
Thread.currentThread().getContextClassLoader()
这 个 类 加载 器加载 符合条 件的类 ,我们 可以在调 用find()
方法之前,调 用setClassLoader(ClassLoader)
设 置需要使用的ClassLoader
,这 个ClassLoader
可 以 从ClassLoaderWrapper
中 获 取 合 适 的 类 加 载 器 。
ResolverUtil的使用案例:
ResolverUtil<ActionBean> resolver = new ResolverUtil<ActionBean>(); |
ResolverUtil.findImplementations()
方法和ResolverUtil.findAnnotated()
方法都是依赖RescolverUtil.find()
方法实现的,findImplementations()
方法会创建IsA
对象作为检测条件,findAnnotated()
方法会创建AnnotatedWith
对象作为检测条件。
public ResolverUtil<T> find(Test test, String packageName) { |
VFS
VFS表示虚 拟 文件系统 (VirtualFileSystem),它 用来 査找 指定路径 下的资 源。VFS是一个 抽象类 ,MyBatis中提供了 JBoss6VFS和 DefaultVFS两 个 VFS的实 现 ,如图所示。用户 也可以提供自定义 的VFS实 现 类 。
VFS采用单例模式实现
参考
《MyBatis技术内幕》
部分图片来源——《MyBatis技术内幕》