JavaRush /Курсы /Java Server /build/, jar и запуск вне IDE

build/, jar и запуск вне IDE

Java Server
4 уровень , 3 лекция
Открыта

1. Запуск вне IDE

К этому моменту run уже настроен: Gradle знает, какой main() запускать и как собрать для него classpath. Но на этом история сборки не заканчивается. После classes, jar и run в проекте остаются вполне конкретные следы: скомпилированные классы, обработанные ресурсы и архив в build/libs.

Поэтому теперь смотрим не на сам факт запуска, а на следы сборки. Где лежат эти файлы, что именно попадает в обычный jar и чем такой архив отличается от запуска через Gradle — вот это и важно понять.

Команда ./gradlew run уже даёт воспроизводимый старт. Теперь используем её как точку сравнения: она помогает увидеть, что Gradle собирает автоматически, а что при прямом запуске JVM придётся задавать руками.

2. Содержимое build/ после ./gradlew run

Папка build/ — это место, куда Gradle складывает результаты своей работы. Представьте, что src/ — это «ингредиенты и рецепт», а build/ — «кухня после готовки»: там лежат подготовленные файлы, промежуточные результаты и готовые артефакты. Важно: кухня может быть разгромлена, но рецепт от этого не должен страдать.

Когда вы делаете ./gradlew run, Gradle обычно, если нужно, сначала компилирует код и обрабатывает ресурсы, а потом запускает приложение. И результаты этой подготовки вы увидите именно в build/.

Примерно так (упрощённо) выглядит то, что вам важно уметь «узнавать в лицо»:

build
├─ classes/java/main        <- скомпилированные .class файлы
├─ resources/main           <- обработанные ресурсы
└─ libs                     <- сюда обычно попадает jar

Полезная привычка: не пытайтесь «понять сборку по ощущениям» — проверяйте следы в build/. Если вы запустили classes, а у вас нет build/classes/java/main, значит задача не отработала. Если ресурсы лежат в src/main/resources, но не появились в build/resources/main, значит проблема где-то в обработке ресурсов.

Чтобы не держать всё в голове, удобно использовать маленькую таблицу:

Что ищем Где обычно лежит Откуда берётся Что это означает
Скомпилированные классы build/classes/java/main после compileJava (внутри classes) Java-код успешно прошёл компиляцию
Ресурсы build/resources/main после processResources (внутри classes) файлы из src/main/resources попали в classpath на запуске
Архив приложения build/libs/*.jar после jar проект упакован в один .jar файл

И ещё один принцип, который часто спасает нервы: папка build/ — не источник истины. Если вы отредактировали файл в build/resources/main, это почти наверняка пропадёт при следующей сборке. Править нужно исходники в src/, а build/ воспринимать как одноразовый результат.

3. Сборка JAR: файл и расположение

Слово jar пугает только первые два раза. На деле это просто архив (по сути zip), в котором лежит результат вашего проекта. Но здесь важно держать границу: обычный jar, который Gradle собирает этой задачей, — это упаковка вашего кода, ресурсов и служебных метаданных. Он не обязан быть самодостаточным архивом со всеми внешними библиотеками.

Это один из способов сказать: «вот результат сборки в упаковке, его можно передавать как файл».

Собрать jar в Gradle-проекте можно командой:

./gradlew jar

После этого загляните в папку build/libs. Там появится .jar-файл. Его имя обычно зависит от настроек проекта и часто выглядит как имяПроекта-версия.jar. Если вы ещё не задавали версию, Gradle может подставить что-то вроде unspecified — это не ошибка, просто версия пока не определена.

Чтобы не спорить с реальностью, можно сделать так: сначала собрать, потом посмотреть, что получилось:

# Собираем jar-архив приложения
./gradlew jar
# Проверяем, какой файл появился
ls build/libs

(На Windows вместо ls обычно используют dir — смысл тот же: увидеть имя файла.)

И здесь важно не перепутать два разных смысла:

  • ./gradlew run — это запуск приложения через Gradle (с правильным classpath и зависимостями).
  • ./gradlew jar — это упаковка результата в архив. Это не «ещё один run», а другой продукт сборки.

4. Проверяем содержимое jar и ресурсы

Когда вы впервые собираете jar, возникает естественный вопрос: «А что там внутри? Я точно собрал то самое?» Хорошая новость: проверить это можно без гаданий на кофейной гуще.

Есть простая команда, которая показывает содержимое архива:

# Смотрим содержимое jar-архива (подставьте своё имя файла)
jar tf build/libs/readlater-starter.jar

Имя файла у вас может отличаться, так что подставьте своё из build/libs. Команда jar tf выведет список файлов внутри архива. Там вы должны увидеть как минимум:

  • ваш класс ReadLaterApplication в виде .class-файла;
  • ваши ресурсы (например, banner.txt, если вы добавляли его в src/main/resources).

Выглядеть это будет примерно так (фрагмент):

META-INF/
META-INF/MANIFEST.MF
com/example/readlater/ReadLaterApplication.class
banner.txt

Если вы видите banner.txt внутри jar, значит поймали очень важный момент: ресурс стал частью артефакта. То есть приложение может прочитать этот файл не потому, что рядом есть папка src/, а потому что ресурс упакован в итоговый результат и доступен через classpath.

С точки зрения кода здесь важен один вызов, который у нас уже живёт в ReadLaterApplication:

ReadLaterApplication.getResourceAsStream("/banner.txt")

Для JVM не так важно, лежит banner.txt в build/resources/main во время ./gradlew run или уже внутри jar после упаковки. В обоих случаях код обращается к ресурсу через classpath, а не через путь src/main/resources/....

5. Запуск без IDE: Gradle run и java -cp

Теперь разделим две похожие, но разные вещи.

./gradlew run — это запуск в разработке. Gradle сам собирает classpath, подхватывает ресурсы, добавляет зависимости на запуске и вызывает mainClass.

java -cp ... — это прямой запуск JVM. Здесь classpath задаёте уже вы сами, и JVM знает только то, что вы ей явно передали.

Такой прямой запуск удобно показывать на самом маленьком сценарии, где приложению хватает собственного кода и ресурса banner.txt. Как только в деле появляются внешние библиотеки, одного обычного jar из build/libs уже мало: JVM должна увидеть и эти зависимости тоже.

Через Gradle запуск выглядит так:

./gradlew run

А напрямую через JVM — так:

# Запуск main-класса напрямую из jar через classpath
java -cp build/libs/readlater-starter.jar com.example.readlater.ReadLaterApplication

На таком минимальном примере оба варианта могут дать один и тот же результат. Но граница между ними важна: ./gradlew run собирает нужный classpath автоматически, а при прямом запуске ответственность за classpath уже на вас.

То же самое и с аргументами запуска. Через Gradle:

./gradlew run --args="hello world"

И напрямую через JVM:

java -cp build/libs/readlater-starter.jar com.example.readlater.ReadLaterApplication hello world

В обоих случаях аргументы прилетают в тот же String[] args. Как только различие между run, обычным jar и прямым запуском через JVM становится понятным, гораздо легче увидеть, где именно сломалась сборка: в упаковке артефакта, в classpath или уже в самом коде.

6. Типичные ошибки при работе с build/ и jar

Ошибка №1: редактировать файлы в build/, потому что «так быстрее».
Это очень соблазнительно: вы нашли build/resources/main/banner.txt, поправили текст — и он тут же изменился. А потом вы запускаете ./gradlew clean или просто следующую сборку, и изменения исчезают. Причина простая: build/ — это не исходники, а результат. Лечится привычкой править только src/main/java и src/main/resources.

Ошибка №2: считать, что jar — это «запуск», а run — это «тоже запуск, но по-другому».
На практике это две разные цели. run — это удобно запустить приложение в разработке. jar — это упаковать результат. Если держать в голове вопрос «я сейчас хочу запустить или получить артефакт?», путаница уходит почти сразу.

Ошибка №3: удивляться, что jar не содержит зависимости, и ждать от него поведения «как у одного-единственного файла на всё».
Это нормальный шок новичка: «я же подключил библиотеки в Gradle, почему их нет внутри jar?» Потому что стандартный jar — это упаковка вашего кода и ресурсов. То, что зависимости подтягиваются при run, — заслуга Gradle, который формирует classpath. Пока нам важно зафиксировать базовую картину: jar — не волшебная коробка, а конкретный формат.

Ошибка №4: искать артефакты не там, где они реально лежат.
Когда сборка прошла, но вы «не видите jar», обычно дело не в мистике, а в том, что вы смотрите не в build/libs. Точно так же с классами и ресурсами: если вы не понимаете, появились ли они, проверьте build/classes/java/main и build/resources/main. Это банально, но экономит часы.

Ошибка №5: доказывать себе, что всё воспроизводимо, только запуском из IDE.
IDE — отличный инструмент, но её конфигурация живёт на вашей машине. Воспроизводимость проверяется простой вещью: «закрываю IDE, открываю терминал, запускаю ./gradlew run — и проект стартует». Если это работает, значит проект действительно воспроизводим, а не «работает только у автора на ноутбуке».

1
Задача
Java Server, 4 уровень, 3 лекция
Недоступна
Следы сборки в build/ и содержимое jar
Следы сборки в build/ и содержимое jar
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
21 мая 2026
@echo off setlocal rem Используем общий wrapper из корня репозитория. set "SEARCH=%~dp0" :loop if exist "%SEARCH%gradlew.bat" ( if /I not "%SEARCH%"=="%~dp0" ( call "%SEARCH%gradlew.bat" -p "%~dp0." %* exit /b %errorlevel% ) ) for %%I in ("%SEARCH%..") do set "NEXT=%%~fI\" if /I "%NEXT%"=="%SEARCH%" goto fail set "SEARCH=%NEXT%" goto loop :fail echo Root gradlew.bat not found. exit /b 1 rem problem original script from javarush looks only one level up rem if exist "%~dp0..\gradlew.bat" ( rem call "%~dp0..\gradlew.bat" -p "%~dp0." %* rem exit /b %errorlevel% rem ) rem Запасной путь для окружений, где gradle доступен напрямую. gradle -p "%~dp0." %*