如何避免库jar中的资源冲突?

我很担心库FooBar每个都在类路径上公开一个具有相同名称的资源,比如本例中的properties.txt

假设一个Maven设置并且使用Maven部署了jars ,如果我有这个设置:

图书馆Foo:

 $ cat Foo/src/main/resources/properties.txt $ Foo 

和图书馆酒吧:

 $ cat Bar/src/main/resources/properties.txt $ Bar 

还有一个依赖于它们的App ,其pom看起来像这样 – 简而言之,这只是说“构建一个依赖于jar的jar,依赖于FooBar

  org.apache.maven.plugins maven-assembly-plugin   bundle-project-sources package  single     me.unroll.deptest.App true   ${buildNumber}    jar-with-dependencies      

问题是看起来好像是properties.txt文件被破坏了。 让我们尝试一下jar tf

 unrollme-dev-dan:target Dan$ jar tf App-1.0-SNAPSHOT-jar-with-dependencies.jar META-INF/ META-INF/MANIFEST.MF properties.txt META-INF/maven/ META-INF/maven/me.unroll.deptest/ META-INF/maven/me.unroll.deptest/Bar/ META-INF/maven/me.unroll.deptest/Bar/pom.xml META-INF/maven/me.unroll.deptest/Bar/pom.properties META-INF/maven/me.unroll.deptest/Foo/ META-INF/maven/me.unroll.deptest/Foo/pom.xml META-INF/maven/me.unroll.deptest/Foo/pom.properties me/ me/unroll/ me/unroll/deptest/ me/unroll/deptest/App.class 

所以我在App中运行了一个main类:

 try (InputStream is = App.class.getClassLoader().getResourceAsStream("properties.txt")) { java.util.Scanner s = new java.util.Scanner(is); System.out.println("Scanner: " + s.next()); } 

输出是:

 unrollme-dev-dan:target Dan$ java -jar App-1.0-SNAPSHOT-jar-with-dependencies.jar Scanner: Bar 

哎呀,酒吧赢了。 mvn package -ing App时没有警告或错误。 运行时可能没有选择错误文件的警告或错误,实际上它无声地失败。

所以我想要求适当的练习来避免这种情况。 一,这样的事情应该大声失败,而不是轻轻地失败。 第二,我能想到的唯一解决方案是所有资源文件都应该像Java开发中的其他所有资源一样正确打包,即库永远不会在“全局”命名空间中公开properties.txt ; 它应该出现在像me/unroll/deptest/foo这样的文件夹中,就像其他一切一样。 我持怀疑态度,因为我没有看到任何实际上这样做的Maven示例。 那么这里的最佳做法是什么?

你用Java做什么来避免库之间的冲突? 套餐! 这是已建立并且易于理解的方法。 包也适用于资源:

 com/example/foo/Foo.class com/example/foo/properties.txt 

和第二个图书馆:

 com/example/bar/Bar.class com/example/bar/properties.txt 

请注意, properties.txt位于不同的包中,因此位于最终JAR中的目录中。 实际上这种方法是首选,因为用于检索此类资源的API变得更容易:

 App.class.getResourceAsStream("properties.txt")) 

 Bar.class.getResourceAsStream("properties.txt")) 

它只是起作用,因为Class.getResourceAsStream()默认是本地的基础类包。 当然,当你在FooBar的实例方法中时,你只需要说getClass().getResourceAsStream("properties.txt") 。 此外,您仍然可以轻松引用这两个文件,就像您引用类一样:

 getClass().getResourceAsStream("/com/example/foo/properties.txt"); getClass().getResourceAsStream("/com/example/bar/properties.txt"); 

我持怀疑态度,因为我没有看到任何实际上这样做的Maven示例。

真实世界的例子:你有一个名为com.example.foo.FooTest的Spring集成测试。 默认情况下,Spring期望上下文文件位于: /src/test/resources/com/example/foo/FooTest-context.xml