• ClassLoader 详解及用途


    ClassLoader主要对类的请求提供服务,当JVM需要某类时,它根据名称向ClassLoader要求这个类,然后由ClassLoader返回 这个类的class对象。

    1.1 几个相关概念

    ClassLoader负责载入系统的所有Resources(Class,文件,来自网络的字节流 等),通过ClassLoader从而将资源载入JVM。
    每个class都有一个reference,指向自己的ClassLoader。Class.getClassLoader() 。
    array的ClassLoader就是其元素的ClassLoader,若是基本数据类型,则这个array没有ClassLoader 。

    1.2 主要方法和工作过程

    从JDK1.2以后,ClassLoader做了改进,使用了委托模型:所有系统中的ClassLoader组成一棵树,ClassLoader在载入类库时先让Parent寻找,Parent找不到才自己找。  
    JVM 在运行时会产生三个ClassLoader,Bootstrap ClassLoader、Extension ClassLoader和 App ClassLoader。其中,

    • Bootstrap ClassLoader是用C++编写的,在Java中看不到它,是null。它用来加载核 心类库,就是在lib下的类库,
    • Extension ClassLoader加载lib/ext下的类库,
    • App ClassLoader加载 Classpath里的类库,

    三者的关系为:App ClassLoader的Parent是Extension ClassLoader,而 Extension ClassLoader的Parent为Bootstrap ClassLoader。

    加载一个类时,首先BootStrap进行寻 找,找不到再由Extension ClassLoader寻找,最后才是App ClassLoader。  
    将 ClassLoader设计成委托模型的一个重要原因是出于安全考虑,比如在Applet中,如果编写了一个java.lang.String类并具有破 坏性。

    假如不采用这种委托机制,就会将这个具有破坏性的String加载到了用户机器上,导致破坏用户安全。

    但采用这种委托机制则不会出现这种情况。因为 要加载java.lang.String类时,系统最终会由Bootstrap进行加载,这个具有破坏性的String永远没有机会加载。  
    委托模型还带来了一些问题,在某些情况下会产生混淆,如下是Tomcat的ClassLoader结构图:  

    Bootstrap 
                      | 
                    System 
                      | 
                    Common 
                    /     
                Catalina  Shared 
                          /     
                       Webapp1  Webapp2 ... 

    1.4 线 程中的ClassLoader

    每个运行中的线程都有一个成员contextClassLoader,用来在运行时动态地载入其它类。可以使用方法

    Thread.currentThread().setContextClassLoader(...);

    更改当前线程的 contextClassLoader,来改变其载入类的行为;也可以通过方法

    Thread.currentThread().getContextClassLoader()

    来获得当前线程的ClassLoader。  
    实际上,在Java应用中所有程序都运行在线程里,如果在程序中没有手工设置过ClassLoader,对于一般的java类如下两种方法获得的ClassLoader通常都是同一个 

    this.getClass.getClassLoader();  
    Thread.currentThread().getContextClassLoader();  

    方 法一得到的Classloader是静态的,表明类的载入者是谁;

    方法二得到的Classloader是动态的,谁执行(某个线程),就是那个执行者的 Classloader。对于单例模式的类,静态类等,载入一次后,这个实例会被很多程序(线程)调用,对于这些类,载入的Classloader和执行 线程的Classloader通常都不同。 

    1.5 Web应用中的ClassLoader

    回到上面的例子,在Tomcat 里,WebApp的ClassLoader的工作原理有点不同,它先试图自己载入类(在ContextPath/WEB-INF/...中载入类),如果 无法载入,再请求父ClassLoader完成。  
    由此可得:  
    对于WEB APP线程,它的contextClassLoader是WebAppClassLoader  
    对于Tomcat Server线程,它的contextClassLoader是CatalinaClassLoader  

    1.6 获得ClassLoader的几种方法

    可以通过如下3种方法得到ClassLoader  

    this.getClass.getClassLoader(); // 使用当前类的ClassLoader  
    Thread.currentThread().getContextClassLoader(); // 使用当前线程的ClassLoader  
    ClassLoader.getSystemClassLoader(); // 使用系统ClassLoader,即系统的入口点所使用的ClassLoader。
    (注意,system ClassLoader与根 ClassLoader并不一样。JVM下system ClassLoader通常为App ClassLoader)

    2.0 资源载入 

    所有资源都通过ClassLoader载入到JVM里,那么在载入资源时当然可以使用ClassLoader,只是对于不同的资源还可以使用一些别的方式载 入

    例如对于类可以直接new,对于文件可以直接做IO等。 

    2.1 载入类的几种方法假设有类A和类B,A在方法amethod里需要实例化B,可能的 方法有3种。对于载入类的情况,用户需要知道B类的完整名字(包括包名,例如"com.rain.B")  
    1. 使用Class静态方法 Class.forName  

        Class cls = Class.forName("com.rain.B"); 
        B b = (B)cls.newInstance(); 

    2. 使用ClassLoader  

        ClassLoader cl; // 如何获得ClassLoader参考1.6 
        Class cls = cl.loadClass("com.rain.B"); // 使用第一步得到的ClassLoader来载入B 
        B b = (B)cls.newInstance(); // 有B的类得到一个B的实例 

    3. 直接new  

        B b = new B(); 

    2.2 文 件载入(例如配置文件等)

    假设在com.rain.A类里想读取文件夹 /com/rain/config 里的文件sys.properties,读取 文件可以通过绝对路径或相对路径,

    绝对路径很简单,在Windows下以盘号开始,在Unix下以"/"开始  
    对于相对路径,其相对值是相对于ClassLoader的因为ClassLoader是一棵树,所以这个相对路径和ClassLoader树上的任何一个ClassLoader相对比较后可以找到文件,那么文件就可以找到,当然,读取文件也使用委托模型 

    1. 直接IO  

    File f = new File("C:/test/com/rain/config/sys.properties"); // 使用绝对路径 
    //File f = new File("com/rain/config/sys.properties"); // 使用相对路径 
    InputStream is = new FileInputStream(f); 

    如果是配置文件,可以通过java.util.Properties.load(is)将内容读到Properties里,Properties默认认为is的编码是ISO-8859-1,如果配置文件是非英文的,可能出现乱码问题。  

    2. 使用ClassLoader 

     /** 
     * 因为有3种方法得到ClassLoader,对应有如下3种方法读取文件 
     * 使用的路径是相对于这个ClassLoader的那个点的相对路径,此处只能使用相对路径 
     */ 

    InputStream is = null; 
    is = this.getClass().getClassLoader().getResourceAsStream( 
           "com/rain/config/sys.properties"); //方法1 
    is = Thread.currentThread().getContextClassLoader().getResourceAsStream( 
           "com/rain/config/sys.properties"); //方法2 
    is = ClassLoader.getSystemResourceAsStream("com/rain/config/sys.properties"); //方法3 

    如果是配置文件,可以通过java.util.Properties.load(is)将内容读到Properties里,这里要注意编码问题。  

    3. 使用ResourceBundle

    ResourceBundle bundle = ResourceBundle.getBoundle("com.rain.config.sys"); 

    这种用法通常用来载入用户的配置文件,关于ResourceBunlde更详细的用法请参考其他文档  
    总结:有如下3种途径来载入文件  

        1. 绝对路径 ---> IO 
        2. 相对路径 ---> IO 
                    ---> ClassLoader 
        3. 资源文件 ---> ResourceBundle 

  • 相关阅读:
    Quartz.NET-2.3.3 各种 数据库配置 类别大全
    C#获取当前路径的七种方法 【转载】
    BCB 如何拦截TAB键消息
    用union 和 struct 位域操作
    表值函数
    C#中 委托和事件的关系
    关于C++ Builder Codegurad 问题的排查。
    存储过程中使用事务的“正规”写法
    C++ 中对vector<T*> 数组的查找和排序
    BCB 中 Application->CreateForm 和 New 的一个区别
  • 原文地址:https://www.cnblogs.com/alsf/p/9277932.html
Copyright © 2020-2023  润新知