转载

Mybatis IO模块分析

Mybatis IO模块分析

包括了这几个类:

ClassLoaderWrapper
DefaultVFS
ExternalResources
JBoss6VFS
ResolverUtil
Resources
VFS

ClassLoaderWrapper

作用

使用5种类加载器,one by one查找资源,只要其中一个命中,就OK

  1. 自定义类加载器 > 默认类加载器 > 当前线程Id上下文类加载器 > 当前类的加载器 > 系统类加载器
  2. 封装加载类资源有三种,一种返回URL、一种是返回InputStream,最后一种根据类名返回Class
  3. 在实例化该对象的时候默认会赋值系统类加载器

类图

Mybatis IO模块分析

Thinking

  • 为什么需要用5种不同的类加载器

尽最大可能找到指定的资源

  • 每种类加载器的异同

Resources

作用

简化资源文件的获取。 主要是通过ClassLoaderWrapper(封装ClassLoader读取文件)进行文件加载

  • 对于简单的只读文本数据,加载为 Reader
  • 对于简单的只读二进制或文本数据,加载为 InputStream
  • 对于可读写的二进制或文本文件,加载为 File
  • 对于只读的配置属性文件,加载为 Properties
  • 对于只读的通用资源,加载为 URL

类图

Mybatis IO模块分析

Thinking

  • 有了 ClassLoaderWrapper ,为啥又定义 Resources

ClassLoderWrapper 作为 Resouces 类的一个成员变量,将前者提供的方法都包装成静态方法,方便调用。 并且扩展了其他资源读取的方法,eg: getResourceAsProperties() 。看着有点像是装饰模式的样子~~

ResolverUtil

作用

ResolverUtil用于查找在类路径可用并满足任意条件的类。最常见的两种情况是一个类继承或实现了另一个类, 或者此类被指定的注解标记了。并且,通过使用Test类,可以满足任意条件的搜索。即找一个package下满足条件的所有类

类图

Mybatis IO模块分析

主要方法

/**
   * 主要的方法,找一个package下满足条件的所有类,被TypeHandlerRegistry, MapperRegistry, TypeAliasRegistry调用
   * 1. 浏览提供的包及其子包的类
   * 2. 每个发现的类都会被提供给Test类,如果Test类的方法返回true就保留
   * 3. 累的类可以通过getClasses()方法获得
   */
  public ResolverUtil<T> find(Test test, String packageName) {
    String path = getPackagePath(packageName);

    try {
      // 通过VFS来深入jar包里面去找一个class
      List<String> children = VFS.getInstance().list(path);
      for (String child : children) {
        if (child.endsWith(".class")) {
          addIfMatching(test, child);
        }
      }
    } catch (IOException ioe) {
      log.error("Could not read package: " + packageName, ioe);
    }

    return this;
  }
复制代码

UT

在当前包中查找所有被 CacheNamespace 注解修饰的类

@Test
  void findAnnotated() {
    ResolverUtil<Object> resolverUtil = new ResolverUtil<>();
    // 在当前包中查找所有被CacheNamespace注解修饰的类 
    resolverUtil.findAnnotated(CacheNamespace.class, this.getClass().getPackage().getName());
    Set<Class<?>> classSets = resolverUtil.getClasses();
    //org.apache.ibatis.io.ResolverUtilTest.TestMapper
    assertEquals(classSets.size(), 1);
    classSets.forEach(c -> assertNotNull(c.getAnnotation(CacheNamespace.class)));
  }
复制代码

Thinking

很巧妙的应用了 设计模式原则的开发-封闭原则 。在这个工具类中,定义了一个内部接口Test和两个Test的内部实现类。 使得我们可以只实现Test接口,而不用修改 ResolverUtil 的内部方法,即可直接使用 ResolverUtilfind 方法

public class CustomTest implements Test {
    @Override
    public boolean matches(Class<?> type) {
        if (type == CustomTest.class) {
            return true;
        }
        return false;
    }
}
复制代码

扩展阅读

  • MyBatis源码分析系列 ResolverUtl
  • Mybatis ResolverUtil的设计概念
原文  https://juejin.im/post/5f007a43e51d4534c14da6c2
正文到此结束
Loading...