Mybatis源码阅读----I/O模块

Mybaits资源加载模块所在位置:
在这里插入图片描述
在java中类加载器(classloader)主要负责加载文件系统、网络或者其他来源的类文件,通过双亲委派模式进行加载,默认使用三种类加载器进行加载相关的类,分别为BootStrapClassloader,Extension Classloader以及System Classloader(Applicaiton Classloader),三种加载器加载不同位置的类,BootStrapClassloader加载JDK自带的rt.jar包中的类文件,而Extension Classlader负责加载java的扩展类库。System Classloader主要负责加载classpath环境变量下的类文件,通常我们自定义的类都由该加载器进行加载。双亲委派模型如下:
在这里插入图片描述
当一个类被加载时,一次向上委托,直到到BootStrap Classloader,当BootStrap Classloader已加载该类时,其他加载器将不能加载,当改加载器不能加载该类时,将由Extension Classloader进行加载,若其不能加载,继续向下交给Application ClassLoader进行加载。

1、在Mybatis的IO模块中ClassloaderWrapper对ClassLoader进行了封装

 /**
   *  默认classloader 对象,通过ClassLoaderWrapper.defaultClassLoader = xxx方式进行设置
   */
  ClassLoader defaultClassLoader;
  /**
   *  系统classloader
   */
  ClassLoader systemClassLoader;

  ClassLoaderWrapper() {
    try {
      systemClassLoader = ClassLoader.getSystemClassLoader();
    } catch (SecurityException ignored) {
      // AccessControlException on Google App Engine
    }
  }

ClassLoadWrapper主要通过三种方式进行加载相关资源。getResourceAsURL()、getResourceAsStream()和classForName()方法。在该类中对三种方法进行了重载,最终都调用了其带有String,Classloader[]参数的重载方法。

public URL getResourceAsURL(String resource, ClassLoader classLoader) {
    return getResourceAsURL(resource, getClassLoaders(classLoader));
  }

对于getClassLoaders(classLoader)方法主要获取classloader[]并确定类加载器的顺序。

// 获取classloader数组
  ClassLoader[] getClassLoaders(ClassLoader classLoader) {
    return new ClassLoader[]{
        classLoader,   // 指定类加载器
        defaultClassLoader,  // 默认加载器
        Thread.currentThread().getContextClassLoader(), // 当前线程绑定加载器
        getClass().getClassLoader(), // 加载器当前类所使用的加载器
        systemClassLoader}; // 系统类加载器
  }
 /**
   * Get a resource as a URL using the current class path
   *
   * @param resource    - the resource to locate
   * @param classLoader - the class loaders to examine
   * @return the resource or null
   * 获得指定资源的 URL
   */
  URL getResourceAsURL(String resource, ClassLoader[] classLoader) {

    URL url;

    // 遍历classloader数组,当其中有个加载器加载到资源时立即返回
    for (ClassLoader cl : classLoader) {

      if (null != cl) {

        // 获得URL 不带 /
        // look for the resource as passed in...
        url = cl.getResource(resource);

        // ...but some class loaders want this leading "/", so we'll add it
        // and try again if we didn't find the resource
        if (null == url) {
          // 获取URL 带 /
          url = cl.getResource("/" + resource);
        }

        // "It's always in the last place I look for it!"
        // ... because only an idiot would keep looking for it after finding it, so stop looking already.
        if (null != url) {
          // 成功获得返回
          return url;
        }

      }

    }

    // didn't find it anywhere.
    return null;

  }

2、Resources类主要对ClassloaderWrapper进行了封装,其中的静态方法都调用了ClassloaderWrapper中的方法,相当于一个工具类。

 public static URL getResourceURL(ClassLoader loader, String resource) throws IOException {
    URL url = classLoaderWrapper.getResourceAsURL(resource, loader);
    if (url == null) {
      throw new IOException("Could not find resource " + resource);
    }
    return url;
  }

 public static Reader getResourceAsReader(ClassLoader loader, String resource) throws IOException {
    Reader reader;
    if (charset == null) {
      reader = new InputStreamReader(*getResourceAsStream(loader, resource*));
    } else {
      reader = new InputStreamReader(getResourceAsStream(loader, resource), charset);
    }
    return reader;
  }

其中许多方法只是进行了进一步封装,就不一一说明了。

3、ResolverUtil主要根据指定条件查找指定包下的类,条件通过类的方式体现。在其内部定义了Test接口,以该接口的实现类作为条件。

public interface Test {
    boolean matches(Class<?> type);
  }

在这里插入图片描述
Test主要有两个实现类,IsA,与AnnotatedWith

 public static class IsA implements Test {

    /**
     *  用于检测类是否继承了指定的parent类
     */
    private Class<?> parent;

    public IsA(Class<?> parentType) {
      this.parent = parentType;
    }

    @Override
    public boolean matches(Class<?> type) {

      return type != null && parent.isAssignableFrom(type);
    }

    @Override
    public String toString() {
      return "is assignable to " + parent.getSimpleName();
    }
  }

/**
   *  判断是否有指定的注解
   */
  public static class AnnotatedWith implements Test {

    // 注解
    private Class<? extends Annotation> annotation;

    public AnnotatedWith(Class<? extends Annotation> annotation) {

      this.annotation = annotation;
    }

    @Override
    public boolean matches(Class<?> type) {
      return type != null && type.isAnnotationPresent(annotation);
    }

    @Override
    public String toString() {
      return "annotated with @" + annotation.getSimpleName();
    }
  }

ResolverUtil内部定义的属性:

/**
   *  存储匹配的类
   */
  private Set<Class<? extends T>> matches = new HashSet<>();
  private ClassLoader classloader;
  public Set<Class<? extends T>> getClasses() {
    return matches;
  }
  public ClassLoader getClassLoader() {
  // 默认使用当前线程绑定的 类加载器
    return classloader == null ? Thread.currentThread().getContextClassLoader() : classloader;
  }
  public void setClassLoader(ClassLoader classloader) {
    this.classloader = classloader;
  }

findImplementations(Class<?> parent, String… packageNames)方法,创建IsA作为检测对象。

 if (packageNames == null) {
      return this;
    }

    Test test = new IsA(parent);
    for (String pkg : packageNames) {
      find(test, pkg);
    }

    return this;

findAnnotated()方法,创建AnnotatedWith作为检测对象

public ResolverUtil<T> findAnnotated(Class<? extends Annotation> annotation, String... packageNames) {
    if (packageNames == null) {
      return this;
    }

    Test test = new AnnotatedWith(annotation);
    for (String pkg : packageNames) {
      find(test, pkg);
    }

    return this;
  }

find(test, pkg):方法如下

public ResolverUtil<T> find(Test test, String packageName) {

    // 根据包名获取其对应的路径
    String path = getPackagePath(packageName);

    try {
      // 通过VFS查找该包下的所有资源
      List<String> children = VFS.getInstance().list(path);
      for (String child : children) {
        if (child.endsWith(".class")) {

          // 匹配则进行添加
          addIfMatching(test, child); // 检测该类是否符合test条件
        }
      }
    } catch (IOException ioe) {
      log.error("Could not read package: " + packageName, ioe);
    }

    return this;
  }

将包名转化为路径的方法:

 protected String getPackagePath(String packageName) {
    // 获取包路径
    return packageName == null ? null : packageName.replace('.', '/');
  }
protected void addIfMatching(Test test, String fqn) {
    try {
      // 获得全类名
      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)) {
        // 匹配则进行添加
        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());
    }
  }
}

4、VFS虚拟文件系统,用来查找指定路径下的资源

  // 记录mybatis提供的VFS的实现类
  public static final Class<?>[] IMPLEMENTATIONS = { JBoss6VFS.class, DefaultVFS.class };

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

对于VFS的构建则是通过内部静态类的方式进行构造的:

/** Singleton instance holder. */
  private static class VFSHolder {
    static final VFS INSTANCE = createVFS();

    @SuppressWarnings("unchecked")
    static VFS createVFS() {
      // Try the user implementations first, then the built-ins

      List<Class<? extends VFS>> impls = new ArrayList<>();
      // 先使用用户自定义的vfs实现类,
      impls.addAll(USER_IMPLEMENTATIONS);
      // 再使用mybatis提供的实现类
      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()) {
            if (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;
    }
  }`在这里插入代码片`

  /**
 
   * 单例模式,通过内部静态类方式实现
   */
  public static VFS getInstance() {
    return VFSHolder.INSTANCE;
  }

该类中提供了一个极为重要的方法 list(URL url, String forPath),用于查找指定资源的名称列表,在其子类中实现,以DefaultVFS为例:

 @Override
  public List<String> list(URL url, String path) throws IOException {
    InputStream is = null;
    try {
      List<String> resources = new ArrayList<>();

      // First, try to find the URL of a JAR file containing the requested resource. If a JAR
      // file is found, then we'll list child resources by reading the JAR.
      // 如果url指向的资源在一个jar包中,则获取这个jar包的url,负责返回null
      URL jarUrl = findJarForResource(url);
      if (jarUrl != null) {
        is = jarUrl.openStream();
        if (log.isDebugEnabled()) {
          log.debug("Listing " + url);
        }
        // 遍历jar包中的资源并返回以path开头的资源列表
        resources = listResources(new JarInputStream(is), path);
      }
      else {
        List<String> children = new ArrayList<>();
        try {
          //  // 判断为 JAR URL
          if (isJar(url)) {
            // Some versions of JBoss VFS might give a JAR stream even if the resource
            // referenced by the URL isn't actually a JAR
            is = url.openStream();
            try (JarInputStream jarInput = new JarInputStream(is)) {
              if (log.isDebugEnabled()) {
                log.debug("Listing " + url);
              }
              for (JarEntry entry; (entry = jarInput.getNextJarEntry()) != null; ) {
                if (log.isDebugEnabled()) {
                  log.debug("Jar entry: " + entry.getName());
                }
                children.add(entry.getName());
              }
            }
          }
          else {
         
            // 获取路径下所有资源
            is = url.openStream();
            List<String> lines = new ArrayList<>();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
              for (String line; (line = reader.readLine()) != null;) {
                if (log.isDebugEnabled()) {
                  log.debug("Reader entry: " + line);
                }
                lines.add(line);
                if (getResources(path + "/" + line).isEmpty()) {
                  lines.clear();
                  break;
                }
              }
            }
            if (!lines.isEmpty()) {
              if (log.isDebugEnabled()) {
                log.debug("Listing " + url);
              }
              children.addAll(lines);
            }
          }
        } catch (FileNotFoundException e) {
          /*
           * For file URLs the openStream() call might fail, depending on the servlet
           * container, because directories can't be opened for reading. If that happens,
           * then list the directory directly instead.
           */
          if ("file".equals(url.getProtocol())) {
            File file = new File(url.getFile());
            if (log.isDebugEnabled()) {
                log.debug("Listing directory " + file.getAbsolutePath());
            }
            if (file.isDirectory()) {
              if (log.isDebugEnabled()) {
                  log.debug("Listing " + url);
              }
              children = Arrays.asList(file.list());
            }
          }
          else {
            // No idea where the exception came from so rethrow it
            throw e;
          }
        }

        // The URL prefix to use when recursively listing child resources
        String prefix = url.toExternalForm();
        if (!prefix.endsWith("/")) {
          prefix = prefix + "/";
        }

        // Iterate over immediate children, adding files and recursing into directories
        for (String child : children) {
          String resourcePath = path + "/" + child;
          resources.add(resourcePath);
          URL childUrl = new URL(prefix + child);
          resources.addAll(list(childUrl, resourcePath));
        }
      }

      return resources;
    } finally {
      if (is != null) {
        try {
          is.close();
        } catch (Exception e) {
          // Ignore
        }
      }
    }
  }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值