JavaRush /Курси /Java Server /Gradle: clean, classes, jar, build, run

Gradle: clean, classes, jar, build, run

Java Server
Рівень 4 , Лекція 0
Відкрита

1. Життєвий цикл збірки: не одна кнопка

Якщо ви прийшли в програмування через навчальні задачі, «збірка» легко сприймається як одна команда: «зібрати проєкт». У реальній розробці це ланцюжок кроків: прибрати старі результати, скомпілювати код, підготувати ресурси, упакувати проєкт, а інколи ще й прогнати перевірки. Gradle якраз і потрібен для того, щоб цей ланцюжок був зрозумілим і відтворюваним.

Учора ми підключали плагіни та залежності. Тепер важливо побачити, що Gradle насправді робить із проєктом: як він проходить шлях від вихідного коду до готових класів, ресурсів, архіву та запуску.

Build lifecycle — це «життєвий цикл збірки»: набір стандартних задач, які майже в будь-якому Java-проєкті означають приблизно одне й те саме. Важливо: lifecycle — не «одна суперкоманда», а система задач, у кожної з яких своя мета. Приклад із життя: «приготувати вечерю» — це не одне діяння, а набір кроків. І якщо переплутати «помити овочі» та «поставити в духовку», вечеря вийде… для тещі.

У Gradle такий «крок» називається task (задача). Ви викликаєте задачу з термінала, а Gradle сам вирішує, які кроки потрібно виконати заздалегідь, щоб результат був коректним. І ось тут починається надзвичайно важлива думка дня: коли ви пишете ./gradlew jar, ви не запускаєте «одну команду». Ви просите Gradle довести проєкт до стану, у якому можна створити jar. А це автоматично включає підготовчі кроки.

2. Граф задач Gradle: читаємо виведення

Коли новачок уперше бачить виведення Gradle, перша реакція зазвичай така: «Ого, воно щось робить, я не знаю що, але нехай робить». Друга реакція — «чому воно робить так багато?». Обидві реакції нормальні, але ми хочемо дійти до третьої: «я бачу, які задачі виконалися, і розумію, на якому етапі перебуваю».

Ключовий факт: у Gradle задачі пов’язані залежностями. Це не завжди сувора «лінійка» (ABC), а радше граф: частину кроків можна виконувати незалежно, а частину — лише після інших. На практиці для нашого набору задач усе виглядає досить лінійно, і це чудово: менше шансів загубитися.

Подивіться на типовий шматок виведення під час пакування jar:

> Task :compileJava
> Task :processResources
> Task :classes
> Task :jar

Ці рядки — не декорація. Вони буквально кажуть: спочатку ми скомпілювали Java-код, потім підготували ресурси, далі довели проєкт до стану «класи готові» і лише після цього упакували все в jar. Тобто jar — не магічна кнопка, а фінальний крок після підготовки.

Якщо ви запускаєте задачу повторно і Gradle пише щось на кшталт UP-TO-DATE, це означає: «я вже робив це, вхідні дані не змінилися, результат і досі актуальний». І так, це одна з причин, чому запускати clean перед кожним чханням — сумнівне задоволення (ми ще поговоримо про це).

Щоб закріпити картину зв’язків, ось проста схема:

flowchart TD
    %% Спрощені залежності задач: що за чим підтягується автоматично
    compileJava --> classes
    processResources --> classes
    classes --> jar
    classes --> run
    jar --> build
    test --> build

Схема навмисно спрощена, але правильно передає логіку: classes — база; від неї залежать і jar, і run. А build — ширша задача: вона охоплює і пакування, і перевірки.

3. clean: забути результати минулої збірки

Команда clean звучить майже терапевтично: наче ви прибираєтеся в кімнаті й одночасно позбуваєтеся тривожності. У світі Gradle це приблизно так і працює, але важливо не перетворювати прибирання на спосіб життя. clean видаляє результати попередньої збірки — те, що Gradle поклав у папку build/. Вихідний код (src/...) при цьому взагалі не чіпає.

Викликається це так (у Windows буде gradlew.bat clean, але зміст той самий):

# Видалити результати минулих збірок (папку build/)
./gradlew clean

Після clean ви по суті повертаєте проєкт у стан «у нас є тільки вихідний код і налаштування». Це корисно, коли ви підозрюєте, що результати старої збірки заважають побачити реальну картину. Наприклад, ви перейменували файл ресурсу, і десь «завис» старий результат. Або змінювали налаштування збірки й хочете переконатися, що все збирається з нуля.

Але clean не робить двох речей, яких новачки інколи очікують. По-перше, він не видаляє завантажені залежності. Бібліотеки лежать у кешах Gradle, і clean не перетворює ваш комп’ютер назад на абсолютно порожній. По-друге, clean не «полагоджує проєкт». Він просто прибирає результат. Якщо проєкт не компілюється, він не почне компілюватися лише тому, що ви пʼять разів усе почистили. Це як перезавантажувати роутер, коли у вас помилка в Java-коді: ритуал гарний, ефект сумнівний.

У ReadLater Starter найчастіше clean потрібен не «за звичкою», а за потреби. Якщо все працює, не треба щоразу влаштовувати генеральне прибирання. Нехай Gradle економить вам час — це його робота.

4. classes: підготовка до запуску без пакування

Назва classes багатьох збиває з пантелику: здається, що йдеться про «класи в Java» як про концепцію ООП. Насправді задача classes доводить проєкт до стану, де у вас є скомпільовані .class-файли і підготовлені ресурси, які можна покласти на classpath. Це проміжний, але дуже важливий рівень готовності.

Запускається так:

# Скомпілювати код і обробити ресурси, але нічого не пакувати
./gradlew classes

Що при цьому зазвичай відбувається? Gradle підтягує дві ключові підзадачі: compileJava (компіляція вихідного коду) і processResources (підготовка ресурсів). Ви побачите це у виведенні:

> Task :compileJava
> Task :processResources
> Task :classes

Зверніть увагу: поруч із compileJava стоїть processResources. Тобто до стану classes належать не лише .class-файли. Збірка ще й підхоплює файли з src/main/resources, які застосунку знадобляться під час запуску.

Тут важливо вловити різницю між «скомпілювати» та «зібрати застосунок». classes — це «ми приготували інгредієнти». Вони вже нарізані й готові, але страва ще не запакована в контейнер для доставки.

Для ReadLater Starter на цьому етапі особливо корисно вловити одну річ: навіть якщо застосунок поки що крихітний, Gradle уже розділяє «підготовку коду» та «пакування в артефакт». Ця звичка потім різко зменшує хаос: ви не намагаєтеся запускати пакування, коли у вас банально не компілюється проєкт.

5. jar: упакувати результат в архів

JAR — це формат Java-архіву. У такому файлі можуть лежати не лише скомпільовані класи, а й ресурси, і службові метадані на кшталт META-INF. У нього пакують і бібліотеки, і застосунки.

Слово jar звучить як банка (англійською jar). І, чесно кажучи, аналогія непогана: ви берете підготовлені класи та ресурси й складаєте їх в один архівний файл. Але важливо не приписувати йому зайвого: звичайна задача jar пакує результат поточного проєкту, а не автоматично створює self-contained bundle з усіма залежностями, потрібними для запуску.

Запускається так:

# Упакувати скомпільовані класи та ресурси в jar-архів
./gradlew jar

Ключовий момент: jar залежить від classes, тому Gradle спочатку підготує класи, а потім упакує. Типовий порядок задач ви вже бачили:

> Task :compileJava
> Task :processResources
> Task :classes
> Task :jar

Після цього в папці build/libs з’являється .jar-файл. Назва залежить від налаштувань проєкту (імʼя проєкту, версія тощо), але сама ідея незмінна: це «упакований результат».

Дуже важливо не плутати «отримати jar» і «запустити застосунок». jar — це про пакування. Він не зобов’язаний запускатися сам по собі, і задача jar взагалі не намагається виконувати ваш код. Це як «запакувати валізу» і «поїхати у відпустку»: дії логічно пов’язані, але одна не замінює іншу.

Якщо хочеться просто перевірити, що застосунок стартує, jar — не найпряміший шлях. У таких випадках зазвичай зручніший run, про який ми поговоримо окремо. А jar потрібен тоді, коли вам важливий саме артефакт збірки.

6. build: повний стандартний цикл збірки

Задача build звучить максимально загально, і в цьому одночасно її сила та пастка. Це не «зібрати jar». Це «виконати повний стандартний сценарій збірки»: компіляція, перевірки та збірка артефактів. У Java-проєкті під «перевірками» часто мають на увазі тести, статичні перевірки та інші речі — у нас поки що тестів немає, але сама структура життєвого циклу вже на місці.

Запускається так:

# Виконати повний стандартний сценарій збірки проєкту
./gradlew build

У виведенні ви можете побачити щось на кшталт:

> Task :compileJava
> Task :processResources
> Task :classes
> Task :jar
> Task :test NO-SOURCE
> Task :build

NO-SOURCE поруч із test означає: «задача є, але тестів немає — виконувати нічого». І це нормально. Важливіше інше: build справді ширше, ніж jar. jar — один із кроків, а build — підсумкова «галочка»: проєкт пройшов стандартний цикл і готовий.

Новачкові корисно тримати в голові просте правило. Якщо ви хочете «швидко зібрати артефакт» — часто достатньо jar. Якщо хочете переконатися, що проєкт у цілому в порядку, — беріть build. В індустрії build часто стає базовою командою перед тим, як щось віддавати іншим: тому що вона включає більше перевірок.

І так, build — це все ще не запуск. Його завдання — зібрати.

7. run: запуск застосунку через Gradle

Задача run — це міст між «я зібрав проєкт» і «я реально запускаю застосунок як програму». Вона з’являється завдяки плагіну application і запускає ваш main() у коректному середовищі: з потрібним classpath, із залежностями, потрібними для запуску, і без необхідності вручну збирати довжелезну команду java -cp ....

Запускається так:

# Запустити застосунок через Gradle (з коректним classpath і залежностями)
./gradlew run

І тут важливий зв’язок: run залежить від classes. Тобто, якщо ви змінили код, Gradle спочатку скомпілює проєкт, а вже потім запустить. Тому виведення часто починається знайомими рядками:

> Task :compileJava
> Task :processResources
> Task :classes
> Task :run

Щоб це було зовсім наочно, нам поки достатньо найпростішої точки входу. Тут важливий сам факт старту JVM, а не зміст main():

package com.example.readlater;

// Точка входу застосунку: саме цей main() запускає задача run
public class ReadLaterApplication {
    public static void main(String[] args) {
        // Демонстраційний вивід, щоб побачити, що JVM справді стартувала
        System.out.println("ReadLater Starter"); // ReadLater Starter
    }
}

Тоді ./gradlew run справді запустить JVM, і ви побачите цей виведення. Це цінно з двох причин. По-перше, ви перевіряєте не тільки компіляцію, а й сам факт запуску. По-друге, це відтворювано: якщо в одногрупника або колеги проєкт не запускається, ви можете сказати «запусти ./gradlew run і покажи виведення», а не гадати, що в нього там у налаштуваннях IDE.

Зверніть увагу: run — не «покращений build». Це інший тип дії. build готує артефакти, run виконує програму. Іноді потрібен build, іноді run, іноді обидва — але плутати їх не можна, інакше ви будете «лагодити» не ту частину реальності.

8. Вибір задачі під мету: карта рішень

Коли команд стає більше однієї, мозок новачка намагається схитрувати: «а можна я завжди писатиму одну й ту саму?». Можна, але ви заплатите часом і плутаниною. Набагато приємніше мати коротку карту: яка задача відповідає на яке запитання. Це як вибрати правильний інструмент: молотком можна вкрутити шуруп, але навіщо страждати, якщо є викрутка.

Ось компактна таблиця-шпаргалка (її не потрібно вчити напам’ять, достатньо впізнавати в потрібний момент):

Що ви хочете отримати Яка задача зазвичай підходить Що ви отримуєте на виході
«Прибрати результати минулої збірки та почати з чистого аркуша» clean Папка build/ очищена
«Перевірити, що код компілюється» classes Скомпільовані класи + підготовлені ресурси (без jar)
«Отримати .jar-архів» jar Файл у build/libs
«Прогнати повний стандартний сценарій збірки» build Збірка + перевірки (що є в проєкті)
«Запустити застосунок як програму» run Запуск main() через Gradle

А тепер один важливий практичний нюанс: Gradle дозволяє вказувати кілька задач в одній команді. Наприклад, якщо ви хочете точно перебудувати jar з нуля, ви можете зробити так:

# Спочатку прибрати старі результати, потім зібрати jar заново
./gradlew clean jar

Gradle спочатку виконає clean, потім побудує граф для jar, побачить, що потрібно зробити classes, і виконає все в правильному порядку. Це не «два різні світи», а одна система.

Головне — не перетворюйте такі команди на рефлекс «про всяк випадок». Якщо ви щоразу пишете clean build run, ви формально не помиляєтеся… але за часом це відчувається так, ніби їхати в магазин за одним яблуком на автобусі з пересадкою, хоча поруч є кіоск.

9. Типові помилки під час роботи із задачами Gradle

На початку шляху помилки з Gradle зазвичай не «складні», а «психологічні»: ми очікуємо від команди одне, а вона робить інше. Це нормально — ви просто переносите звички з IDE-світу «натисни зелену кнопку» у світ збірки «обери задачу під мету». Давайте проговоримо найчастіші граблі, щоб ви наступали на них рідше й із меншим ентузіазмом.

Помилка №1: вважати, що build і run — це одне й те саме «зібрати й запустити».
Дуже легко переплутати: слова схожі, обидві команди щось роблять. Але build — про те, щоб підготувати проєкт і артефакти, а run — про те, щоб реально виконати ваш main(). Тому ситуація «build пройшов, а run не запускається» цілком можлива. І навпаки: «run працює, а build падає на перевірках» — теж можлива. Це не суперечність, а різні цілі.

Помилка №2: очікувати, що classes створить jar.
classes готує результат компіляції та ресурси, але не пакує. Якщо вам потрібен архів — це jar. Якщо потрібно просто зрозуміти, чи компілюється код, — classes чудовий. Коли ви плутаєте ці цілі, ви або чекаєте файл, який не зобов’язаний з’явитися, або запускаєте зайве пакування, хоча вам була потрібна лише компіляція.

Помилка №3: запускати clean перед кожною командою «про всяк випадок».
Це популярна звичка — як «перезавантажити комп’ютер, бо щось не працює». Іноді допомагає, але частіше просто витрачає час. Gradle якраз створений для того, щоб не перебудовувати те, що не змінювалося. Якщо ви постійно робите clean, ви вручну вимикаєте частину цього сенсу. Користуйтеся clean, коли розумієте, навіщо він потрібен: наприклад, хочете гарантовано перебудувати результат без слідів минулого.

Помилка №4: плутати «я хочу артефакт» і «я хочу перевірити запуск».
Якщо ви хочете переконатися, що застосунок стартує, найчастіше логічніше run. Якщо вам потрібен jar — це jar або build. Коли ви намагаєтеся «перевірити запуск» через jar, ви робите зайві кроки й усе одно не отримуєте відповіді на головне запитання. І навпаки, коли вам потрібен артефакт, а ви запускаєте run, легко забути, що jar так і не зібрано.

Помилка №5: ігнорувати порядок задач у виведенні Gradle.
Виведення Gradle — це ваша карта місцевості. Коли щось падає, там майже завжди є рядок на кшталт > Task :що-небудь FAILED. Якщо не помічати, на якій саме задачі все зламалося, дуже легко почати лагодити все підряд: залежності, код, IDE і навіть настрій. Набагато спокійніше спочатку побачити: це помилка на компіляції (compileJava), на пакуванні (jar) чи на запуску (run). Навіть без глибоких знань це вже економить години.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ