MyBatis--资源加载模块

ClassLoaderWrapper

在IO包中提供的ClassLoaderWrapper是一个ClassLoader包装器,其中包含多个ClassLoader对象。

ClassLoaderWrapper的功能

  • getResourceAsURL

  • getResourceAsStream

  • classForName

    public class ClassLoaderWrapper {

    ClassLoader defaultClassLoader;//默认加载器
    ClassLoader systemClassLoader;
    
    ClassLoaderWrapper() {
      try {//初始化systemClassLoader
        systemClassLoader = ClassLoader.getSystemClassLoader();
      } catch (SecurityException ignored) {
        // AccessControlException on Google App Engine   
      }
    }
    
     public URL getResourceAsURL(String resource, ClassLoader classLoader) {
      return getResourceAsURL(resource, getClassLoaders(classLoader));
    }
    //返回ClassLoader[]数组,该数组指明类加载器的使用顺序
     ClassLoader[] getClassLoaders(ClassLoader classLoader) {
      return new ClassLoader[]{
          classLoader,//参数指定的类加载器
          defaultClassLoader,//系统指定的默认类加载器
          Thread.currentThread().getContextClassLoader(),//当前线程绑定的加载器
          getClass().getClassLoader(),//当前类使用的加载器
          systemClassLoader};//systemClassLoader
    }
    
    URL getResourceAsURL(String resource, ClassLoader[] classLoader) {
    
      URL url;
    
      for (ClassLoader cl : classLoader) {//遍历classLoader数组
    
        if (null != cl) {
          // 调用classLoader.getResource()查找指定的资源
          url = cl.getResource(resource);
          if (null == url) {//尝试以"/"开头,再次查找
            url = cl.getResource("/" + resource);
          }
          if (null != url) {//查找到指定的资源
            return url;
          }
    
        }
    
      }
      return null;
    
    }
    复制代码

ResolverUtil

他可以根据指定的条件查找指定包下的类,条件由Test接口表示。

private ClassLoader classloader;//记录当前使用的类加载器(默认绑定上下文的classloader),可通过setClassLoader()设置
//Test接口的两个实现
//IsA用于检测类是否继承指定类或者接口
  public static class IsA implements Test {
    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();
    }
  }
//AnnotatedWith检测类是否添加指定的注解
  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();
    }
  }



  public ResolverUtil<T> find(Test test, String packageName) {
    String path = getPackagePath(packageName);//根据包名得到路径

    try {
      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;
  }
 protected void addIfMatching(Test test, String fqn) {
    try {//fqn是类完全限定名
      String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
      ClassLoader loader = getClassLoader();
    //日志
      Class<?> type = loader.loadClass(externalName);//加载类
      if (test.matches(type)) {//检测是否满足
        matches.add((Class<T>) type);//存到matches
      }
    } catch (Throwable t) {
    
  }
复制代码

VFS

它用来查找指定路径下的资源。它是一个抽象类,有两个实现类(DefaultVFS,JBoss6VFS)。

public abstract class VFS {
//记录MyBatis提供的实现类
    public static final Class<?>[] IMPLEMENTATIONS = { JBoss6VFS.class, DefaultVFS.class };
  //用户自定义的实现类。VFS.addImplClass方法添加
    public static final List<Class<? extends VFS>> USER_IMPLEMENTATIONS = new ArrayList<>();
  //取VFS实例,单例
   public static VFS getInstance() {
    return VFSHolder.INSTANCE;
  }
  private static class VFSHolder {
    static final VFS INSTANCE = createVFS();
    static VFS createVFS() {
      // 优先使用用户自定义的VFS实现
      List<Class<? extends VFS>> impls = new ArrayList<>();
      impls.addAll(USER_IMPLEMENTATIONS);
      impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS));

      // 遍历VFS的实现类
      VFS vfs = null;
      for (int i = 0; vfs == null || !vfs.isValid(); i++) {
        Class<? extends VFS> impl = impls.get(i);
        try {
          vfs = impl.newInstance();//实例化
          if (vfs == null || !vfs.isValid()) {//isValid抽象方法
            //log
          }
        } catch (InstantiationException e) {
          return null;
        } 
      }
      return vfs;
    }
  }
  //这个方法是VFS的抽象方法。在ResolverUtil.find中调用  
  //todo没仔细研究这个函数
  public List<String> list(URL url, String path) throws IOException {
    InputStream is = null;
    try {
      List<String> resources = new ArrayList<>();
			//如果url指向的资源在一个jar包中,则获取该Jar包对应的URL,否则返回null
      URL jarUrl = findJarForResource(url);
      if (jarUrl != null) {
        is = jarUrl.openStream();
        //遍历Jar,返回以path开头的资源列表
        resources = listResources(new JarInputStream(is), path);
      }
      else {
        List<String> children = new ArrayList<>();
        try {
          if (isJar(url)) {
            is = url.openStream();
            try (JarInputStream jarInput = new JarInputStream(is)) {
              for (JarEntry entry; (entry = jarInput.getNextJarEntry()) != null; ) {
                children.add(entry.getName());
              }
            }
          }
          else {
            is = url.openStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            List<String> lines = new ArrayList<>();
            for (String line; (line = reader.readLine()) != null;) {
              lines.add(line);
              if (getResources(path + "/" + line).isEmpty()) {
                lines.clear();
                break;
              }
            }
            if (!lines.isEmpty()) {
              children.addAll(lines);
            }
          }
        } catch (FileNotFoundException e) {
          if ("file".equals(url.getProtocol())) {
            File file = new File(url.getFile());
            if (file.isDirectory()) {
              children = Arrays.asList(file.list());
            }
          }
          else {
            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
        }
      }
    }
  }
}

protected List<String> listResources(JarInputStream jar, String path) throws IOException {
    //开始和结束位置添加"/"
    if (!path.startsWith("/")) {
      path = "/" + path;
    }
    if (!path.endsWith("/")) {
      path = path + "/";
    }

    // 遍历jar
    List<String> resources = new ArrayList<>();
    for (JarEntry entry; (entry = jar.getNextJarEntry()) != null;) {
      if (!entry.isDirectory()) {
        String name = entry.getName();
        if (!name.startsWith("/")) {
          name = "/" + name;
        }

        // 检测name是否以path开头
        if (name.startsWith(path)) {
          resources.add(name.substring(1));//记录资源名称
        }
      }
    }
    return resources;
  }
复制代码

转载于:https://juejin.im/post/5bdaa76ce51d453eee31d1d0

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值