Maven 和 Eclipse:在 Maven 库项目中加载默认属性并在可运行的 Jar 中使用它

Posted

技术标签:

【中文标题】Maven 和 Eclipse:在 Maven 库项目中加载默认属性并在可运行的 Jar 中使用它【英文标题】:Maven and Eclipse : loading default properties in maven library project and use it in runnable Jar 【发布时间】:2015-07-02 20:37:46 【问题描述】:

我相信之前在 *** 上已经问过这个问题。我想提一下,我尝试了与我的问题相关的解决方案。最接近我的问题的是: Load properties file in JAR?. 遗憾的是,那里描述的解决方案对我不起作用。而且由于问题的年龄,我认为再问一次是要走的路。

开始描述我的问题。 因此,目前我正在开发一个已使用 maven 设置并为当前 Spring AMQP 项目创建扩展的库项目。 这里的目标是提供一个 JAR 文件,该文件可以包含在另一个项目中,以支持通过消息代理进行通信的特定方式。 此时,我正在实施配置选项以允许用户根据自己的喜好配置消息传递客户端。但是,当我在测试此功能的功能时,我在可执行 JAR 中使用该库时遇到了问题。

在 Eclipse 工作区中运行它时,一切似乎都运行良好。但是当我尝试从桌面运行它(作为可运行的 JAR)时,似乎在任何地方都找不到属性文件。

只是为了快速概述如上所述的工作区/项目设置:

两个项目的项目结构都反映了Maven默认的一个:

- src/main/java
    - java source files
- src/main/resources
    - resource files
- src/test/java
    - java test files
- src/test/resources
    - test resource files

其中库文件在 src/main/resources 文件夹中包含一个 default.properties 文件,而聊天客户端项目包含一个 custom.properties 文件。

一旦构建了可运行的 JAR 文件,它就具有以下结构。

- com
- junit
- META-INF
- org
- resources
    - default.resources
    - custom.resources

我认为资源文件不应位于那里。但在 META-INF/maven 文件夹中。在尝试了以下内容后:

将 META-INF 文件夹添加到我的 src/main/resources 文件夹中,并将属性文件放在那里。 添加一个包含 Class-Path: . 的 MANIFEST 文件。 在代码中以多种方式加载文件。

但似乎没有任何效果。我猜它与 Maven 相关,对 pom.xml 的简单更改可以解决它。遗憾的是,我对 Maven 项目设置和 pom 相关主题的了解非常基础(这是我第一个使用 maven 的项目)。而且我似乎找不到任何关于它的文档,即使我知道它应该在那里(可能是我造成的问题)。

在我忘记提及之前。我使用这种方式加载属性文件:

Properties props = new Properties();
prop.load(<custom static class>.class.getResourceAsStream(filename));
return props;

我的库的 pom.xml 看起来像:

-- Artifact stuff --
<packaging>jar</packaging>
<build>
  <plugins>
    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.1</version>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>
  </plugins>
</build>
-- Dependency stuff --

使用该库的项目看起来像:

-- Artifact stuff --
<build>
  <plugins>
    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.1</version>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>
  </plugins>
</build>

<dependencies>
  <dependency>
    <groupId>com.maxxton</groupId>
    <artifactId>async-amqp-messaging</artifactId>
    <version>0.2</version>
  </dependency>
</dependencies>
-- Other stuff --

我希望有人在这个主题上更先进,可以帮助找到解决这个问题的方法。如果您需要有关项目文件/结构的任何其他信息,请告诉我。我很乐意与您分享。

更新(2015 年 4 月 28 日1)

为了测试,我创建了一个示例项目,它尝试以与上述场景相同的方式加载属性文件。 即使在关注Maven documentation(使用 META-INF 文件夹)时,我也无法加载属性。

为了这个问题,我上传了the testing workspace here。

我希望有人能帮我解决这个问题,因为 Maven 网站上描述的正常方法似乎对我不起作用。

更新(2015 年 4 月 28 日 2)

好吧,我设法解决了部分问题。 由于我添加了 maven-assembly-plugin 的配置(使用 deps 构建可运行的 JAR),我能够在我的 JAR 文件中获得正确的结构。 我添加的是:

<build>
    <plugins>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <finalName>project</finalName>
                <appendAssemblyId>false</appendAssemblyId>
                <archive>
                    <manifest>
                        <mainClass>com.test.project.Application</mainClass>
                    </manifest>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

然后在运行 clean compile assembly:single 时,我设法得到了正确的结构。

JAR root
    - com
    - META-INF
        - MANIFEST.MF
        - default.properties
        - custom.properties

虽然这解决了部分问题。文件加载仍然会导致 NullPointerException。

最终更新 (04-05-2015)

经过长时间的 Maven 斗争,我设法得到了我想要的一切。 根据@Deepak 和@Joop Eggen 给出的建议,我对如何将lib 文件夹中的所有依赖项作为jar 而不是将它们解压缩到“uber”jar 中进行了一些研究。在尝试了很多东西后,我偶然发现了this answer。按照说明,似乎创建了这个结构:

- runnable.jar
- lib
    - spring-amqp.jar
    - spring-core.jar
    ...

按照@Joop Eggen 的建议,我设法按照我想要的方式加载了该属性。所以看来这个问题已经回答了。目前我仍在研究如何奖励每个答案,因为我无法将赏金分成两部分。我会回来的。

旁注

虽然我将赏金和答案都授予了@Joop Eggen,但这并不意味着@Deepak 的答案没有贡献。它确实提供了一些关于最佳实践的重要信息,但不如公认的答案那么完整。所以,当你在这里找到你的答案时,也请给他一些功劳。

【问题讨论】:

你的 JAR 结构在我看来是错误的。 是的。但我想为了修复它,我需要更改我的 Maven pom.xml 中的一些设置。毕竟这定义了 JAR 结构的生成方式。还是我错了? 找出你的 JAR 的根指向什么,然后从那里开始。我的怀疑是它没有指向你的想法。 spring-boot 提供了一个插件,可以创建一个可运行的 jar。看@***.com/a/29040063/3985566。看看它是否有帮助。 好的,谢谢,我会调查的。但我真的希望这个问题不要关注任何与框架相关的解决方案。 【参考方案1】:

有两种获取资源的方式,

使用绝对路径(不带/...)针对整个类路径使用ClassLoader, 带有一个类,在该类的 jar 中使用相对 (...) 或绝对 (/...) 路径。

后者似乎更直接,可以用作:

getClass().getResource("/...");
ClassInJar.class.getResource("/...");

现在getClass() 只能在非静态对象中使用,并且也危险:实际的类可能是某个子类,而不是在库 jar 中。


关于您的应用程序的实际结构。我知道一个 Maven 目录约定:

src/main/java/...
src/main/resources/...

/... 进入罐子/战争的地方;包目录。

重新组装罐子不好。 META-INF/MANIFEST.MF 中始终存在Class-Path: ... 条目。最好遵循基本的 Maven 约定。

【讨论】:

好的,感谢您与我分享您的解决方案。 @Deepak 答案有一些重叠点。所以我猜这是一件好事。我想知道的一件事是您将资源放置在哪里。你会将它们放在 META-INF 文件夹中还是放在 JAR 文件的底部? 不在 META-INF 中。根文件夹没有错。 META-INF 具有 JVM 功能:不仅是清单,还有类索引或 Java SPI 接口查找文件。【参考方案2】:

解决这个问题的方法比较简单,但是我希望您注意几个概念。

    使用 maven 程序集插件创建一个大 jar 不是一个好习惯。在您的示例中,您使用的是由您开发的小罐子,所以看起来还可以。但是随着您的实际项目变得越来越大,这并不理想,您需要分离不同的模块而不是一个大罐子。理想的做法与你想要达到的目标相反——你应该以拥有越来越小的罐子为目标。将来您可能希望方便地更换这些较小的罐子,而不必再次交付整个包裹。

    您肯定不想解压 3PP jar 并将它们打包为您自己的 jar!

    基于以上内容,您可能想知道如何创建可执行 jar。好吧,答案是在类路径中有依赖的 jar。执行“项目”jar 时,您应该在类路径中有“库”jar,它将能够按预期找到其中的属性文件。

    您将属性文件放在 META-INF 目录中。适合他们的地方是资源文件夹。如果您遵循第 1 点,一切都会按预期进行。

【讨论】:

这似乎是一个很好的建议,即依赖项 jar 应该与可运行的 JAR 分开。我会记住这一点。属性在 META-INF 文件夹中的原因与 Maven 文档有关。它指出资源应该放在那里。也许他们这样做是为了防止“破坏” JAR 结构。 不,作为一种良好做法,资源应保存在资源文件夹或资源本身内的任何文件夹中。如果您觉得它有用,请接受答案。谢谢。 好的,谢谢。好吧,答案对于最佳实践的东西很有用,尽管它只是在创建解决方案的方向上给出了提示。请耐心等待,因为我想看看其他人对此问题的意见/解决方案。

以上是关于Maven 和 Eclipse:在 Maven 库项目中加载默认属性并在可运行的 Jar 中使用它的主要内容,如果未能解决你的问题,请参考以下文章

尝试在 Eclipse 中添加谷歌视觉教程库时出现 Maven 错误

如何在嵌入 Eclipse 的 Maven 存储库中安装 jars?

在 Eclipse 中为项目添加 Maven 存储库?

通过 Maven / Eclipse 导入 Spotify API 客户端库

eclipse怎么设置maven仓库地址

如何在 Eclipse 中更新 Maven 存储库?