从类路径中获取资源

2022-01-25  本文已影响0人  zhengaoly

从类路径中获取资源

从类路径获取资源只资源未打成jar包时,如果资源打成了jar包,那么这种方式将失效,可以通过getResourceAsStream从jar中读取资源

参考:

https://www.iteye.com/blog/hxraid-483115

http://www.360doc.com/content/11/0610/14/1861654_126000844.shtml

这里所说的资源,可以是任何文件,包括配置文件、脚本、图片等。Java 有能力从类路径中查找获取资源,可将资源放在 CLASSPATH 里,也可打包到 Jar 中。

Java 可通过以下几种方法来访问资源:

在使用中,Class 可通过直接引用类的 class 属性而获得,或是通过实例的 getClass() 方法来获得。获取 ClassLoader 的方式则比较多,常见以下几种:

不过,若是对 Java 的 ClassLoader 概念不太了解,最好还是尽量避免使用它。

Class.getResource 与 ClassLoader.getResource 的区别

这两种方式,都接受一个字符串形式的路径表达式,即资源名,并返回找到的资源的 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");
//从classpath开始,定位

如果用 Class 的 getResource 方法,则有两种写法:

从上面的例子,可以看出两者之间巨大的区别。有些人从网络上复制类似的代码,看看不能正确运行,就开始尝试在资源名前加上 "/",或是去掉开头的 "/",试成功了,便算完工,这绝非正道。

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();
    }
}

编译上述代码,看看不同资源路径的输出结果。

java.class.path: D:\devenv\JDK8\jre\lib\charsets.jar;D:\devenv\JDK8\jre\lib\deploy.jar;D:\devenv\JDK8\jre\lib\ext\access-bridge-64.jar;D:\devenv\JDK8\jre\lib\ext\cldrdata.jar;D:\devenv\JDK8\jre\lib\ext\dnsns.jar;D:\devenv\JDK8\jre\lib\ext\jaccess.jar;D:\devenv\JDK8\jre\lib\ext\jfxrt.jar;D:\devenv\JDK8\jre\lib\ext\localedata.jar;D:\devenv\JDK8\jre\lib\ext\nashorn.jar;D:\devenv\JDK8\jre\lib\ext\sunec.jar;D:\devenv\JDK8\jre\lib\ext\sunjce_provider.jar;D:\devenv\JDK8\jre\lib\ext\sunmscapi.jar;D:\devenv\JDK8\jre\lib\ext\sunpkcs11.jar;D:\devenv\JDK8\jre\lib\ext\zipfs.jar;D:\devenv\JDK8\jre\lib\javaws.jar;D:\devenv\JDK8\jre\lib\jce.jar;D:\devenv\JDK8\jre\lib\jfr.jar;D:\devenv\JDK8\jre\lib\jfxswt.jar;D:\devenv\JDK8\jre\lib\jsse.jar;D:\devenv\JDK8\jre\lib\management-agent.jar;D:\devenv\JDK8\jre\lib\plugin.jar;D:\devenv\JDK8\jre\lib\resources.jar;D:\devenv\JDK8\jre\lib\rt.jar;E:\workspace\java\jnatest\target\classes;D:\devenv\apache-maven-3.8.3\repo\net\java\dev\jna\jna\5.10.0\jna-5.10.0.jar;D:\devenv\jetbrains\ideaIU-2021.1.1.win\lib\idea_rt.jar;D:\devenv\jetbrains\ideaIU-2021.1.1.win\plugins\Groovy\lib\agent\gragent.jar;D:\devenv\jetbrains\ideaIU-2021.1.1.win\plugins\java\lib\rt\debugger-agent.jar
user.dir: E:\workspace\java\spring

## Test resource for: “” ##
ClassLoader#getResource("")=file:/E:/workspace/java/jnatest/target/classes/
Class#getResource("")=file:/E:/workspace/java/jnatest/target/classes/org/example/
## Test resource for: “/” ##
ClassLoader#getResource("/")=null
Class#getResource("/")=file:/E:/workspace/java/jnatest/target/classes/
## Test resource for: “ClassResourceTest.class” ##
ClassLoader#getResource("ClassResourceTest.class")=null
Class#getResource("ClassResourceTest.class")=file:/E:/workspace/java/jnatest/target/classes/org/example/ClassResourceTest.class
## Test resource for: “org/example/ClassResourceTest.class” ##
ClassLoader#getResource("org/example/ClassResourceTest.class")=file:/E:/workspace/java/jnatest/target/classes/org/example/ClassResourceTest.class
Class#getResource("org/example/ClassResourceTest.class")=null
## Test resource for: “/org/example/ClassResourceTest.class” ##
ClassLoader#getResource("/org/example/ClassResourceTest.class")=null
Class#getResource("/org/example/ClassResourceTest.class")=file:/E:/workspace/java/jnatest/target/classes/org/example/ClassResourceTest.class
************************jar包中的资源********************
## Test resource for: “java/lang/Object.class” ##
ClassLoader#getResource("java/lang/Object.class")=jar:file:/D:/devenv/JDK8/jre/lib/rt.jar!/java/lang/Object.class
Class#getResource("java/lang/Object.class")=null
## Test resource for: “/java/lang/Object.class” ##
ClassLoader#getResource("/java/lang/Object.class")=null
Class#getResource("/java/lang/Object.class")=jar:file:/D:/devenv/JDK8/jre/lib/rt.jar!/java/lang/Object.class

打包为 Jar 包后的变化

现在,将上述代码编译后的结果打包成 Jar 文件,假设是 test.jar ,然后从这个 jar 包中运行上述代码,再看看输出结果,比较下与上面的输出有什么变化:

java -classpath test.jar test.ClassResourceTest

值得注意的几点:

错误与陷阱

正确使用 getResource 方法

getResources: 枚举资源

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 {
    Class cls = 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());
    }

}
上一篇 下一篇

猜你喜欢

热点阅读