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

Maven and Eclipse : loading default properties in maven library project and use it in runnable Jar

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

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

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

只是为了快速概述 workspace/projects 如上所述的设置:

两个项目的项目结构都反映了 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 文件,chatclient 项目包含 custom.properties 文件。

构建 运行 可用的 JAR 文件后,它具有以下结构。

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

我认为资源文件不应该放在那里。但在 META-INF/maven 文件夹中。在尝试了类似的东西之后:

但似乎没有任何效果。我猜它与 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 --

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

更新 (28-04-2015 {1})

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

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

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

更新 (28-04-2015 {2})

好吧,我设法解决了部分问题。 由于我添加了 maven-assembly-plugin 的配置(使用 deps 构建 运行nable 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>

然后当 运行ning 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 的答案没有贡献。它确实提供了一些关于最佳实践的重要信息,但不如公认的答案那么完整。所以当你在这里找到你的答案时,请也给他一些功劳。

这个问题的解决方案更简单,但是我希望您了解几个概念。

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

  2. 你肯定不想把3PP罐子拆开打包成自己的罐子吧!

  3. 基于以上所述,您可能想知道如何创建可执行 jar。好吧,答案是在 class-path 中有依赖的 jar。当执行"project" jar 时,你应该在class-path 中有"library" jar,它将能够按预期找到里面的属性文件。

  4. 您将属性文件放在 META-INF 目录中。它们的正确位置是资源文件夹。如果您遵循第 1 点,事情将按预期进行。

获取资源的方式有两种,

  • 使用绝对路径(没有 /...)对整个 class 路径使用 ClassLoader,
  • with a class,在 class.
  • 的 jar 中使用相对 (...) 或绝对 (/...) 路径

后者看起来更直接,可以用作:

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

现在 getClass() 只能在 non-static object 中使用并且 也很危险 :实际的 class 可能是一些 child,不在库 jar 中。


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

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

where /... 进入 jar/war;包目录。

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