从另一个jar文件访问资源

16

我有一个简单的结构:一个数据Jar文件,其中包含一批数据,和一个服务Jar文件,它使用该数据运行服务。为了使数据易于更换,我将它们分开,并且service.jar的类路径包含了data.jar所在的目录。

在service.jar中,我使用getResource来加载数据文件。如果数据文件直接在文件夹内,则可以正常工作,但是如果数据文件在data.jar内,则会失败;

这会失败:

all
+ globalclasspath
| + data.jar
|   + mine.properties
+ daemons
  + service.jar

jsvc -cp globalclasspath:daemons/service.jar (...)

MyClass.class.getClassLoader( ).getResource( "mine.properties" ); // <-- null

但是这个有效:

all
+ globalclasspath
| + mine.properties
+ daemons
  + service.jar

jsvc -cp globalclasspath:daemons/service.jar (...)

MyClass.class.getClassLoader( ).getResource( "mine.properties" ); // <-- not null

我不想改变类路径(除非我可以将其更改为不依赖于数据jar文件名称的通用内容),但我可以更改getResource字符串(我尝试过/data/mine.properties和/data.jar/mine.properties都没有成功)。是否有一种更改方式,使资源可以从jar包内加载?

3个回答

5

解决方案1

使用类路径通配符。

jsvc -cp globalclasspath/*:daemons/service.jar (...)

参见 "如何在类路径中使用通配符添加多个jar包?"

解决方案2

要读取不在类路径上的JAR中的数据,可以使用URLClassLoader。一般的算法是:

  1. 找到globalclasspath目录中JAR的列表。
  2. 从这个JAR列表创建一个URLClassLoader
  3. URLClassLoader实例中查找所需的资源。

要查找类路径上的JAR,我使用了StackOverflow文章"从类路径目录获取资源列表"中的ResourceList

public class MyClass {
    /**
     * Creates a {@code URLClassLoader} from JAR files found in the
     * globalclasspath directory, assuming that globalclasspath is in
     * {@code System.getProperty("java.class.path")}.
     */
    private static URLClassLoader createURLClassLoader() {
        Collection<String> resources = ResourceList.getResources(Pattern.compile(".*\\.jar"));
        Collection<URL> urls = new ArrayList<URL>();
        for (String resource : resources) {
            File file = new File(resource);
            // Ensure that the JAR exists
            // and is in the globalclasspath directory.
            if (file.isFile() && "globalclasspath".equals(file.getParentFile().getName())) {
                try {
                    urls.add(file.toURI().toURL());
                } catch (MalformedURLException e) {
                    // This should never happen.
                    e.printStackTrace();
                }
            }
        }
        return new URLClassLoader(urls.toArray(new URL[urls.size()]));
    }

    public static void main(String[] args) {
        URLClassLoader classLoader = createURLClassLoader();
        System.out.println(classLoader.getResource("mine.properties"));
    }
}

我运行了以下命令:
java -cp globalclasspath:daemons/service.jar MyClass

终端输出:
jar:file:/workspace/all/globalclasspath/data.jar!/mine.properties

2

抱歉,我实际上已经在使用getResourceAsStream了(我写getResource是因为我不知道有什么区别,而且它似乎更简单)。 - Dave

0

现在您可以使用maven-resources-plugin在jar之间共享资源!

在源模块/ jar的pom.xml文件中,添加以下插件:

<artifactId>DATA_MODULE_NAME</artifactId>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
        </plugin>
    </plugins>
</build>

这将告诉Maven将资源文件夹的内容与jar一起复制。

在您想要访问这些资源的模块中,在pom的依赖项中添加以下内容:

<dependencies>
    <dependency>
        <groupId>${project.groupId}</groupId>
        <artifactId>DATA_MODULE_NAME</artifactId>
        <version>${project.version}</version>
    </dependency>
</dependencies>

当您构建最终的jar包时,源模块中的资源将被复制并可通过getResource()使用。


2
我认为原始需求(尽管很久以前!)是能够从一个“独立”的jar文件中加载数据;不是编译的一部分(例如,程序在一个jar中,数据jar在编译后但启动前移动到正确的位置)。这样做的想法是可以频繁更新数据而无需重新编译。但是,也很好知道,在编译时捆绑资源的相关用例有可能有解决方案。 - Dave

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接