IO模块是mybatis的各种资源加载方式的实现,比如从读取配置文件、如何加载一个class对象等等。
下面是包中的主要成员,出来给大家打个招呼啦
├── ClassLoaderWrapper.java 一个类加载容器包装,为的是方便实现统一的类加载逻辑.
├── DefaultVFS.java 缺省的VFS资源加载实现,关于VFS文章后有链接阅读.
├── ExternalResources.java 扩展资源加载工具类(已废弃)
├── JBoss6VFS.java Jboss6VFS资源加载实现
├── ResolverUtil.java 用于检测类是不是可用的工具类
├── Resources.java Mybatis内部主要使用的资源加载工具类
├── VFS.java VFS资源加载接口定义
可以说这个包主要的作用就是为mybatis框架提供外部资源的读取加载的能力,所以我们挑重点只分析一下两个类:
首先看到作者Clinton的注释是这么写的:
A class to wrap access to multiple class loaders making them work as one.
用来封装对多个类加载器的访问,对外提供统一的行为。
这就是装饰器。
这个类提供了一下几个方法
ClassLoaderWrapper() {
try {
// 一般情况下这个地方应该得到的是AppClassLoader
systemClassLoader = ClassLoader.getSystemClassLoader();
} catch (SecurityException ignored) {
// AccessControlException on Google App Engine
}
}
构造方法是protected的也就这能在IO包中使用,开闭原则。
从构造方法看到只是初始化了一个类成员变量systemClassLoader,这里默认情况下你会得到的是AppClassLoader对象,也就是我们经常使用的系统类加载器(它负责加载应用classpath目录下的所有jar和class文件),为什么初始化系统类加载器留着疑问到后边解答。
关于更多的类加载器介绍请看链接:Java类加载器
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
return new ClassLoader[]{
classLoader, //1. the custom CL
defaultClassLoader, // 2. default CL which provides by use also.
Thread.currentThread().getContextClassLoader(), // 3. the current thread's CL
getClass().getClassLoader(), // 4. the current ClassLoaderWrapper's CL
systemClassLoader}; // 5. try to use the system CL
}
提供了5中类加载器,分别是:
可以看出来,框架本身希望通过提供这么5中类加载器来完成系统的资源加载,而不是单一的希望通过特定某一个来加载资源。
public Class> classForName(String name, ClassLoader classLoader) throws ClassNotFoundException {
// 先获得一组 CL
return classForName(name, getClassLoaders(classLoader));
}
/*
* Attempt to load a class from a group of classloaders
* @param name - the class to load
* @param classLoader - the group of classloaders to examine
* @return the class
* @throws ClassNotFoundException - Remember the wisdom of Judge Smails: Well, the world needs ditch diggers, too.
*/
Class> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException {
for (ClassLoader cl : classLoader) {// 循环使用classLoader来尝试加载类
if (null != cl) {
try {
Class> c = Class.forName(name, true, cl);
if (null != c) {
return c;
}
} catch (ClassNotFoundException e) {
// we'll ignore this until all classloaders fail to locate the class
}
}
}
throw new ClassNotFoundException("Cannot find class: " + name);
}
这个方法其实非常容易理解,就是使用内部提供的多个CL(ClassLoader)逐个尝试加载类,其实就是为了方便使用自己的类加载来加载资源。实现自己的类加载器主要有两个用处:1. 加密,2. 特殊逻辑处理,比如IOC,特殊路径搜索,系统安全控制等。
可用从上面getClassLoaders和for (ClassLoader cl : classLoader)这里的循环看出,框架会优先使用自己定义的类加载器加载,如果加载不到才交给上一级的类加载器,最后才委托给系统类加载器,也就是JVM或者容器的类加载器。
其实这也摆脱了JVM默认提供的双亲委派的逻辑。
这两个方法跟上一个方法类似,都是实现资源加载器的顺序控制。另外提供这两个方法也为框架提供了如配置文件加载的实现。
这里也不展开了,可以简单看下类的api
public class Resources {
private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();
....
Resources() {
}
.....
这个类其实就是通过调用ClassLoaderWrapper来实现更加丰富的资源加载接口:file, properties, reader, stream, URL…
Mybatis使用了自己的一套资源文件加载逻辑,其目的就是为了实现自己的资源加载方式,这也是很多中间件框架喜欢的方式。比如把mybatis配置文件放到某个带有安全控制的统一服务器上,然后其他应用都通过api的方式来加载配置,这个时候就需要自定义一个类加载器,同时将这个类加载器放到getClassLoaders的前面。