您的位置:首页 > 其它

Class.getResource vs ClassLoader.getResource .

2012-06-01 17:59 369 查看
这两个方法还是略有区别的, 以前一直不加以区分,直到今天发现要写这样的代码的时候运行

错误, 才把这个问题澄清了一下。

基本上,两个都可以用于从 classpath 里面进行资源读取, classpath包含classpath中的路径

和classpath中的jar。

两个方法的区别是资源的定义不同, 一个主要用于相对与一个object取资源,而另一个用于取相对于classpath的

资源,用的是绝对路径。

在使用Class.getResourceAsStream 时, 资源路径有两种方式, 一种以 / 开头,则这样的路径是指定绝对

路径, 如果不以 / 开头, 则路径是相对与这个class所在的包的。

在使用ClassLoader.getResourceAsStream时, 路径直接使用相对于classpath的绝对路径。

举例,下面的三个语句,实际结果是一样的:

com.explorers.Test.class.getResourceAsStream("abc.jpg")

= com.explorers.Test.class.getResourceAsStream("/com/explorers/abc.jpg")

= ClassLoader.getResourceAsStream("com/explorers/abc.jpg")

主题:资源获取:

在开发java程序的过程中,我们经常要做的一件事就是获取资源。那么什么是资源呢?说白了,在计算机里那就是一堆数据。只是这堆数据对我们的java程序有多种表现形式,一般来说有File,URL,InputStream等等。而单就文件这一项就有很多种:配置文件,java类文件,jps文件,图片、css、js文件等等。面对这林林总总的资源,我们在设计一个读取资源的接口时,就需要针对不同形式的资源提供方法,这样就导致我们的接口还是与实际的资源形式绑定在一起,未能完全的抽象。另外,在java程序中资源的存放位置也是各异的。有的存放在classpath中,有的存放在文件系统中,有的存放在web应用中。而对于不同位置的资源,java程序获取这些资源的方法各有不同。

A、获取classpath中的资源:

Java代码



URL url = this.getClass().getResource("resource_name");
URL url = this.getClass().getClassLoader().getResource("resource_name");
URL url = Thread.currentThread().getContextClassLoader().getResource("resource_name");

那么在jdk中为什么又提供了三种方式来获取classpath下的资源呢?这其中是有些来头的。

第一行代码中是利用Class类的实例来获取,第二行代码是使用加载当前类的classloader来获取。看下jdk中的源代码会发现class类的实例最后还是委托加载他的classloader来获取资源的。


public java.net.URL getResource(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResource(name);
}
return cl.getResource(name);
}


从上面的代码中可以看出,对于资源的加载并没有像类加载所采用的双亲委托机制。而是当前类的classloader不为null的情况下先从当前类的classloader中加载资源。而只有当前类的classloader为null的时候才从system classloader中去加载资源。这样可以方便我们自定义配置类覆盖一些默认配置。当然,j2se应用中如果没有特别定制classloader时,我们自己写的类都是被system classloader加载的。到底利用class去获取资源和利用classloader去获取资源有什么区别呢?区别就在
resolveName(name)这个方法中。两种方式对于资源名称的表示方式不同。下面是一个简单的包结构,/表示类路径的根

/

|-com.cn.test

|-Test.class

|-test2.txt

|-test1.txt

Java代码



// 获取与当前类在同一个包下的资源

URL url1 = this.getClass().getResource("test2.txt");
// 获取com.cn.test包下的资源,需加/

URL url2 = this.getClass().getResource("/com/cn/test/test2.txt");
// 获取类路径根下的资源
URL url3 = this.getClass().getClassLoader().getResource("test1.txt");
// 获取包com.cn.test包下的资源

URL url4 = this.getClass().getResource("com/cn/test/test2.txt");

比如我们有以下目录
|--project
|--src
|--javaapplication
|--Test.java
|--file1.txt
|--file2.txt
|--build
|--javaapplication
|--Test.class
|--file3.txt
|--file4.txt
在上面的目录中,有一个src目录,这是JAVA源文件的目录,有一个build目录,这是JAVA编译后文件(.class文件等)的存放目录
那么,我们在Test类中应该如何分别获得
file1.txt file2.txt file3.txt file4.txt这四个文件呢?
首先讲file3.txt与file4.txt
file3.txt:

Java代码



方法一:File file3 = new File(Test.class.getResource("file3.txt").getFile());

方法二:File file3 = new File(Test.class.getResource("/javaapplication/file3.txt").getFile());

方法三:File file3 = new File(Test.class.getClassLoader().getResource("javaapplication/file3.txt").getFile());

file4.txt:

Java代码



方法一:File file4 = new File(Test.class.getResource("/file4.txt").getFile());

方法二:File file4 = new File(Test.class.getClassLoader().getResource("file4.txt").getFile());

很好,我们可以有多种方法选择,但是file1与file2文件呢?如何获得?
答案是,你只能写上它们的绝对路径,不能像file3与file4一样用class.getResource()这种方法获得,它们的获取方法如下
假如整个project目录放在c:/下,那么file1与file2的获取方法分别为
file1.txt

Java代码



方法一:File file1 = new File("c:/project/src/javaapplication/file1.txt");

方法二:。。。没有

file2.txt

Java代码



方法一:File file2 = new File("c:/project/src/file2.txt");

方法二:。。。也没有

总结一下,就是你想获得文件,你得从最终生成的.class文件为着手点,不要以.java文件的路径为出发点,因为真正使用的就是.class,不会拿个.java文件就使用,因为java是编译型语言嘛
至于getResouce()方法的参数,你以class为出发点,再结合相对路径的概念,就可以准确地定位资源文件了,至于它的根目录嘛,你用不同的IDE build出来是不同的位置下的,不过都是以顶层package作为根目录,比如在Web应用中,有一个WEB-INF的目录,WEB-INF目录里面除了web.xml文件外,还有一个classes目录,没错了,它就是你这个WEB应用的package的顶层目录,也是所有.class的根目录
“/”,假如clasaes目录下面有一个file.txt文件,它的相对路径就是"/file.txt",如果相对路径不是以"/"开头,那么它就是相对于.class的路径。。
还有一个getResourceAsStream()方法,参数是与getResouce()方法是一样的,它相当于你用getResource()取得File文件后,再new InputStream(file)一样的结果

而第三利用当前线程的contextClassLoader来获取资源的解释可以参考我的另一篇

B、获取文件系统中的资源

[java]
view plaincopyprint?

// 1、获得File对象
File file = new File("test.txt");
// 2、获得File对象的字节流
InputStream in = new FileInputStream(file);

值得注意的是在File的构造函数File(String name) 中的name参数可以是相对路径和绝对路径。相对路径是相对于System.getProperties("user.dir")的。

C、获取web应用中的资源

Java代码



servletContext.getResourceAsStream(resource_name);

resource_names为相对于webroot的路径表示。例如获取web.xml,resource_name表示为"/WEB-INF/web.xml"

面对上面介绍的各种资源表现形式和存放位置,难道java中就没有提供一个统一处理方式吗?有,java.net.URL。

从名称上来看 URL(Uniform Resource Locator) 统一资源定位器。看起来很好很强大。但很多时候使用它并不能定位到我们需要的资源。

首先,它jdk中体统的URL能访问的协议非常有限(当然可以进行扩展,不过很麻烦);常用的有http,file,ftp等等。并没有提供对classpath和servletContext中的资源的获取方法。

另外,它没有提供判断资源是否存在的方法。每次只有等我们真正去获取资源的时候抛出异常才能知道资源无法获取。

其次,URL这个类的职责未划分清楚,既用来表示资源有用来获取其资源。

===============================================

From : http://blog.csdn.net/ruyanhai/archive/2007/11/07/1871663.aspx
◆ 一般情况下,我们都使用相对路径来获取资源,这样的灵活性比较大.

比如当前类为com/bbebfe/Test.class

而图像资源比如sample.gif应该放置在com/bbebfe/sample.gif

而如果这些图像资源放置在icons目录下,则应该是com/bbebfe/icons/sample.gif

通过当前类文件的路径获取资源主要有如下几种方式:

· 假设当前类为com.bbebfe.Test

· 包所在的文件夹为bin

String imageName = "icons/sample.gif"

1, 通过Class.getResource()定位类路径下的资源(bin/com/bbebfe/icons/sample.gif)

Class clazz = this.getClass();

URL url = clazz.getResource(imageName);

2, 通过ClassLoader.getResource()定位包的根目录下的资源(bin/icons/sample.gif)

Class clazz = this.getClass();

URLClassLoader loader = (URLClassLoader)clazz.getClassLoader();

URL url = loader.getResource(imageName);

3, 通过ClassLoader.findResource()提供自己定制的方式定位资源

URL url = loader.findResource(imageName);

◆ 那么这三种方法有那些区别, 我们应该在何时使用哪种方法呢?

· Class.getResource() 方法

该方法实际通过该Class的Class Loader的getResource()方法来获得资源, 在调用ClassLoader的getResource()方法之前, Class.getResource()方法会对资源名称做一定的处理,构建一个该资源的绝对名称(absolute name, 大意是:

+ 如果资源名称以'/'('/u002f') 开始, 则资源的绝对名称是'/'以后的部分.

如果imageName是"/icons/sample.gif", 则在这里会变成"icons/sample.gif"

+ 否则对于其他情况, 绝对名称将是如下形式(给资源名称的前面加上modified_package_name/):

modified_package_name/resource_name (修正的包名称/资源名称)

其中修正的包名称含义是将当前对象所在的包名称中的'.'('/u002e')替换为'/'

如果ClassLoader.getResource()方法返回一个值为null的URL, 则Class.getResource()方法最终会将资源请求交给ClassLoader.getSystemResource(java.lang.String).

· ClassLoader.getResource() 方法

该对资源进行查找, 资源的名称是以'/'分隔的路径, 这个方法首先查找自己的父亲ClassLoader, 由自己的父ClassLoader来查找资源(实际上, 如果父亲的父亲不是空, 则父亲仍会向上提交查找请求). 如果自己的父ClassLoader是null, 则查找Java虚拟机中内建的class loader, 并将资源请求提交给它们, 如果这些操作都失败了, 则ClassLoader会调用自己的findResource()方法来查找资源.

· ClassLoader.findResource() 方法

该方法在内部查找指定的资源, 如果你实现了自己的Class Loader,则应该重载这个方法以自己特定的方式来查找类文件和资源.

◆ 通过以上的总结, 我们可以看到三点.

1, 无论是getResource(), 还是findResource(), 这些方法都只是资源的定位方法, 最终都只是返回一个URL, 只是对资源的定位而已, 我们随后应通过自己的方法来读取这些资源. 而在Class和ClassLoader中还定义的有getResourceAsStream方法, 该方法是getResource的增强版, 这里就不介绍了.

2, 如果需要以类为相对路径查找资源, 则应该调用Class.getResource()方法, 不要直接调用ClassLoader.getResource()方法. 另外, 除非是你自己定义了ClassLoader并重载了findResource方法,否则也不要直接调用ClassLoader.findResource方法, 因为在Class.getResource()方法中会对资源名称作一定的处理, 这在上面介绍了, 下面举个实例:

假设我的当前类在Eclipse工程Database下, 类所在的包是com.bbebfe.test, 而icons目录放在bin/com/bbebfe/test/目录下, 我需要得到icons/sample.gif文件的URL, 则调用this.getClass().getResource()得到的URL是:

file:/E:/MyLife/MyProjects/Eclipse3.2/Database/bin/com/bbebfe/test/icons/disremove.gif

3, 有时候我们希望某个jar库的图像资源在同一个icons下统一管理, 而不是为每个包下面的Class建一个icons, 也就是说需要以库为相对路径来查找资源, 此时则应该调用ClassLoader.getResource()方法, 举个例子:

· 某个工程有如下的包结构:

com.bbebfe.ui

com.bbebfe.test

com.bbebfe.database

· 如果以类为相对路径, 则在每个包下都必须建立一个icons目录, 并放置相应的资源文件. 如下:

com.bbebfe.ui/icons/...

com.bbebfe.test/icons/...

com.bbebfe.database/icons/...

· 而我们可能希望在根目录下放置一个icons目录, 把所有资源放置在这里管理, 这样还可以防止资源的重复. 就是如下形式

com.bbebfe.ui

com.bbebfe.test

com.bbebfe.database

icons/sample.gif ...

则此时我们应该调用ClassLoader.getResource方法, 由于它没有对资源名称作处理, 也就是说没有将修正的包名添加到资源名称前, 所以它会在类所在的包的根下去查找资源.(运行java程序的语法是java com.bbebfe.ui.Test, 所以根目录是com目录的上级目录).

◆ 最后, 在Java中对资源进行定位的方法有很多种, 在Eclipse源代码中还有如下一段定位文件资源的代码, 还没有时间研究, 以后再谈:

ProtectionDomain domain = Main.class.getProtectionDomain();

CodeSource source = null;

URL result = null;

if (domain != null)

source = domain.getCodeSource();//获得code source

if (source != null)

result = source.getLocation();//获得URL

String path = decode(result.getFile());//

// normalize to not have leading / so we can check the form

File file = new File(path);

path = file.toString().replace('//', '/');

// create a file URL (via File) to normalize the form (e.g., put

// the leading / on if necessary)

path = new File(path).toURL().getFile();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: