在使用中,Class 可通过直接引用类的 class 属性而获得,或是通过实例的 getClass() 方法来获得。获取 ClassLoader 的方式则比较多,常见以下几种:
不过,若是对 Java 的 ClassLoader 概念不太了解,最好还是尽量避免使用它。
这两种方式,都接受一个字符串形式的路径表达式,即资源名,并返回找到的资源的 URL。两种方式都可用来定位资源,在网络上流传的文章中,两者都是常见的。实际上,Class 的 getResource 方法也调用了 ClassLoader 的 getResource 方法,但两者有着很大的不同,不了解这两种方法的区别,就容易造成隐患。隐患经常比编写时就出错要可怕得多,因为它在一定场合下是正常的,不容易被发现。
两者最大的区别,是从哪里开始寻找资源。ClassLoader 并不关心当前类的包名路径,它永远以 classpath 为基点来定位资源。而 Class.getResource 则不同,如果资源名是绝对路径(以"/"开头),它会将开头的"/"去除,然后调用 ClassLoader 的 getResource 方法来寻找资源;如果资源名是相对路径,它会在当前的包路径下面寻找资源。
举例来说,假设我们有一个类:test.App (包名为 test),并且在 test 包下有一个与类名同名的 js 文件,名为 App.js。如果用 ClassLoader 来获取这个 js 文件,应该这样写:
App.class.getClassLoader().getResource("test/App.js");
如果用 Class 的 getResource 方法,则有两种写法:
App.class.getResource("App.js");
App.class.getResource("/test/App.js");
从上面的例子,可以看出两者之间巨大的区别。有些人从网络上复制类似的代码,看看不能正确运行,就开始尝试在资源名前加上 "/",或是去掉开头的 "/",试成功了,便算完工,这绝非正道。
Class 与 ClassLoader 的 getResource 方法还有其它一些不同,对 Class 的 getResource 方法来说,若传入的是相对路径,它还会尝试做包名与路径名的转换。查看 Class.getResource 方法的源码,可以看到它首先对资源名调用了 resolveName 方法,然后再调用 ClassLoader 的 getResource 方法来完成资源的定位。
作为演示,我写了以下代码来展示 Class 与 ClassLoader 的 getResource 方法的输出:
/** * Copyright (c) 2014 Chen Zhiqiang <chenzhiqiang@mail.com>. Released under the MIT license. */ package test; import java.net.URL; import java.util.Enumeration; /** * Tests for the use of {@link Class#getResource(String)} and * {@link ClassLoader#getResource(String)}. * * @author Chen Zhiqiang <chenzhiqiang@mail.com> */ public class ClassResourceTest { Class<ClassResourceTest> cls = ClassResourceTest.class; ClassLoader ldr = cls.getClassLoader(); // Thread.currentThread().getContextClassLoader() public static void println(Object s) { System.out.println(s); } void showResource(String name) { println("## Test resource for: “" + name + "” ##"); println(String.format("ClassLoader#getResource(\"%s\")=%s", name, ldr.getResource(name))); println(String.format("Class#getResource(\"%s\")=%s", name, cls.getResource(name))); } public final void testForResource() throws Exception { showResource(""); showResource("/"); showResource(cls.getSimpleName() + ".class"); String n = cls.getName().replace('.', '/') + ".class"; showResource(n); showResource("/" + n); showResource("java/lang/Object.class"); showResource("/java/lang/Object.class"); } public static void main(String[] args) throws Exception { println("java.class.path: " + System.getProperty("java.class.path")); println("user.dir: " + System.getProperty("user.dir")); println(""); ClassResourceTest t = new ClassResourceTest(); t.testForResource(); } }
编译上述代码,看看不同资源路径的输出结果。
现在,将上述代码编译后的结果打包成 Jar 文件,假设是 test.jar ,然后从这个 jar 包中运行上述代码,再看看输出结果,比较下与上面的输出有什么变化:
java -classpath test.jar test.ClassResourceTest
值得注意的几点:
这是一种常见的错误,并在网络上广为流传。它们在打包成 Jar 包后,其结果会发生变化。
资源有可能以文件和目录的形式位于类路径之中,但也可能打包进了 Jar 包或 Zip 包,你不能假设你的代码不会被打包。
网络上有人说,对于 ClassLoader 的 getResource 方法来说,资源名是否以 "/" 开头是一样的,然而,在我这里,ClassLoader 的 getResource 方法并不接受绝对路径,其输出结果为 null。
Java 的 CLASSPATH 是一个路径列表,因此,有可能在多个类路径中出现同样的资源名。如果要列举它们,可以使用 ClassLoader 的 getResources 方法。
下面的代码可以枚举所有的 "META-INF/MANIFEST.MF",你还可以观察到在类路径中哪些 jar 文件包含有该资源:
import java.net.URL; import java.util.Enumeration; public class Test { public static void main(String[] args) throws Exception { ClassLoader ldr = Test.class.getClassLoader(); System.out.println("## Test for getResources(‘META-INF/MANIFEST.MF') ##"); Enumeration<URL> urls = ldr.getResources("META-INF/MANIFEST.MF"); while(urls.hasMoreElements()) System.out.println(urls.nextElement()); } }
下面的代码演示了如何正确获取代码的类路径起点:
/** * Copyright (c) 2014 Chen Zhiqiang <chenzhiqiang@mail.com>. Released under the MIT license. */ package test; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 演示如何获取当前类路径的起点 * * @author Chen Zhiqiang <chenzhiqiang@mail.com> */ public class AppDirTest { Classcls = AppDirTest.class; URL codeLocation = getCodeLocation(); /** * Get the code location. * * Return the classpath where the code run from. The return url will be: * file:/path/my-app/calsses/ or file:/path/my-app/my-app.jar * * @return URL */ public URL getCodeLocation() { if (codeLocation != null) return codeLocation; // Get code location using the CodeSource codeLocation = cls.getProtectionDomain().getCodeSource().getLocation(); if (codeLocation != null) return codeLocation; // If CodeSource didn't work, use {@link } Class.getResource instead. URL r = cls.getResource(""); synchronized (r) { String s = r.toString(); Pattern jar_re = Pattern.compile("jar:\\s?(.*)!/.*"); Matcher m = jar_re.matcher(s); if (m.find()) { // the code is run from a jar file. s = m.group(1); } else { String p = cls.getPackage().getName().replace('.', '/'); s = s.substring(0, s.lastIndexOf(p)); } try { codeLocation = new URL(s); } catch (MalformedURLException e) { throw new RuntimeException(e); } } return codeLocation; } /** * Get the class path root where the program startup, if run in a jar, * return the jar file's parent path. * * @return */ public String getAppDir() { File f = new File(getCodeLocation().getPath()); return f.isFile() ? f.getParent() : f.getPath(); } public static void main(String[] args) { AppDirTest t = new AppDirTest(); System.out.println("code location: " + t.getCodeLocation()); System.out.println("app dir: " + t.getAppDir()); } }
以上就是Java 正确地从类路径中获取资源的详细内容,更多关于Java 从类路径中获取资源的资料请关注脚本之家其它相关文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、来自互联网转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系QQ:712375056 进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
Copyright © 2009-2021 56dr.com. All Rights Reserved. 特网科技 特网云 版权所有 珠海市特网科技有限公司 粤ICP备16109289号
域名注册服务机构:阿里云计算有限公司(万网) 域名服务机构:烟台帝思普网络科技有限公司(DNSPod) CDN服务:阿里云计算有限公司 中国互联网举报中心 增值电信业务经营许可证B2
建议您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流浏览器浏览本网站