Этот пост будет нацелен на интересную, но довольно мощную концепцию: упаковывать ваше приложение в один исполняемый файл JAR, также известный как один или толстый JAR.
Мы привыкаем к большим архивам WAR, которые содержат все зависимости, упакованные вместе в какую-то общую структуру папок. С JAR-подобной упаковкой история немного другая: для того, чтобы сделать ваше приложение работоспособным (через java -jar ), все зависимости должны быть предоставлены через параметр classpath или переменную среды. Обычно это означает, что там будет какая-нибудь папка lib со всеми зависимостями и какой-нибудь исполняемый скрипт, который будет выполнять работу по созданию classpath и запуску JVM. Плагин Maven Assembly хорошо известен для распространения приложений такого рода.
Несколько иной подход заключается в том, чтобы упаковать все зависимости вашего приложения в один и тот же файл JAR и сделать его работоспособным без каких-либо дополнительных параметров или сценариев. Звучит здорово, но … это не сработает, если вы не добавите немного волшебства: познакомьтесь с проектом One-JAR .
Давайте кратко опишем проблему: мы пишем отдельное приложение Spring, которое можно запустить, просто набрав java -jar <our-app.jar> .
Как всегда, давайте начнем с нашего файла POM, который будет довольно простым
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelversion>4.0.0</modelversion> <groupid>com.example</groupid> <artifactid>spring-one-jar</artifactid> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>spring-one-jar</name> <properties> <project.build.sourceencoding>UTF-8</project.build.sourceencoding> <org.springframework.version>3.1.1.RELEASE</org.springframework.version> </properties> <dependencies> <dependency> <groupid>cglib</groupid> <artifactid>cglib-nodep</artifactid> <version>2.2</version> </dependency> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-core</artifactid> <version>${org.springframework.version}</version> </dependency> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-context</artifactid> <version>${org.springframework.version}</version> </dependency> </dependencies></project> |
Наше примерное приложение загрузит контекст Spring , получит некоторый экземпляр компонента и вызовет для него метод. Наш компонент называется SimpleBean и выглядит так:
|
1
2
3
4
5
6
|
package com.example;public class SimpleBean { public void print() { System.out.println( 'Called from single JAR!' ); }} |
Влюбившись в конфигурацию Spring Java, давайте определим наш контекст как аннотированный AppConfig POJO:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
package com.example.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import com.example.SimpleBean;@Configurationpublic class AppConfig { @Bean public SimpleBean simpleBean() { return new SimpleBean(); }} |
И, наконец, наше приложение Starter с main () :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
package com.example;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import com.example.config.AppConfig;public class Starter { public static void main( final String[] args ) { ApplicationContext context = new AnnotationConfigApplicationContext( AppConfig.class ); SimpleBean bean = context.getBean( SimpleBean.class ); bean.print(); }} |
Добавление нашего основного класса в META-INF / MANIFEST.MF позволяет использовать возможности Java для запуска файла JAR без явного указания класса с помощью метода main () . Плагин Maven JAR может помочь нам в этом.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<build> <plugins> <plugin> <groupid>org.apache.maven.plugins</groupid> <artifactid>maven-jar-plugin</artifactid> <configuration> <archive> <manifest> <mainclass>com.example.Starter</mainclass> </manifest> </archive> </configuration> </plugin> </plugins></build> |
При попытке запустить java -jar spring-one-jar-0.0.1-SNAPSHOT.jar в консоль будет выведено исключение: java.lang.NoClassDefFoundError . Причина довольно проста: даже такое простое приложение, как это, уже требует, чтобы следующие библиотеки находились в пути к классам.
|
1
2
3
4
5
6
7
8
9
|
aopalliance-1.0.jarcglib-nodep-2.2.jarcommons-logging-1.1.1.jarspring-aop-3.1.1.RELEASE.jarspring-asm-3.1.1.RELEASE.jarspring-beans-3.1.1.RELEASE.jarspring-context-3.1.1.RELEASE.jarspring-core-3.1.1.RELEASE.jarspring-expression-3.1.1.RELEASE.jar |
Давайте посмотрим, что One-JAR может сделать для нас здесь. Благодаря наличию onejar-maven-plugin мы можем добавить его в раздел плагинов нашего POM-файла.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
<plugin> <groupid>org.dstovall</groupid> <artifactid>onejar-maven-plugin</artifactid> <version>1.4.4</version> <executions> <execution> <configuration> <onejarversion>0.97</onejarversion> <classifier>onejar</classifier> </configuration> <goals> <goal>one-jar</goal> </goals> </execution> </executions></plugin> |
Кроме того, раздел pluginRepositories должен содержать этот репозиторий для загрузки плагина.
|
1
2
3
4
5
6
|
<pluginrepositories> <pluginrepository> <id>onejar-maven-plugin.googlecode.com</id> </pluginrepository></pluginrepositories> |
В результате в целевой папке будет доступен другой артефакт с постфиксом one-jar : spring-one-jar-0.0.1-SNAPSHOT.one-jar.jar . Запуск этого с java -jar spring-one-jar-0.0.1-SNAPSHOT.one-jar.jar выведет на консоль:
|
1
|
Called from single JAR! |
Полностью запускаемое Java-приложение в виде одного распространяемого JAR- файла! Последний комментарий: хотя наше приложение выглядит довольно просто, One-JAR отлично работает для сложных, больших приложений, а также без каких-либо проблем. Пожалуйста, добавьте его в свой набор инструментов, это действительно полезный инструмент.
Спасибо One-JAR, ребята!
Ссылка: простая, но мощная концепция: упаковка вашего Java-приложения в один (или толстый) JAR от нашего партнера по JCG Андрея Редько в блоге Андрея Редько {devmind} .