ClassLoader提供了两个方法用于从装载的类路径中取得资源:
public URL getResource(String name);
public InputStream getResourceAsStream(String name);
这里name是资源的类路径,它是相对与“/”根路径下的位置。getResource得到的是一个URL对象来定位资源,而getResourceAsStream取得该资源输入流的引用保证程序可以从正确的位置抽取数据。
但 是真正使用的不是ClassLoader的这两个方法,而是Class的 getResource和getResourceAsStream方法,因为 Class对象可以从你的类得到(如YourClass.class或 YourClass.getClass()),而ClassLoader则需要再 调用一次YourClass.getClassLoader()方法,不过根据JDK文档的说法,Class对象的这两个方法其实是“委托” (delegate)给装载它的ClassLoader来做的,所以只需要使用 Class对象的这两个方法就可以了。
因此,直接调用 this.getClass().getResourceAsStream(String name);获取流,静态化方法中则使用ClassLoader.getSystemResourceAsStream(String name); 。
下面是一些得到classpath和当前类的绝对路径的一些方法。你可能需要使用其中的一些方法来得到你需要的资源的绝对路径。
1.this.getClass().getResource("")
得到的是当前类class文件的URI目录。不包括自己!
如:file:/D:/workspace/jbpmtest3/bin/com/test/
2.this.getClass().getResource("/")
得到的是当前的classpath的绝对URI路径。
如:file:/D:/workspace/jbpmtest3/bin/
3.this.getClass().getClassLoader().getResource("")
得到的也是当前ClassPath的绝对URI路径。
如:file:/D:/workspace/jbpmtest3/bin/
4.ClassLoader.getSystemResource("")
得到的也是当前ClassPath的绝对URI路径。
如:file:/D:/workspace/jbpmtest3/bin/
5.Thread.currentThread().getContextClassLoader().getResource("")
得到的也是当前ClassPath的绝对URI路径。
如:file:/D:/workspace/jbpmtest3/bin/
6.ServletActionContext.getServletContext().getRealPath(“/”)
Web应用程序中,得到Web应用程序的根目录的绝对路径。这样,我们只需要提供相对于Web应用程序根目录的路径,就可以构建出定位资源的绝对路径。
如:file:/D:/workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/WebProject
注意点:
1.尽量不要使用相对于System.getProperty("user.dir")当前用户目录的相对路径。这是一颗定时炸的弹,随时可能要你的命。
2.尽量使用URI形式的绝对路径资源。它可以很容易的转变为URI,URL,File对象。
3. 尽量使用相对classpath的相对路径。不要使用绝对路径。使用上面ClassLoaderUtil类的 public static URL getExtendResource(String relativePath)方法已经能够使用相对于 classpath的相对路径定位所有位置的资源。
4.绝对不要使用硬编码的绝对路径。因为,我们完全可以使用ClassLoader类的getResource("")方法得到当前classpath的绝对路径。如果你一定要指定一个绝对路径,那么使用配置文件,也比硬编码要好得多!
获得CLASSPATH之外路径的方法:
URL base = this.getClass().getResource(""); //先获得本类的所在位置,如/home/popeye/testjava/build/classes/net/
String path = new File(base.getFile(), "……/……/……/"+name).getCanonicalPath(); //就可以得到/home/popeye/testjava/name
另外,如果从ANT启动程序,this.getClass().getResource("")取出来的比较怪,直接用JAVA命令行调试就可成功。
例如:
工程结构:
Java-learning
|---- src/main/java
|---- src/test/java
|---- src/main/resources/META-INF/spring
|---- src/main/ resources
|---- 引用的jar包和maven的pom.xml等
测试代码包路径:
src/main/java
|---- com.lee.java.learning
|---- compiler
|---- Test.java
|---- AnClass.java
测试代码:
AnClass.java
view plain
package com.lee.java.learning;
public class AnClass {
public static void main(String[] args) {
}
}
Test.java
view plain
package com.lee.java.learning.compiler;
import java.net.URL;
public class Test {
public static void main(String[] args) {
Test test = new Test();
test.test();
}
public void test() {
URL root = this.getClass().getResource("/");
URL current1 = this.getClass().getResource("");
URL current2 = this.getClass().getResource(".");
URL parent = this.getClass().getResource("..");
URL self1 = this.getClass().getResource("Test.class");
URL self2 = this.getClass().getResource("./Test.class");
URL brother = this.getClass().getResource("../AnClass.class");
System.out.println("root = "+root);
System.out.println("current1 = "+current1);
System.out.println("current2 = "+current2);
System.out.println("parent = "+parent);
System.out.println("self1 = "+self1);
System.out.println("self2 = "+self2);
System.out.println("brother = "+brother);
}
}
测试输出结果:
root = file:/D:/Java/workspace_1/java-learning/target/test-classes/
current1 = file:/D:/Java/workspace_1/java-learning/target/test-classes/com/lee/java/learning/compiler/
current2 = file:/D:/Java/workspace_1/java-learning/target/test-classes/com/lee/java/learning/compiler/
parent = file:/D:/Java/workspace_1/java-learning/target/test-classes/com/lee/java/learning/
self1 = file:/D:/Java/workspace_1/java-learning/target/classes/com/lee/java/learning/compiler/Test.class
self2 = file:/D:/Java/workspace_1/java-learning/target/classes/com/lee/java/learning/compiler/Test.class
brother = file:/D:/Java/workspace_1/java-learning/target/classes/com/lee/java/learning/AnClass.class
分析应该是classpath的问题。eclipse下调试程序,观察命令行参数:
view plain
E:\java6\bin\javaw.exe -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:4584 -Dfile.encoding=UTF-8 -classpath D:\java\eclipse\my_workspace\java-learning\target\test-classes;D:\java\eclipse\my_workspace\java-learning\target\classes;E:\working\maven_lib\commons-logging\commons-logging\1.1.1\commons-logging-1.1.1.jar;E:\working\maven_lib\junit\junit\4.7\junit-4.7.jar;E:\working\maven_lib\org\springframework\spring\2.5.6\spring-2.5.6.jar com.sdo.lee.java.learning.compiler.Test
再查看了一下工程的.classpath文件:
view plain
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="output" path="target/classes"/>
<classpathentry including="**/*.java" kind="src" path="src/main/java"/>
<classpathentry including="**/*.java" kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry excluding="**/*.java" kind="src" path="src/main/resources/META-INF/spring"/>
<classpathentry excluding="META-INF/spring/|**/*.java" kind="src" path="src/main/resources"/>
<classpathentry kind="var" path="M2_REPO/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar"/>
<classpathentry kind="var" path="M2_REPO/junit/junit/4.7/junit-4.7.jar"/>
<classpathentry kind="var" path="M2_REPO/org/springframework/spring/2.5.6/spring-2.5.6.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
</classpath>
发现更改上面的其中一条<classpathentry>如下,即可得到预期结果。
<classpathentry including="**/*.java" kind="src" output="target/classes" path="src/main/java"/>
同时,更改后的eclipse命令行参数如下:
view plain
E:\java6\bin\javaw.exe -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:4785 -Dfile.encoding=UTF-8 -classpath D:\java\eclipse\my_workspace\java-learning\target\classes;D:\java\eclipse\my_workspace\java-learning\target\test-classes;E:\working\maven_lib\commons-logging\commons-logging\1.1.1\commons-logging-1.1.1.jar;E:\working\maven_lib\junit\junit\4.7\junit-4.7.jar;E:\working\maven_lib\org\springframework\spring\2.5.6\spring-2.5.6.jar com.sdo.lee.java.learning.compiler.Test
eclipse在运行时是如何根据.classpath文件来添加命令行参数的-classpath属性不得而知。
(初步实践发现:eclipse可能是逐条读取classpathentry,一旦发现指定output属性,即将其path属性指定的路径添加 到命令行参数的-classpath属性,并在最后将kind="output"指定的的默认path属性路径也添加到-classpath属性中。至于 是否正确,有待验证。)
对于相对路径的解析('/','.','..',''等),java的源代码如下:
Class.class
view plain
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);
}
view plain
private String resolveName(String name) {
if (name == null) {
return name;
}
if (!name.startsWith("/")) {
Class c = this;
while (c.isArray()) {
c = c.getComponentType();
}
String baseName = c.getName();
int index = baseName.lastIndexOf('.');
if (index != -1) {
name = baseName.substring(0, index).replace('.', '/')
+"/"+name;
}
} else {
name = name.substring(1);
}
return name;
}
ClassLoader.class
view plain
public URL getResource(String name) {
URL url;
if (parent != null) {
url = parent.getResource(name);
} else {
url = getBootstrapResource(name);
}
if (url == null) {
url = findResource(name);
}
return url;
}
view plain
public static URL getSystemResource(String name) {
ClassLoader system = getSystemClassLoader();
if (system == null) {
return getBootstrapResource(name);
}
return system.getResource(name);
}
view plain
protected URL findResource(String name) {
return null;
}
view plain
private static URL getBootstrapResource(String name) {
try {
// If this is a known JRE resource, ensure that its bundle is
// downloaded. If it isn't known, we just ignore the download
// failure and check to see if we can find the resource anyway
// (which is possible if the boot class path has been modified).
sun.jkernel.DownloadManager.getBootClassPathEntryForResource(name);
} catch (NoClassDefFoundError e) {
// This happens while Java itself is being compiled; DownloadManager
// isn't accessible when this code is first invoked. It isn't an
// issue, as if we can't find DownloadManager, we can safely assume
// that additional code is not available for download.
}
URLClassPath ucp = getBootstrapClassPath();
Resource res = ucp.getResource(name);
return res != null ? res.getURL() : null;
}
从上面的源代码可以看到,Class类的getResource方法先是进行一个简单的路径解析,然后将其委托给其装载器去处理,装载器则是委托 其父装载器处理,一直到bootstrap装载器。bootstrap装载器是非java实现的,sun没有开源,具体如何实现不得可知,但有一点可以肯 定的是根据classpath来进行装载。java的官方文档说明中讲到:classloader装载类时的优先级(-jar命令参数 > -classpath命令参数 > CLASSPATH环境变量 > 默认的'.'指定的当前路径)。详细可参考官方文档How Classes are Found一文。
在相对路径解析时,我们发现所有的相对路径是以'/'指定的.class文件根目录来进行解析。观察上面的eclipse命令行参数猜想:类装载 器默认将-classpath参数指定的第一个目录作为'/'指定的根目录,即getResource('/')返回的结果,而一旦/后面跟有具体的文件 时(如'./Test.class'),则会顺序遍历classpath参数指定的所有路径,找到相应的Test.class文件,返回其绝对路径的 URL,如有多个,则返回第一个找到的Test.class文件的路径。
为求证,在纯命令行环境下进行实践,实践结果与猜想的一致。
总结:对于这种采用构建工具进行构建的项目,往往指定有多个.class文件输出目录(对于只有一个.class文件输出目录时,不存在本文开始 处描述的问题),因此在采用getResource(String name)之类的涉及到相对路径的方法调用,要稍加注意,有时候结果可能与预期出现偏差。
相关推荐
《Tomcat类加载机制——ClassLoader详解》 在Java Web开发中,Tomcat作为最常用的Servlet容器,其类加载机制对于理解和优化应用性能至关重要。本文将深入探讨Tomcat的ClassLoader是如何工作的,以及它如何影响到...
总结,Java ClassLoader是Java平台的关键特性,理解其工作原理和应用场景对于优化系统性能、设计可扩展的系统至关重要。通过本教程,读者应能掌握如何利用ClassLoader实现动态加载、安全隔离和版本控制等功能,提升...
这篇博客“Java类动态加载(一)——java源文件动态编译为class文件”可能主要探讨了如何在运行时将Java源代码(.java)编译成对应的字节码文件(.class),并将其加载到Java虚拟机(JVM)中。以下是对这个主题的详细解析...
Class对象的创建发生在类加载(java.lang.ClassLoader)的时候,JVM加载一个类的时候首先创建Class对象,然后创建这个类的每个实例的时候都使用这个Class对象。 Class对象的作用是: 1. JAVA 使用 Class 对象来...
### Java杂谈——一个计算机专业学生几年的编程经验汇总谈 #### 一、关于动态加载机制 在Java中,动态加载机制是实现高效资源管理的关键之一。它允许Java虚拟机(JVM)根据需要加载类,而不是一次性加载所有的类。...
Java反射机制是Java编程语言中的一个重要特性,它允许程序在运行时动态地获取类的信息并进行操作。在Java中,反射机制是通过`java.lang.reflect`包提供的API实现的,这些API使得开发者能够访问类的私有属性、方法...
总结起来,学习Java需要逐步深入,从安装环境到理解语法,再到掌握面向对象编程和使用类库,每一步都需要实践和思考。同时,利用优质资源如书籍和文档,将加速学习进程。每个人的路径可能不同,关键是找到适合自己的...
例如,如果图片位于项目的`resources`目录下,可以使用`ClassLoader`来获取URL: ```java InputStream is = getClass().getResourceAsStream("/resources/image.jpg"); ImageIcon icon = new ImageIcon(ImageIO....
2. **扩展类加载器(Extension ClassLoader)**:负责加载JRE/lib/ext目录下的jar包或者通过java.ext.dirs系统属性指定的其他路径下的类库。 3. **系统类加载器(System ClassLoader)**:也称为应用类加载器,负责...
【深入Java虚拟机(二)——类的生命周期(上)类的加载和连接】 Java虚拟机(JVM)是Java程序的核心,它负责解释和执行Java字节码。类的生命周期在JVM中是一个关键的概念,它涵盖了从类的加载到卸载的整个过程。...
关于JDK有两个问题是很容易一直困扰Java程序员的地方:一个是CLASSPATH的问题,其实从原理上来说,是要搞清楚JRE的ClassLoader是如何加载Class的;另一个问题是package和import问题,如何来寻找类的路径问题。把这两...
- 获取方法:可以通过`ClassLoader.getSystemClassLoader()`来获取。 - 负责加载:主要负责通过`java-classpath`或`-Djava.class.path`指定的目录下的类和jar包。 Bootstrap ClassLoader是一个特殊的装载器,它是...
Java Development Kit (JDK) 包含Java编译器(javac)、Java运行时解释器(JRE)、Java应用程序编程窗口(如JConsole)以及Java文档化工具(javadoc)等,为Java开发提供了一整套工具。 【工作空间与环境变量】 在...
- **扩展类加载器**(Extension ClassLoader):加载JRE的lib/ext目录下的扩展类库。 - **应用程序类加载器**(Application ClassLoader):加载classpath路径下的用户自定义类,也就是程序员编写的类。 - **...
本文将深入探讨“安卓原生热更新”及其核心——`ClassLoader`。 热更新的基本原理是通过替换应用程序中的部分代码资源,尤其是Dalvik字节码(.dex文件),以达到更新的效果。在安卓系统中,`.dex`文件包含了应用的...
本文将深入探讨Java的装载工具——类加载器(ClassLoader)以及相关的加载方法,旨在为开发者提供详尽的指导。 首先,类加载器在Java中扮演着关键角色。它的主要职责是将类的字节码文件(.class文件)从文件系统或...
- 通过一个类的全限定名获取该类的二进制流。这通常是指从磁盘读取`.class`文件。 - 将该二进制流中的静态存储结构转化为方法区运行时数据结构。 - 在内存中生成该类的`Class`对象,作为该类的数据访问入口。 2....
- **CLASSPATH**:这是指Java运行环境(JRE)中的类加载器(ClassLoader)加载类文件的路径。要深入理解这一概念,就需要明白JRE中的类加载器是如何加载类文件的。这对于避免类找不到或加载错误等问题至关重要。 - *...
Jworld@TW技术论坛版主,Java权威技术顾问与专业讲师,Java畅销书作者——林信良全新力作。 国内第一本Java 7,前期版本累计销量5万册。 《Java JDK 7学习笔记》针对Java SE 7新功能全面改版,无论是章节架构或...