• Java中用ClassLoader载入各种资源(类、文件、web资源)的方法


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

    ClassLoader负责载入系统的所有资源(Class,文件,图片,来自网络的字节流等),通过ClassLoader从而将资源载入JVM 中。每个class都有一个引用,指向自己的ClassLoader。

    1. 获得ClassLoader的几种方法

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

    [java] view plain copy
     
    1. this.getClass.getClassLoader();  // 使用当前类的ClassLoader   
    2.   
    3. Thread.currentThread().getContextClassLoader();  // 使用当前线程的ClassLoader   
    4.   
    5. ClassLoader.getSystemClassLoader();  // 使用系统ClassLoader,即系统的入口点所使用的ClassLoader。  

    注:system ClassLoader与根ClassLoader并不一样。JVM下system ClassLoader通常为App ClassLoader。

    2. 用ClassLoader载入资源的几种方法 

    所有资源都通过ClassLoader载入到JVM里,那么在载入资源时当然可以使用ClassLoader,只是对于不同的资源还可以使用一些别的方式载入,例如对于类可以直接new,对于文件可以直接做IO等。

    2.1 类的载入方式

    假设有类A和类B,A在其方法里需要实例化B,载入类可能的方法有3种。对于载入类的情况,用户需要知道B类的完整名字(包括包名,例如"com.alexia.B") 

    1. 使用Class静态方法 Class.forName  

    [java] view plain copy
     
    1. Class cls = Class.forName("com.alexia.B");  
    2.  B b = (B)cls.newInstance();  

    2. 使用ClassLoader  

    [java] view plain copy
     
    1. /* Step 1. Get ClassLoader */  
    2. ClassLoader cl = this.getClass.getClassLoader();;  // 如何获得ClassLoader参考1  
    3.   
    4. /* Step 2. Load the class */  
    5. Class cls = cl.loadClass("com.alexia.B"); // 使用第一步得到的ClassLoader来载入B  
    6.      
    7. /* Step 3. new instance */  
    8. B b = (B)cls.newInstance(); // 有B的类得到一个B的实例  

    3. 直接new 

    [java] view plain copy
     
    1. B b = new B();  

    注:有人心里可能会想,对于类的载入方式我们都会选择最简单的第3种方式,前两种方式完全是多余。

    实则不然,直接new的方式也是有局限的,举个最简单的例子:Java中有包名的类怎么引用默认包中的类?当然说这个是因为有包名的类不能直接用new引用默认包中的类,那么怎么办呢?答案是使用反射机制,即使用第一种方式来加载类(具体请看这里)。而且,用new()和用newInstance()创建类的实例是不同的,主要区别简单描述如下:

    从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用newInstance()方法的时候,就必须保证:

    (1)这个类已经加载;

    (2)这个类已经链接了(即为静态域分配存储空间,并且如果必须的话将解析这个类创建的对其他类的所有引用)。而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载javaAPI的那个加载器。

    可以看出,newInstance()实际上是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,然后实例化。这样分步的好处是显而易见的。我们可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。

     

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

    假设在com.alexia.A类里想读取文件夹 /com/alexia/config 里的文件sys.properties,读取文件可以通过绝对路径或相对路径,绝对路径很简单,在Windows下以盘号开始,在Unix下以"/"开始。对于相对路径,其相对值是相对于ClassLoader的,因为ClassLoader是一棵树,所以这个相对路径和ClassLoader树上的任何一个ClassLoader相对比较后可以找到文件,那么文件就可以找到。文件有以下三种加载方式:

    1. 直接用IO流读取 

    [java] view plain copy
     
    1. /** 
    2.  * 假设当前位置是 "C:/test",通过执行如下命令来运行A "java com.aleixa.A" 
    3.  * 1. 在程序里可以使用绝对路径,Windows下的绝对路径以盘号开始,Unix下以"/"开始 
    4.  * 2. 也可以使用相对路径,相对路径前面没有"/" 
    5.  * 因为我们在 "C:/test" 目录下执行程序,程序入口点是"C:/test",相对路径就 
    6.  * 是 "com/alexia/config/sys.properties" 
    7.  * (例子中,当前程序的ClassLoader是App ClassLoader,system ClassLoader = 当前的 
    8.  * 程序的ClassLoader,入口点是"C:/test") 
    9.  * 对于ClassLoader树,如果文件在jdk lib下,或在jdk lib/ext下,或在环境变量里, 
    10.  * 都可以通过相对路径"sys.properties"找到,lib下的文件最先被找到 
    11.  */  
    12. File f = new File("C:/test/com/aleixa/config/sys.properties"); // 使用绝对路径  
    13. //File f = new File("com/alexia/config/sys.properties"); // 使用相对路径  
    14. InputStream is = new FileInputStream(f);    

    2. 使用ClassLoader  

    [java] view plain copy
     
    1. /** 
    2.  * 因为有3种方法得到ClassLoader,对应有如下3种ClassLoader方法读取文件 
    3.  * 使用的路径是相对于这个ClassLoader的那个点的相对路径,此处只能使用相对路径 
    4.  */  
    5. InputStream is = null;  
    6. is = this.getClass().getClassLoader().getResourceAsStream(  
    7.        "com/alexia/config/sys.properties"); //方法1  
    8. //is = Thread.currentThread().getContextClassLoader().getResourceAsStream(  
    9.        "com/alexia/config/sys.properties"); //方法2  
    10. //is = ClassLoader.getSystemResourceAsStream("com/alexia/config/sys.properties"); //方法3   

    3. 使用ResourceBundle 

    [plain] view plain copy
     
    1. ResourceBundle bundle = ResourceBundle.getBoundle("com.alexia.config.sys");   

    这种用法通常用来载入用户的配置文件,关于ResourceBunlde更详细的用法请参考其他文档。

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

    总结:有如下3种途径来载入文件 

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

    2.3 web资源的载入方式

    在web应用里当然也可以使用ClassLoader来载入资源,但更常用的情况是使用ServletContext,如下是web目录结构 
        ContextRoot 
           |- JSP、HTML、Image等各种文件 
            |- [WEB-INF] 
                  |- web.xml 
                  |- [lib] Web用到的JAR文件 
                    |- [classes] 类文件 

    用户程序通常在classes目录下,如果想读取classes目录里的文件,可以使用ClassLoader,如果想读取其他的文件,一般使用ServletContext.getResource()。

    如果使用ServletContext.getResource(path)方法,路径必须以"/"开始,路径被解释成相对于ContextRoot的路径,此处载入文件的方法和ClassLoader不同,举例"/WEB-INF/web.xml","/download/WebExAgent.rar"

  • 相关阅读:
    LeetCode "Top K Frequent Elements"
    LeetCode "Integer Break"
    HackerRank "Angry Children 2"
    HackerRank "Kitty and Katty"
    HackerRank "Minimum Penalty Path"
    HackerRank "Larry's Array"
    HackerRank "TBS Problem" ~ NPC
    HackerRank "Morgan and a String"
    HackerRank "Favorite sequence"
    Windows不分区VHD装Linux多系统(三):VM虚拟机安装ubuntu18.04
  • 原文地址:https://www.cnblogs.com/lxl57610/p/5903877.html
Copyright © 2020-2023  润新知