JavaRush /Курси /Java Server /application плагін і...

application плагін і mainClass

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

1. Gradle і точка входу main()

На цей момент у нас уже є Java-код, ресурси та робочий ReadLaterApplication, який читає banner.txt із classpath. Та для Gradle це поки що просто Java-проєкт. Щоб завдання run узагалі зʼявилося і стабільно запускало один і той самий main(), проєкт потрібно явно оголосити застосунком і вказати точку входу.

У Java точка входу — це метод public static void main(String[] args). Але в проєкті може бути кілька класів із main() (особливо в навчальних експериментах), і Gradle не зобовʼязаний вгадувати, який із них «головний». Тому ми робимо вибір явним: обираємо один клас входу й фіксуємо його в build.gradle.kts.

2. Ролі плагінів java і application

До цього моменту в нас уже був плагін java, і він відповідає за компіляцію, стандартну структуру вихідних файлів і типові завдання на кшталт compileJava та classes. Але плагін java сам по собі не каже Gradle: «Гей, це застосунок, його треба вміти запускати як програму». Він радше про те, щоб зібрати код.

Плагін application якраз додає проєкту поведінку застосунку: він приносить завдання run і повʼязану з ним конфігурацію. Тобто він не змінює вашу Java-програму чарівним чином і не додає до неї main()main() усе одно пишете ви. Плагін лише дає Gradle змогу коректно запускати програму через JVM: зібрати правильний classpath під час запуску (разом із залежностями) і викликати потрібний main().

Щоб умістити це в одну картинку, зручно тримати в голові таку мінітаблицю:

Питання java plugin application plugin
«Де лежить код і ресурси?» Так, задає стандартну структуру src/main/java і src/main/resources Не про це
«Як компілювати?» Так (compileJava, classes) Опосередковано використовує результат компіляції
«Як запустити як програму?» Ні (IDE зможе, Gradle — не зобовʼязаний) Так (run)
«Як зрозуміти, який main() запускати?» Не розвʼязує Розвʼязує через mainClass

Можна уявити це як ролі в оркестрі. java plugin — це людина, яка готує сцену: ставить пюпітри, друкує ноти, роздає інструменти. application plugin — диригент, який каже: «Зараз граємо ось цю пʼєсу і починаємо з цієї партії». А mainClass — та сама «перша скрипка»: конкретна точка, з якої все починається.

І ще один важливий момент, який новачки часто плутають: application plugin не має стосунку до файлу application.properties (це зовсім інша історія). Тут «application» означає просто застосунок, який запускають.

3. mainClass і повне імʼя класу

mainClass — це налаштування для Gradle, яке вказує, який клас потрібно запускати в завданні run. І тут починається типова плутанина: у mainClass пишуть не «імʼя файлу» і не «шлях у файловій системі», а повне імʼя Java-класу, тобто імʼя разом із пакетом.

Наприклад, якщо у вас файл лежить так:

src/main/java/com/example/readlater
└─ ReadLaterApplication.java

і всередині нього в першому рядку написано:

package com.example.readlater;

то повним імʼям класу буде:

com.example.readlater.ReadLaterApplication

Це не косметика, а суворе правило. Java-пакети — це «адреси» в просторі імен. Коротке імʼя ReadLaterApplication схоже на імʼя «Саша» в офісі на 200 людей: технічно імʼя є, але спробуйте зрозуміти, який саме Саша потрібен. Повне імʼя com.example.readlater.ReadLaterApplication — це вже «Саша Петров із курсу з бекенду, стіл праворуч, поруч із кавоваркою». Шанси помилитися різко падають.

Важливо також розуміти різницю між трьома схожими, але різними речами:

Що це Приклад Де використовується
Коротке імʼя класу ReadLaterApplication У коді, якщо клас у тому самому пакеті або імпортований
Повне імʼя класу (FQCN) com.example.readlater.ReadLaterApplication У налаштуванні mainClass, у логах JVM, у помилках ClassNotFound
Шлях до файлу src/main/java/com/example/readlater/ReadLaterApplication.java У файловій системі проєкту, в IDE

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

4. ReadLaterApplication: точка входу

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

Робоча версія цього класу в нас уже є: вона читає banner.txt із classpath. Зараз важливо не переписувати main() заново, а звернути увагу на дві речі: пакет класу і коректну сигнатуру точки входу.

package com.example.readlater;

public class ReadLaterApplication {
    public static void main(String[] args) {
        // тут залишається робоча логіка застосунку
    }
}

Зверніть увагу на три речі, які часто вислизають із поля зору. По-перше, main має бути static, інакше JVM не зможе викликати його без екземпляра обʼєкта. По-друге, сигнатура має бути саме main(String[] args) (є й інші допустимі варіанти, але зараз вони нам не потрібні). Навіть якщо аргументи вам поки що не потрібні, сигнатура залишається саме такою: JVM дивиться на форму точки входу, а не на багатство внутрішньої логіки. По-третє, пакет має відповідати директорії, інакше IDE може «полагодити» запуск, а Gradle — ні.

5. build.gradle.kts: application і mainClass

Тепер залишилося повʼязати цей клас із конфігурацією Gradle. Коли в нас є ReadLaterApplication, Gradle потрібно пояснити, що саме він є точкою входу. Це робиться в build.gradle.kts через підключення плагіна application і блок application { ... }.

У Kotlin DSL це виглядає дуже прямо. Мінімальний фрагмент, який вам потрібен, такий:

plugins {
    java // Компіляція, стандартна структура проєкту, завдання на кшталт compileJava/classes
    application // Додає завдання run і конфігурацію запуску застосунку
}

application {
    // Повне імʼя класу (FQCN): пакет + імʼя класу
    mainClass = "com.example.readlater.ReadLaterApplication"
}

Тут відбуваються одразу дві важливі речі. Підключення application додає завдання run і робить проєкт «запускним» на рівні Gradle. А mainClass фіксує, який клас потрібно запускати. Рядок "com.example.readlater.ReadLaterApplication" — це те саме повне імʼя класу, про яке ми говорили: пакет + імʼя класу через крапку.

Якщо ви забудете вказати mainClass, Gradle не зможе коректно виконати run і скаржитиметься на помилку конфігурації. Якщо mainClass вказано, але ви помилилися в пакеті або назві класу, збирання може пройти, а запуск упаде вже під час старту JVM із повідомленням на кшталт «не знайдено основний клас». І це нормально: компіляція і запуск — різні етапи життя проєкту.

6. Задача run і запуск main()

Після підключення application і вказання mainClass завдання run отримує конкретну мету: запустити com.example.readlater.ReadLaterApplication із коректним classpath під час запуску.

Найнадійніший спосіб перевірити, що все налаштовано правильно, — запустити застосунок через Gradle:

./gradlew run # Запуск mainClass через Gradle (із правильним runtime classpath)

Якщо все налаштовано правильно, ви побачите той вивід, який робить поточний ReadLaterApplication, — зараз це банер із banner.txt.

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

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

Це хороше нагадування: application plugin не компілює і не створює main() за вас. Він просто повʼязує вже наявний клас із завданням запуску.

Якщо потрібно механічно перевірити передавання аргументів, application plugin уміє і це:

./gradlew run --args="hello" # Аргументи потраплять у String[] args вашого main()

Навіть якщо поточна версія ReadLaterApplication їх поки що не друкує, механізм уже той самий: Gradle передасть значення в String[] args.

І давайте чесно: корисно один раз побачити типову поломку. Наприклад, якщо написати:

application {
    // Навмисна описка: JVM спробує знайти клас за цією назвою і не знайде
    mainClass = "com.example.readlater.ReadLaterApplicatoin"
}

то ./gradlew run упаде, тому що JVM не знайде клас. Тут код може чудово компілюватися, але запуск усе одно зламається: ви дали Gradle адресу, за якою ніхто не живе.

7. Типові помилки під час роботи з application і mainClass

У цій темі є особливий тип помилок: вони майже завжди виглядають страшнішими, ніж є насправді. Це як прийти в банк і побачити чергу на 40 людей: здається, що ви пропали, а потім зʼясовується, що половина людей просто питає пароль від Wi‑Fi. Нижче — найчастіші граблі, на які наступають новачки, і способи розпізнати їх за симптомами.

Помилка № 1: вказати в mainClass коротке імʼя класу без пакета.
Якщо написати mainClass = "ReadLaterApplication", Gradle, найімовірніше, не знайде клас, тому що для JVM це не повне імʼя. Іноді здається, що «ну IDE ж бачить», але IDE тут не показник. Правильна стратегія проста: відкрити ReadLaterApplication.java, подивитися рядок package ...; і склеїти package + "." + імʼяКласу.

Помилка № 2: переплутати імʼя класу і шлях до файлу.
Новачки іноді намагаються написати щось на кшталт mainClass = "src/main/java/com/example/readlater/ReadLaterApplication.java". Це логічно по-людськи («ось же файл!»), але для JVM безглуздо: запускається клас, а не файл. У mainClass завжди крапки, а не слеші, і ніколи не пишеться .java.

Помилка № 3: забути підключити application plugin і намагатися запускати run.
Якщо в plugins { ... } немає application, завдання run просто не зʼявиться. Симптом зазвичай такий: Gradle каже, що завдання run не існує, або пропонує щось подібне, але не те. Лікування нудне, зате ефективне: додати application до plugins.

Помилка № 4: клас є, mainClass вказано, але «Main method not found».
Це класика, коли main() написаний неправильно. Наприклад, забули static, написали main(String args) замість String[] або випадково зробили метод private. JVM у цьому разі чесно каже: клас знайшла, але точку входу — ні. Лікується перевіркою сигнатури: public static void main(String[] args).

Помилка № 5: пакет у коді не відповідає директорії, і IDE «підтримує ілюзію», а Gradle ламається.
Наприклад, файл лежить у src/main/java/com/example/readlater/ReadLaterApplication.java, але всередині написано package com.example;. IDE може запустити, тому що в неї свої внутрішні механізми і кеші, а Gradle буде збирати за правилами. Це неприємно, бо виглядає як «магія». Рішення — дисципліна: директорія і package мають збігатися, а mainClass — збігатися з обома.

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