【MyBatis源码解读】之IO模块

  |   0 评论   |   301 浏览

注:本文源码版本为3.4.6

IO模块总览

imagepng
一共包括了这几个类:
ClassLoaderWrapper:ClassLoader的包装类。
DefaultVFS:VFS的默认实现类。
ExternalResources:已经废弃了。
JBoss6VFS:JBoss 6实现的VFS。
ResolverUtil:用来通过某种方式来查找类,如果满足某个条件就查出该类。
Resources:通过类加载器简单的访问资源。
VFS:是提供用于访问应用内资源的一个简便接口。

ResolverUtil类

有两种查找方式:通过注解来查找类;查找是否继承了某个父类。
如图是他们的关系图:
imagepng
ResolverUtil类下面有几个内部类,Test是一个接口,里面有一个matches方法,其他几个内部类实现了Test接口。
matches方式是用来比较是否符合条件。
IsA类:
查找出所有继承了指定类的列表,包括父类。主要是通过isAssignableFrom方法来判断是否为父类。

AnnotatedWith类:
查找出所有加了某个注解的类。这个注解的类型必须是@Target({ElementType.TYPE}),不然会查找不到。
主要是通过isAnnotationPresent判断是否为注解。

ResolverUtil会把匹配的结果放到Set集合中,可以通过getClasses()方法获取Set集合。

可以通过setClassLoader来设置类加载器。如果没有指定,那么使用当前线程的类加载器。
获取类加载器的代码如下:

public ClassLoader getClassLoader() {
  return classloader == null ? Thread.currentThread().getContextClassLoader() : classloader;
}

ResolverUtil提供了findImplementations、findAnnotated、find方法,最核心的方法是find方法,其他两个方法都是调用了find方法。
findImplementations:查找父类关系
findAnnotated:查找注解关系
find方法详解:

public ResolverUtil<T> find(Test test, String packageName) {
  //1、根据传入的包名, 转换为路径,如com.cimu.test--->com/cimu/test
  String path = getPackagePath(packageName);
  try {
    //2、获取包路径下的所有文件
    List<String> children = VFS.getInstance().list(path);
    //3、循环文件集合
    for (String child : children) {
      if (child.endsWith(".class")) {
          //只查找以class为结尾的文件
        addIfMatching(test, child);
      }
    }
  } catch (IOException ioe) {
    log.error("Could not read package: " + packageName, ioe);
  }
  return this;
}

find方法是使用addIfMatching方法来进行比较,如果满足条件,那么会把结果放到set集合中。

protected void addIfMatching(Test test, String fqn) {
  try {
    //过滤掉最后的class字符串,再把/替换为.
    String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
    //获取类加载器
    ClassLoader loader = getClassLoader();
    if (log.isDebugEnabled()) {
      log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
    }
    //通过类加载去加载类文件
    Class<?> type = loader.loadClass(externalName);
    //判断是否符合条件
    if (test.matches(type)) {
      //如果符合条件,那么加入到set集合中。
      matches.add((Class<T>) type);
    }
  } catch (Throwable t) {
    log.warn("Could not examine class '" + fqn + "'" + " due to a " +
        t.getClass().getName() + " with message: " + t.getMessage());
  }
}

在mybatis中他的作用是对mapper映射的注册、TypeHandler的注册等。

Resources类

通过该类的某个方法可以获取以下资源Properties、Reader、InputStream、File、URL信息。然后在把这些配置信息传给SqlSessionFactory对象。他的作用就是用来加载一些配置文件。

VFS类

他有两个实现类:DefaultVFS和JBoss6VFS类。也可以自己实现。VFS内部通过单例来创建对象。
imagepng
在createVFS方法里面,会优先加入用户实现的VFS子类,再加入默认的实现类。for循环获取所有实现类,直到找到第一个isValid为true的VFS实现类。
imagepng
实现VFS的类需要实现isValid() 和 list(URL url, String path)两个方法

DefaultVFS类

isValid方法返回true;
imagepng
list(URL url, String path)的解析:
1、根据URL判断是否是jar,并验证jar是否可以打开,只有是jar文件才会返回,其他的类型都不返回。
2、如果jarUrl不为空,查找jar下的资源列表。
3、如果jarUrl为空且是jar,那么通过JarInputStream把jar里面的对象放到列表里。
4、如果jarUrl为空且不是jar,通过BufferedReader来一行一行的读,把读到的内存放到list中,如果不是包就清除list的内容。
5、如果url的协议是file,那么获取这个目录下的所有文件。
6、把类路径放到list中,并递归获取目录下的类。

isJar(URL url):判断URL是否是jar地址。

JBoss6VFS类

JBoss 6下面获取文件资源。
isValid方法会根据当前环境判断是否为true。
initialize方法:找到访问JBoss 6 VFS所需的所有类和方法。项目启动会检查是否存在org.jboss.vfs.VFS和org.jboss.vfs.VirtualFile类,验证org.jboss.vfs.VFS类里面是否有getChild方法。验证org.jboss.vfs.VirtualFile方法里是否存在getChildrenRecursively、getPathNameRelativeTo方法。如果不满足要求,那么isValid会设置为false。通过checkReturnType方法验证结果类型是否相等,不相等的时候isValid为设置为false。

list(URL url, String path)的解析:
1、根据url获取VirtualFile类。
2、获取所有的VirtualFile列表。
3、把类路径放到列表中。

也可以关注我的公众号:程序之声

关注公众号,领取更多资源

本文为博主原创文章,未经博主允许不得转载。

评论

发表评论