Как мне поместить все необходимые файлы JAR в папку библиотеки внутри окончательного файла JAR с Maven?

105

Я использую Maven в своем автономном приложении и хочу упаковать все зависимости в моем файле JAR в папку библиотеки, как указано в одном из ответов здесь:

Как я могу создать исполняемый JAR с зависимостями с помощью Maven?

Я хочу, чтобы в моем последнем файле JAR была папка библиотеки, содержащая зависимости в виде файлов JAR, а не то, maven-shade-pluginчто помещает зависимости в виде папок, таких как иерархия Maven в папке .m2.

На самом деле текущая конфигурация делает то, что я хочу, но у меня проблема с загрузкой файлов JAR при запуске приложения. Я не могу загрузить классы.

Вот моя конфигурация:

<plugins>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
            <execution>
                <id>copy-dependencies</id>
                <phase>prepare-package</phase>
                <goals>
                    <goal>copy-dependencies</goal>
                </goals>
                <configuration>
                    <outputDirectory>${project.build.directory}/classes/lib</outputDirectory>
                    <overWriteReleases>false</overWriteReleases>
                    <overWriteSnapshots>false</overWriteSnapshots>
                    <overWriteIfNewer>true</overWriteIfNewer>
                </configuration>
            </execution>
        </executions>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
            <archive>
                <manifest>
                    <addClasspath>true</addClasspath>
                    <classpathPrefix>lib/</classpathPrefix>
                    <mainClass>com.myapp.MainClass</mainClass>
                </manifest>
            </archive>
        </configuration>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
            <source>1.6</source>
            <target>1.6</target>
        </configuration>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
            <execution>
                <id>install</id>
                <phase>install</phase>
                <goals>
                    <goal>sources</goal>
                </goals>
            </execution>
        </executions>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>2.5</version>
        <configuration>
            <encoding>UTF-8</encoding>
        </configuration>
    </plugin>

</plugins>

Проект отлично работает в Eclipse, и файлы JAR помещаются в папку библиотеки внутри моего последнего файла JAR, как я хочу, но при запуске последнего файла JAR из целевой папки я всегда получаю ClassNotFoundException:

Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContext
Caused by: java.lang.ClassNotFoundException: org.springframework.context.ApplicationContext
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
Could not find the main class: com.myapp.MainClass. Program will exit.

Как я могу исправить это исключение?

Махмуд Салех
источник
1
какую команду вы используете для запуска jar? возможно, вы можете предпочесть плагин maven exec?
Андрей Борисов
Сообщение об исключении устарело по сравнению с файлом POM? Похоже, что основной класс com.myapp.MainClassищется, а не com.tastycafe.MainClass.
Дункан Джонс
@Duncan Jones, проблема с копировальной пастой, я редактировал вопрос
Махмуд Салех
3
Обратите внимание: если вы хотите использовать jar-файлы внутри jar, то стандартные загрузчики классов в Java не смогут их понять.
Торбьёрн Равн Андерсен
Как заставить его разместить зависимости maven в папке lib, но за пределами JAR?
ed22 02

Ответы:

82

Вот мое решение. Проверьте, работает ли он для вас:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.build.directory}/classes/lib</outputDirectory>
                <overWriteReleases>false</overWriteReleases>
                <overWriteSnapshots>false</overWriteSnapshots>
                <overWriteIfNewer>true</overWriteIfNewer>
            </configuration>
        </execution>
    </executions>
</plugin>

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <!-- <classpathPrefix>lib</classpathPrefix> -->
                <!-- <mainClass>test.org.Cliente</mainClass> -->
            </manifest>
            <manifestEntries>
                <Class-Path>lib/</Class-Path>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>

Первый плагин помещает все зависимости в папку target / classes / lib, а второй включает папку библиотеки в окончательный файл JAR и настраивает Manifest.mfфайл.

Но тогда вам нужно будет добавить собственный код загрузки классов для загрузки файлов JAR.

Или, чтобы избежать пользовательской загрузки классов, вы можете использовать "$ {project.build.directory} / lib, но в этом случае у вас нет зависимостей внутри окончательного файла JAR, что противоречит цели.

Прошло два года с тех пор, как был задан вопрос. Тем не менее проблема вложенных файлов JAR сохраняется. Надеюсь, это кому-то поможет.

gmode
источник
1
Использование: mvn install, cd target, java -jar MyJarFile-1.0.jar
djb
что я скучаю? Это создает запись пути к классам манифеста, содержащую «lib /» и все отдельные jar-файлы из папки lib. Это предназначено? Зачем?
gapvision
2
Это сработало после того, как я изменил "$ {project.build.directory} / classes / lib" на $ {project.build.directory} / lib
Раджендра Торат
1
К сожалению, включает в себя зависимости, заявленные как предоставленные.
Philippe Gioseffi
@Rajendra, спасибо, это помогло: $ {project.build.directory} / lib
Миндаугас К.
31

Обновлено:

<build> 
  <plugins> 
    <plugin> 
    <artifactId>maven-dependency-plugin</artifactId> 
    <executions> 
      <execution> 
        <phase>install</phase> 
          <goals> 
            <goal>copy-dependencies</goal> 
          </goals> 
          <configuration> 
             <outputDirectory>${project.build.directory}/lib</outputDirectory> 
          </configuration> 
        </execution> 
      </executions> 
    </plugin> 
  </plugins> 
</build> 
Ахмет Каракая
источник
4
это помещает папку lib за пределы банки (это не то, что я хочу). это лучше, чем поместить библиотеку в банку? а как в этом случае поставить приложение клиенту?
Махмуд Салех,
теперь мне стало понятнее. позвольте мне сделать поиск в Google. Но я хотел бы знать, почему вы хотите скопировать весь файл jar зависимостей в указанную папку внутри исполняемого файла jar. Если все файлы jar зависимостей находятся внутри файла jar, почему вам нужно размещать их в папке lib?
Ахмет Каракая
23

Самый простой и эффективный способ - использовать такой плагин uber:

          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <finalName>uber-${artifactId}-${version}</finalName>
            </configuration>
        </plugin>

У вас все будет денормализовано в одном JAR-файле.

Андрей Борисов
источник
использовать его в дополнение к моей текущей конфигурации? и что именно делает этот плагин?
Махмуд Салех
15
Я не хочу, чтобы мой файл jar выглядел так, я хочу, чтобы все зависимости были в папке lib внутри файла jar, как это делает netbean.
Махмуд Салех
12

Упаковщик исполняемых Maven плагин может быть использован именно для этой цели: создание автономного Java - приложений , содержащих всю зависимость , как JAR - файлы в папке конкретной.

Просто добавьте следующую строку в ваш pom.xmlвнутри <build><plugins>секции (не забудьте заменить значение mainClassсоответственно):

<plugin>
    <groupId>de.ntcomputer</groupId>
    <artifactId>executable-packer-maven-plugin</artifactId>
    <version>1.0.1</version>
    <configuration>
        <mainClass>com.example.MyMainClass</mainClass>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>pack-executable-jar</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Созданный файл JAR target/<YourProjectAndVersion>-pkg.jarпосле запуска находится в mvn package. Все его зависимости времени компиляции и выполнения будут включены в lib/папку внутри файла JAR.

Отказ от ответственности: я являюсь автором плагина.

Кибран
источник
Я попробовал и могу сказать, что он работает ... Как вы сказали, этот плагин создает файл jar со своими библиотеками (jar) в каталоге lib в jar ... Конфигурация действительно проста .. Я думаю, что это то, что автор этого вопроса ожидает ... Я отдам вам свой голос ... Единственный недостаток, который я нашел в нем (поэтому я не мог его использовать), есть задержка, когда я выполняю свой jar и приложение отображается (около 20 секунд), вероятно, из-за процесса регистрации библиотек в пользовательском загрузчике классов ... Но это отличный подход и отличный плагин ...
Адам М. Гамбоа Г.
3

Вот как я это делаю:

    <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <appendAssemblyId>false</appendAssemblyId>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <archive>
                    <manifest>
                        <mainClass>com.project.MainClass</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>

А потом просто бегу:

mvn assembly:assembly
Эдуардо Андраде
источник
1
Обратите внимание, что вы всегда должны выполнять компиляцию перед рукой, потому что assemblyвы просто поместите все, что находится в "target / classes" в JAR. Это гарантирует, что JAR будет включать все изменения, которые вы недавно внесли в исходный код. Таким образом, вы должны сделать что - то вроде: mvn clean compile assembly:assembly.
naXa
2

Я нашел такой ответ на вопрос:

http://padcom13.blogspot.co.uk/2011/10/creating-standalone-applications-with.html

Вы не только получаете зависимые файлы lib в папке lib, но и получаете директорию bin с исполняемыми файлами unix и dos.

В конечном итоге исполняемый файл вызывает java с аргументом -cp, который также перечисляет все ваши зависимые библиотеки.

Вся партия находится в папке appasembly внутри целевой папки. Эпично.

============= Да, я знаю, что это старая ветка, но она все еще занимает высокие позиции в результатах поиска, поэтому я подумал, что это может помочь кому-то вроде меня.

user4815755
источник
1

Это явно проблема пути к классам. Учтите, что путь к классам должен немного измениться, когда вы запускаете свою программу вне IDE. Это связано с тем, что среда IDE загружает другие файлы JAR относительно корневой папки вашего проекта, тогда как в случае окончательного JAR это обычно неверно.

Что мне нравится делать в таких ситуациях, так это создавать JAR вручную. На это у меня уходит максимум 5 минут, и это всегда решает проблему. Я не предлагаю вам этого делать. Найдите способ использовать Maven, в этом его цель.

Раду Мурзея
источник
@SoboLAN Создание JAR вручную здесь не является решением. Намерение состоит в том, чтобы использовать Maven, что является полной противоположностью "вручную"!
Дункан Джонс
@DuncanJones Вы совершенно правы. Я предлагаю, чтобы он использовал для этого Maven. Однако у меня нет опыта с этим и я не знал, какое решение порекомендовать. Я отредактировал свой ответ, чтобы отразить это.
Radu Murzea 01