Источник: Mccue.dev
Сегодня вы узнаете, как создать из Java-программы исполняемый EXE-файл для запуска в операционной системе Windows.
Двойной щелчок для запуска — один из самых простых способов открыть программу. Если у человека, которому вы хотите показать свое приложение, уже установлена правильная версия Java, для запуска он может дважды щелкнуть файл jar. Если же у него не установлена Java, то есть способы создать исполняемый установщик, такой как jpackage. После этого для запуска кода нужно лишь нажать на этот установщик. Также можно использовать Native Image, чтобы превратить код в исполняемый файл, который не требует какой-либо дополнительной установки.
В этой статье мы сосредоточимся на довольно простом подходе, который работает для любого приложения, независимо от того, какие зависимости вы включаете или какие функции JVM используете.
Код, о котором сегодня пойдет речь, можно найти в репозитории GitHub, а исполняемые файлы с программой выложены здесь.
Используемый стек
Java 9+
java --version
jlink --version
Maven
mvn --version
NodeJS
npx --version
Шаг 1. Скомпилируйте и упакуйте свой код в jar
Эта базовая программа создаст простое окно с текстом, который вы можете менять, нажимая на одну из кнопок в интерфейсе.
package example;
import org.apache.commons.text.WordUtils;
import javax.swing.*;
import java.awt.*;
public class Main {
public static void main(String[] args) {
var label = new JLabel("Hello, World!");
label.setFont(new Font("Serif", Font.PLAIN, 72));
var uppercaseButton = new JButton("Uppercase");
uppercaseButton.addActionListener(e ->
label.setText(WordUtils.capitalize(label.getText()))
);
var lowercaseButton = new JButton("lowercase");
lowercaseButton.addActionListener(e ->
label.setText(WordUtils.uncapitalize(label.getText()))
);
var panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(label);
panel.add(uppercaseButton);
panel.add(lowercaseButton);
var frame = new JFrame("Basic Program");
frame.add(panel);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}
Сейчас наша цель состоит в том, чтобы упаковать код вместе с его зависимостями в jar. JAR-файлы — это обычные ZIP-архивы с небольшой дополнительной структурой.
Для проекта Maven конфигурация будет выглядеть следующим образом.
<?xml version="1.0" encoding="UTF-8"?>
<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>example</groupId>
<artifactId>javaexe</artifactId>
<version>1.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>18</maven.compiler.source>
<maven.compiler.target>18</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.9</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>example.Main</Main-Class>
<Build-Number>1.0</Build-Number>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Здесь плагин “shade” будет обрабатывать включение кода из всех ваших зависимостей в jar. В данном случае единственной внешней зависимостью является org.apache.commons/commons-text.
mvn clean package
Затем мы переместим этот jar-файл в новый каталог target/, где он будет отделен от других файлов.
mkdir build
mv target/javaexe-1.0.jar build
Шаг 2. Создайте среду выполнения Java (Java Runtime Environment, JRE)
Чтобы запустить уже созданный нами jar-файл, нужно связать его со средой выполнения Java. Для этого мы будем использовать jlink. Поскольку в экосистеме Java не используются модули, то вы, скорее всего, не слышали о них и не использовали jlink. Короче говоря, jlink может создавать “настраиваемые исполняемые образы”. Например, вы делаете веб-сервер. Вам не нужны AWT или Swing, поэтому включать их в код будет лишним. С помощью jlink вы можете создать JRE, которая вообще не включает модуль java.desktop. Эта система работает лучше всего, если ваше приложение и все его зависимости включают скомпилированные файлы module-info.java, которые дают jlink точную информацию, какие модули вы хотите включить. Вы также можете вручную определить список необходимых модулей, используя jdeps. И даже без модульного проекта мы можем эффективно клонировать нашу инсталляцию Java в каталог с помощью jlink.jlink --add-modules ALL-MODULE-PATH --output build/runtime
Включение каждого модуля по отдельности дает уверенность в том, что такие библиотеки как org.apache.commons/commons-text будут работать именно так, как задумано. Нужно лишь выяснить, какие модули нам требуются.
Шаг 3. Объедините Jar и JRE в исполняемый файл
Имея jar-файл, содержащий код и все его зависимости, а также JRE, остается только объединить их. Для этого нам нужно выполнить следующие действия:- Заархивируйте каталог, содержащий JRE и jar вашего приложения.
- Прикрепите сценарий-заглушку (stub script) к верхней части этого zip-файла, который извлечет данные во временный каталог и запустит код.
npx caxa \
--input build \
--output application \
--no-include-node \
-- "{{caxa}}/runtime/bin/java" "-jar" "{{caxa}}/javaexe-1.0.jar"
Это создаст исполняемый файл с именем “application”. Если вы создаете его для Windows, то нужно указать “application.exe”. Когда исполняемый файл запускается, {{caxa}} будет заменен на временный каталог, в котором был развернут zip-файл.
Учтите, что при создании исполняемых файлов используются также и такие механизмы, как подпись кода и автоматические обновления. Однако эти вещи требуют более глубокого изучения, которое трудно вместить в одну публикацию.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ