ClassLoader 详解及用途

1.

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

1.1 this.getClass.getClassLoader();  
1.2 Thread.currentThread().getContextClassLoader();  
方法一得到的Classloader是静态的,表明类的载入者是谁;

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

2.

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

对于WEB APP线程,它的contextClassLoader是WebAppClassLoader  
对于Tomcat Server线程,它的contextClassLoader是CatalinaClassLoader

3.

获得ClassLoader的几种方法可以通过如下3种方法得到ClassLoader  
this.getClass.getClassLoader(); // 使用当前类的ClassLoader  
Thread.currentThread().getContextClassLoader(); // 使用当前线程的ClassLoader  
ClassLoader.getSystemClassLoader(); // 使用系统ClassLoader,即系统的入口点所使用的ClassLoader。(注意,system ClassLoader与根ClassLoader并不一样。JVM下system ClassLoader通常为App ClassLoader)

4.资源载入

4.1 使用ClassLoader

  /* Step 1. Get ClassLoader */ 
   ClassLoader cl; // 如何获得ClassLoader参考3

  /* Step 2. Load the class */ 
  Class cls = cl.loadClass("com.rain.B"); // 使用第一步得到的ClassLoader来载入B 
     
  /* Step 3. new instance */ 
  B b = (B)cls.newInstance(); // 有B的类得到一个B的实例 

4.2 使用Class静态方法 Class.forName

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

4.3 直接new  
    B b = new B();

5. 文件载入

5.1 直接IO  

/** 
 * 假设当前位置是 "C:/test",通过执行如下命令来运行A "java com.rain.A" 
 * 1. 在程序里可以使用绝对路径,Windows下的绝对路径以盘号开始,Unix下以"/"开始 
 * 2. 也可以使用相对路径,相对路径前面没有"/" 
 * 因为我们在 "C:/test" 目录下执行程序,程序入口点是"C:/test",相对路径就 
 * 是 "com/rain/config/sys.properties" 
 * (例子中,当前程序的ClassLoader是App ClassLoader,system ClassLoader = 当前的 
 * 程序的ClassLoader,入口点是"C:/test") 
 * 对于ClassLoader树,如果文件在jdk lib下,如果文件在jdk lib/ext下,如果文件在环境变量里, 
 * 都可以通过相对路径"sys.properties"找到,lib下的文件最先被找到 
 */ 
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,如果配置文件是非英文的,可能出现乱码问题。 

5.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里,这里要注意编码问题。 

5.3 使用ResourceBundle  

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

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

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

 如何在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"


6.

Class.getResource(String path)

path不以’/'开头时,默认是从此类所在的包下取资源;
path  以’/'开头时,则是从ClassPath根下获取;
package testpackage;
public class TestMain {
    public static void main(String[] args) {
        System.out.println(TestMain.class.getResource(""));
        System.out.println(TestMain.class.getResource("/"));
    }
}

输出结果:

file:/E:/workspace/Test/bin/testpackage/
file:/E:/workspace/Test/bin/

再来一个实例,假设有如下Project结构:

如果我们想在TestMain.java中分别取到1~3.properties文件,该怎么写路径呢?代码如下:

package testpackage;


public class TestMain {


    public static void main(String[] args) {
        // 当前类(class)所在的包目录
        System.out.println(TestMain.class.getResource(""));
        // class path根目录
        System.out.println(TestMain.class.getResource("/"));
        
        // TestMain.class在<bin>/testpackage包中
        // 2.properties  在<bin>/testpackage包中
        System.out.println(TestMain.class.getResource("2.properties"));
        
        // TestMain.class在<bin>/testpackage包中
        // 3.properties  在<bin>/testpackage.subpackage包中
        System.out.println(TestMain.class.getResource("subpackage/3.properties"));
        
        // TestMain.class在<bin>/testpackage包中
        // 1.properties  在bin目录(class根目录)
        System.out.println(TestMain.class.getResource("/1.properties"));
    }
}

Class.getClassLoader().getResource(String path)

path不能以’/'开头时;
path是从ClassPath根下获取;

还是先看一下下面这段代码的输出:

复制代码
package testpackage;
public class TestMain {
    public static void main(String[] args) {
        TestMain t = new TestMain();
        System.out.println(t.getClass());
        System.out.println(t.getClass().getClassLoader());
        System.out.println(t.getClass().getClassLoader().getResource(""));
        System.out.println(t.getClass().getClassLoader().getResource("/"));//null
    }
}
复制代码

输出结果:

class testpackage.TestMain
sun.misc.Launcher$AppClassLoader@1fb8ee3
file:/E:/workspace/Test/bin/
null

getResource 跟getResourceAsStream 区别一个返回 URL,一个返回Stream



 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值