第二章 资源加载 ClassLoader

简书 许乐
转载请注明原创出处,谢谢!

一、ClassLoaderWrapper 类

  在MyBatis的IO包中封装了ClassLoader以及读取资源文件相关的API。 ClassLoaderWrapper是一个ClassLoader的包装器,其中包含了多个ClassLoader对象。使用ClassLoaderWrapper就如同使用一个ClassLoader对象,ClassLoaderWrapper 会按照指定的顺序依次检测其中封装的ClassLoader对象,并从中选取第一个可用的ClassLoader完成相关功能。

  ClassLoaderWrapper中定义了两个字段,分别记录了系统指定的默认加载器(defaultClassLoader)和系统加载器(systemClassLoader)

ClassLoader defaultClassLoader;//应用指定的默认类加载器
ClassLoader systemClassLoader;// 系统类加载器

特别注意】defaultClassLoader 和 systemClassLoader的修饰符为包内可见,它们的初始化可以在其它的类中完成。defaultClassLoader会在同一包下的Resources类中初始化。

ClassLoaderWrapper的主要功能分为三类:

  • getResourceAsURL()
  • getResourceAsStream()
  • classForName()
    这三个方法逻辑很相似,这里以classForName()为例进行介绍
public Class classForName(String name) throws ClassNotFoundException { 
  return classForName(name, getClassLoaders(null)); 
}
public Class classForName(String name, ClassLoader classLoader) throws ClassNotFoundException { 
  return classForName(name, getClassLoaders(classLoader)); 
}

真正被调用的方法如下:

private Class classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException { 
  for (ClassLoader cl : classLoader) {//遍历所有加载器 
    if (null != cl) { 
       try { 
         Class c = Class.forName(name, true, cl);//使用Class.forName加载一个类  
         if (null != c) {//直到加载到Class返回  
              return c; 
         } 
       } catch (ClassNotFoundException ignore) { 
        //ignore
       } 
    } 
  } 
 //所有类加载器都加载不到,抛出ClassNotFoundException  
 throw  new ClassNotFoundException("Cannot find class: " + name);
}

getClassLoaders(classLoader):

// 返回ClassLoader[] 数组,该数组指明了类加载器的使用顺序  
private ClassLoader[] getClassLoaders(ClassLoader classLoader) { 
    ClassLoader[] classLoaders=new ClassLoader[]{ 
      classLoader,//由参数指定的默认类加载器 
      defaultClassLoader,//系统指定的默认类加载器 
      Thread.currentThread().getContextClassLoader(),//当前线程绑定的类加载器 
      getClass().getClassLoader(),//加载当前类所使用的类加载器 
      systemClassLoader};//系统类加载器  
   return classLoaders; 
}

二、Resources 类

  Resources类是提供了多个静态方法的工具类,其中封装了一个ClassLoaderWrapper类型的静态字段,Resources 提供的静态方法都是通过调用ClassLoaderWrapper的方法实现,代码比较简单,仅贴出核心代码。

public  class Resources { 
  private  static ClassLoaderWrapper classLoaderWrapper =
                 new ClassLoaderWrapper(); 
  ...... 
 }

三、思考与总结:

1. 为什么使用ClassLoaderWrapper而不是JDK提供的ClassLoader?

  框架中加载资源的场景非常普遍,有类加载,文件加载(如配置文件,db.properties)等,这些操作普遍需要判断文件、路径、加载器是否为空,还要抛出异常,将这些判断是否为空的逻辑与异常的处理封装起来,简化上层调用逻辑,使得上层代码的结构更清晰。其实这种包装在框架中会普遍存在,因为JDK API只是提供了功能的实现,并没有考虑业务场景的多样性。
  感觉就是几乎每一个框架在真正功能实现层(基础层),和外部系统使用层(接口层),之间会多出来一个层(核心层),这一层扮演的角色就是委托,如果有一些逻辑处理,就进行逻辑处理,再调用基础层;没有逻辑处理的就直接调用基础层,而不是直接在对外部系统使用的接口层,来直接耦合基础层的类。这样虽然有些麻烦,但是感觉提高了扩展性,基础层我只提供不变的逻辑处理,而多出来的核心层,可以预处理一些接口层传递过来的数据,核心层处理变化的逻辑。这样就起到复用基础层的提供的功能。
  这里也引发了对于SOA服务的思考,或许我们也可以这样去设定,对外暴露的接口,就是这里的核心层,而抽离出来一些不变的逻辑放在基础层,以后有了新的变动,只需要在核心层加接口,实现一些逻辑,复用基础层提供的服务;而不是只有单独一层,每次新变动都需要写新逻辑以及重复copy不变的逻辑。

2. ClassLoaderWrapper 怎么打破了双亲委托模型?

遍历classLoader[]数组,如果加载到所需要的类则返回,类加载器的使用顺序是从数组的第一个元素到最后一个元素。

3. ClassLoaderWrapper的* getResourceAsURL()* getResourceAsStream() 实现的功能是什么?
    原文作者:我是大牛膜拜吧小白
    原文地址: https://www.jianshu.com/p/364f8528405f
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞