Сборка в один jar
На сервере нет Maven и исходников — туда нужно положить готовую программу. Удобнее всего собрать исполняемый jar со всеми зависимостями внутри (его называют fat jar или uber jar). Такой файл запускается одной командой java -jar, а Jetty едет внутри него — отдельный веб-сервер на машине не нужен.
Почему обычного jar мало
Команда mvn package и без плагинов создаёт jar, но в нём только ваши классы. Javalin, Jetty, Jackson и H2 лежат в локальном репозитории Maven на вашем компьютере, а на сервере их нет. Плагин maven-shade-plugin распаковывает все зависимости и складывает их в один файл вместе с вашим кодом.
Плагин
Добавьте плагин в раздел plugins файла pom.xml:
<!-- pom.xml -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.example.blog.Main</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
</configuration>
</execution>
</executions>
</plugin>| Часть | Зачем |
|---|---|
phase package | Плагин срабатывает при mvn package |
ManifestResourceTransformer | Записывает главный класс в манифест jar, иначе java -jar не знает, что запускать |
ServicesResourceTransformer | Объединяет служебные файлы META-INF/services, через которые Jetty и драйверы находят свои части |
Порт из окружения
На сервере порт часто задают переменной окружения. Сделайте так, чтобы main читал PORT и брал 8080 по умолчанию:
// src/main/java/com/example/blog/Main.java
public static void main(String[] args) {
var repo = new PostRepository(Database.create("jdbc:h2:./blog"));
int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "8080"));
createApp(repo).start(port);
}Сборка и запуск
mvn packageMaven выполнит тесты и соберёт файл target/blog-1.0.0.jar размером около 12 МБ. Во время сборки плагин печатает много предупреждений Discovered module-info.class и overlapping resource — для нашего приложения они безопасны. Запустите jar:
java -jar target/blog-1.0.0.jar[main] INFO io.javalin.Javalin - Javalin started in 176ms \o/
[main] INFO io.javalin.Javalin - Listening on http://localhost:8080/
На другом порту: PORT=9000 java -jar target/blog-1.0.0.jar. На сервер достаточно скопировать этот файл и установить Java 25. Файл базы blog.mv.db появится в папке, из которой запущена команда.
Что дальше
Чтобы приложение работало постоянно и перезапускалось после сбоев, его запускают как службу (systemd в Linux) или в Docker-контейнере на основе образа с Java, например eclipse-temurin:25-jre. В обоих случаях команда запуска та же — java -jar blog-1.0.0.jar.
Частая ошибка
Рядом с нужным файлом плагин оставляет original-blog-1.0.0.jar — обычный jar без зависимостей и без главного класса. Если запустить его, Java сразу сообщит:
no main manifest attribute, in target\original-blog-1.0.0.jar
А если указать класс вручную через java -cp, программа упадёт на первом же классе Javalin: NoClassDefFoundError: io/javalin/rendering/FileRenderer. Запускайте файл без приставки original-.