【Mybatis】【基础设施】【三】Mybatis源码解析-VFS虚拟文件系统

1  前言

这节我们介绍Mybatis又一个出现的基础设施VFS,他是干啥的呢,就是加载指定目录下的文件的。

前置知识:java.net.URL这个类知道么,不要看名字把它觉得就是我们浏览器里的URL,它不是奥。它叫统一资源定位符有:http、https、file和jar。

2  源码分析

看源码前我们简单看一下类图:

VFS是个抽象类,下边有两个子类DefaultVFS、JBOss6VFS,有点模板模式的意思奥。 两个抽象方法是:

/**
   * Return true if the {@link VFS} implementation is valid for the current environment.
   * 当前的VFS是否可用
   * @return true, if is valid
   */
  public abstract boolean isValid();

  /**
   * Recursively list the full resource path of all the resources that are children of the
   * resource identified by a URL.
   * 列出某个URL下的资源 forPath下的
   * @param url The URL that identifies the resource to list.
   * @param forPath The path to the resource that is identified by the URL. Generally, this is the
   *            value passed to {@link #getResources(String)} to get the resource URL.
   * @return A list containing the names of the child resources.
   * @throws IOException If I/O errors occur
   */
  protected abstract List<String> list(URL url, String forPath) throws IOException;

好了,那我们具体看下VFS的源码:

public abstract class VFS {
  private static final Log log = LogFactory.getLog(VFS.class);

  // 两个子类
  public static final Class<?>[] IMPLEMENTATIONS = { JBoss6VFS.class, DefaultVFS.class };

  // 用户自定义实现
  public static final List<Class<? extends VFS>> USER_IMPLEMENTATIONS = new ArrayList<>();

  /** Singleton instance holder. */
  // 单例的对象实例化拥有者,也就是要想获得VFS实例,就直接VFSHolder.INSTANCE即可。
  private static class VFSHolder {
    static final VFS INSTANCE = createVFS();

    @SuppressWarnings("unchecked")
    // 实例化VFS
    static VFS createVFS() {
      //合并用户自定义实现和默认的两个实现
      List<Class<? extends VFS>> impls = new ArrayList<>();
      impls.addAll(USER_IMPLEMENTATIONS);
      impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS));

      // Try each implementation class until a valid one is found
      VFS vfs = null;
      // 可以看到找到一个实现的子类即可
      for (int i = 0; vfs == null || !vfs.isValid(); i++) {
        Class<? extends VFS> impl = impls.get(i);
        try {
          // 实例化
          vfs = impl.getDeclaredConstructor().newInstance();
          if (!vfs.isValid() && log.isDebugEnabled()) {
            log.debug("VFS implementation " + impl.getName()
                + " is not valid in this environment.");
          }
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
          log.error("Failed to instantiate " + impl, e);
          return null;
        }
      }

      if (log.isDebugEnabled()) {
        log.debug("Using VFS adapter " + vfs.getClass().getName());
      }

      return vfs;
    }
  }

  /**
   * Get the singleton {@link VFS} instance. If no {@link VFS} implementation can be found for the current environment,
   * then this method returns null.
   * 获取VFS实例
   * @return single instance of VFS
   */
  public static VFS getInstance() {
    return VFSHolder.INSTANCE;
  }

  // 添加自定义实现
  public static void addImplClass(Class<? extends VFS> clazz) {
    if (clazz != null) {
      USER_IMPLEMENTATIONS.add(clazz);
    }
  }

  // 加载指定全类名的类
  protected static Class<?> getClass(String className) {
    try {
      return Thread.currentThread().getContextClassLoader().loadClass(className);
      // return ReflectUtil.findClass(className);
    } catch (ClassNotFoundException e) {
      if (log.isDebugEnabled()) {
        log.debug("Class not found: " + className);
      }
      return null;
    }
  }

  // 获取某个类的方法
  protected static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
    if (clazz == null) {
      return null;
    }
    try {
      return clazz.getMethod(methodName, parameterTypes);
    } catch (SecurityException e) {
      log.error("Security exception looking for method " + clazz.getName() + "." + methodName + ".  Cause: " + e);
      return null;
    } catch (NoSuchMethodException e) {
      log.error("Method not found " + clazz.getName() + "." + methodName + "." + methodName + ".  Cause: " + e);
      return null;
    }
  }
// 执行某个类的方法
  protected static <T> T invoke(Method method, Object object, Object... parameters)
      throws IOException, RuntimeException {
    try {
      return (T) method.invoke(object, parameters);
    } catch (IllegalArgumentException | IllegalAccessException e) {
      throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
      if (e.getTargetException() instanceof IOException) {
        throw (IOException) e.getTargetException();
      } else {
        throw new RuntimeException(e);
      }
    }
  }

  // 获取某个路径下的资源
  protected static List<URL> getResources(String path) throws IOException {
    return Collections.list(Thread.currentThread().getContextClassLoader().getResources(path));
  }

  public abstract boolean isValid();
  protected abstract List<String> list(URL url, String forPath) throws IOException;

  // 获取路径下的文件
  public List<String> list(String path) throws IOException {
    List<String> names = new ArrayList<>();
    for (URL url : getResources(path)) {
      names.addAll(list(url, path));
    }
    return names;
  }

  public static void main(String[] args) throws IOException {
    VFS instance = VFS.getInstance();
    List<String> list = instance.list("org/apache/ibatis/type");
    System.out.println(list);
  }
}

总结一下的大概的方法有:

  • 有一个静态内部类VFSHolder,用于初始化VFS对象,并持有其单例
  • addImplClass 添加自定义实现
  • getClass 根据全类名加载某个类
  • getInstance 获取VFS实例
  • getMethod 获取某个类的某个方法
  • getResources 获取某个路径下的资源列表 通过类加载器
  • invoke 执行某个方法
  • isValid 当前VFS是否可用
  • list(path) 获取某个路径下的所有文件名
  • list(URL url, String forPath) 获取某个路径下指定的子路径下的所有文件名

3  示例

4  小结

好了,关于VFS的介绍就到这里了,知道它是用来加载某个路径下的资源即可,有理解不对的地方欢迎指正哈。

posted @ 2023-02-26 12:16  酷酷-  阅读(77)  评论(0编辑  收藏  举报