2013-02-18 3 views
3

Я беспокоюсь о ситуации, когда библиотеки Foo и Bar каждый раскрывает ресурс в пути к одному и тому же имени, скажем properties.txt.Как избежать конфликтов ресурсов в библиотечных баночках?

Предполагая Maven созданы и jars развернуты с Maven, если я это установить:

Library Foo:

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

и библиотека Бар:

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

И a App, который зависит от них, pom выглядит примерно так: в двух словах это просто говорит: «Создайте jar-with-dependencies и d epend на Foo и Bar:

<plugin> 
    <groupId>org.apache.maven.plugins</groupId> 
    <artifactId>maven-assembly-plugin</artifactId> 
    <executions> 
    <execution> 
     <id>bundle-project-sources</id> 
     <phase>package</phase> 
     <goals> 
     <goal>single</goal> 
     </goals> 
     <configuration> 
     <archive> 
      <manifest> 
      <mainClass>me.unroll.deptest.App</mainClass> 
      <addDefaultImplementationEntries>true</addDefaultImplementationEntries> 
      </manifest> 
      <manifestEntries> 
      <Implementation-Build>${buildNumber}</Implementation-Build> 
      </manifestEntries> 
     </archive> 
     <descriptorRefs> 
      <descriptorRef>jar-with-dependencies</descriptorRef> 
     </descriptorRefs> 
     </configuration> 
    </execution> 
    </executions> 
</plugin> 

Проблема заключается в том, что на самом деле кажется, что файл 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 

Так что я побежал main класс в App, что делает:

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-App. Никаких предупреждений или ошибок при запуске, что неправильный файл, возможно, был выбран, на самом деле он потерпел неудачу молча.

Так что я хочу спросить правильную практику, чтобы избежать этого. Во-первых, что-то подобное должно терпеть неудачу громко, а не мягко. Во-вторых, единственным решением, которое я могу думать, является то, что все файлы ресурсов должны быть правильно упакованы, как и все остальное в Java-разработке, т. Е. Библиотека никогда не должна выставлять properties.txt в «глобальное» пространство имен; он должен появиться в папке me/unroll/deptest/foo, как и все остальное. Я скептически отношусь к тому, что я не видел примера Maven, который на самом деле это делает. Так что же лучше всего здесь?

ответ

3

И что вы делаете в 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() по умолчанию локального по отношению к пакету базового класса.Конечно, когда вы находитесь внутри метода экземпляра Foo или Bar, вы просто говорите getClass().getResourceAsStream("properties.txt"). Кроме того вы можете ссылаться как файлы легко, так же, как вы ссылаться на классы:

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

Я скептически, потому что я не видел ни одного примера Maven, что на самом деле это делает.

Пример реального мира: у вас есть тест интеграции весны под названием com.example.foo.FooTest. По умолчанию Spring ожидает, что файл контекста будет находиться ниже: /src/test/resources/com/example/foo/FooTest-context.xml.

+1

* '/ src/test/resources'? – djechlin

+0

@djechlin: спасибо! –

+0

Итак, обратите внимание, что мой проект включает в себя пакет 'App', извлекающий ресурс в пакете' Bar'. Таким образом, на самом деле нужно было бы вызвать «App.class.getResourceAsStream» («/ me/unroll/bar/properties.txt») ', если namespaced этот путь, или' App.class.getClassLoader(). GetResourceAsStream ("me/unroll/bar /properties.txt ")'. Это правильная/хорошая практика? – djechlin

Смежные вопросы