1. Каркас проєкту
Проєкт — це не просто клас Main, який запускається. Спершу легко подумати: «я створив файл, натиснув Run — отже, проєкт уже є». Але щойно з’являється другий файл, повторний запуск через тиждень або спроба стартувати на іншій машині, з’ясовується: у «папки з кодом» немає пам’яті. Вона не пояснює, як її зібрати й запустити.
Каркас Gradle-проєкту — це, по суті, явна домовленість між вами й майбутнім собою, а ще — з одногрупниками, рев’юером, роботодавцем і вашим котом, який випадково натиснув Delete. У цій домовленості прописано, де лежить код, де лежать ресурси, які файли вважаються «офіційними», як називається проєкт, який у нього стартовий клас і чому запуск виконується з кореня проєкту однією командою, а не за схемою «я тут в IDEA галочку поставив».
Можна провести грубу, але корисну аналогію: Java-файл — це інгредієнт, а каркас проєкту — рецепт із температурою, часом і кроками. Інгредієнти без рецепта іноді теж можна з’їсти, але на виході часто виходить «салат із усього, що було в холодильнику».
2. Підсумковий вигляд ReadLater Starter
Зараз ми зберемо мінімальну структуру, у якій з першого погляду зрозуміло: це Gradle-проєкт, він запускається через Wrapper, у нього є build-файли, код і ресурси. Це не «ідеальна» структура на всі часи, але це стійка база, на яку ми спокійно нарощуватимемо наступні теми курсу.
І ще одна важлива домовленість: Wrapper-файли в цьому дереві не пишуть руками. Вони вже входять до стартового каркаса проєкту й далі комітяться в репозиторій як частина єдиної точки входу. Нижче — той самий мінімальний каркас, який потім будемо перевіряти через build і run.
Ось еталонне дерево — найменший мінімум:
readlater-starter/
├─ gradlew
├─ gradlew.bat
├─ gradle/
│ └─ wrapper/
│ ├─ gradle-wrapper.jar
│ └─ gradle-wrapper.properties
├─ settings.gradle.kts
├─ build.gradle.kts
├─ README.md
└─ src/
└─ main/
├─ java/
│ └─ com/
│ └─ example/
│ └─ readlater/
│ ├─ ReadLaterApplication.java
│ └─ ConsoleBanner.java
└─ resources/
└─ application.properties
Щоб це дерево не лишилося просто «страшною картинкою», корисно один раз зрозуміти роль кожного елемента. Нижче — компактна таблиця, до якої ви ще не раз повертатиметеся. І це нормально.
| Що це | Де лежить | Для чого потрібно |
|---|---|---|
| Wrapper-скрипти | gradlew, |
Єдина точка входу в проєкт: запускаємо Gradle «з проєкту», а не «із системи». |
| Wrapper-налаштування | gradle/wrapper/* | Фіксують версію Gradle й дають змогу автоматично завантажувати потрібний Gradle. |
| Ім’я збирання | settings.gradle.kts | Повідомляє Gradle, як називається проєкт і де взагалі починається збирання. |
| Правила збирання | build.gradle.kts | Головний файл, де описано, як збирати й запускати проєкт. |
| Документація запуску | README.md | Щоб проєкт запускався не «на словах», а за інструкцією в репозиторії. |
| Java-код | src/main/java | «Бойовий» код застосунку: не тести й не конфіги. |
| Ресурси | src/main/resources | Не-Java файли: конфіги, шаблони, зразковий JSON тощо. |
Якщо ви зараз дивитеся на це й думаєте: «Занадто багато папок заради одного рядка в консолі», вітаю — це класична правильна реакція. Саме тому в реальних проєктах каркас часто генерують. Але в навчальному курсі нам важливо один раз зібрати його вручну, щоб потім не сприймати структуру як магію.
3. Корінь проєкту: навігація й опора для Gradle
Корінь репозиторію — не місце для «всього поспіль». Це як панель приладів: подивилися й зрозуміли, де запуск, де збирання, де документація, де код. Якщо в корені лежать десятки випадкових файлів, проєкт починає виглядати як робочий стіл у Windows після важкого тижня — ніби все десь є, але знайти неможливо.
У нашому каркасі корінь містить лише те, що дійсно відповідає за життя проєкту як артефакта: Wrapper, два Gradle-файли, README й папку src. І зверніть увагу на важливу звичку: ми не кладемо Java-файли поруч із build.gradle.kts. Код живе в src/main/java, тому що Gradle, та й уся Java-екосистема, очікує саме такого порядку. А отже, сюрпризів буде менше.
Ще один момент, який краще знати наперед: згодом у вас з’являться папки на кшталт build/ і .gradle/. Їх Gradle створює сам. Це згенеровані директорії, і в «чистому» репозиторії їх зазвичай не тримають як вихідники. Зараз ми їх не створюємо вручну й докладно не розбираємо — просто не лякайтесь, коли побачите.
4. settings.gradle.kts: ім’я проєкту
Файл settings.gradle.kts — коротка, але важлива річ: він допомагає Gradle зрозуміти, що перед ним за збирання, і задає ім’я проєкту. Початківці часто плутають його з build.gradle.kts — і, якщо чесно, назви в них не найпривітніші. Але ролі різні: settings — це радше про проєкт цілком, а build — про правила збирання й запуску.
У нашому курсі ми тримаємо settings.gradle.kts максимально простим. Він лежить у корені й містить один рядок, який задає ім’я збирання. Це ім’я потім буде видно в логах, у назві артефактів і взагалі в загальному відчутті «що я зараз збираю».
Створіть файл settings.gradle.kts у корені й покладіть туди:
// Ім’я проєкту, яке буде видно в логах і артефактах
rootProject.name = "readlater-starter"
Тут важливе не стільки саме ім’я — воно може бути трохи іншим, — скільки принцип: ім’я має бути стабільним, зрозумілим і збігатися з тим, як ви називаєте репозиторій. Якщо репозиторій називається readlater-starter, а Gradle-проєкт — demo-final-v3-real-last, то це, звісно, весело… але лише перші 30 секунд.
5. build.gradle.kts: мінімум для збирання
build.gradle.kts — головний файл, який зазвичай лякає найбільше. Він виглядає як код, але це не ваш Java-код, а опис збирання на Kotlin DSL. Важливо відразу виробити спокійне ставлення: вам не потрібно розуміти весь Gradle, щоб нормально стартувати. На цьому етапі достатньо, щоб файл був мінімальним, читабельним і відповідав технічному baseline курсу: Java 25, Kotlin DSL, запуск через Gradle.
Ми зараз зробимо build.gradle.kts настільки маленьким, наскільки можливо, але при цьому достатньо корисним, щоб проєкт міг компілюватися і — трохи згодом — запускатися через ./gradlew run. При цьому ми свідомо не заглиблюємося в подробиці плагінів і залежностей: для цього буде окремий рівень. Тут ми просто фіксуємо каркас.
Почнімо з мінімального блоку plugins. Ми підключаємо два базові плагіни: java і application.
plugins {
// Плагін для компіляції Java-коду
java
// Плагін для запуску застосунку через `gradlew run`
application
}
Ці два рядки дають проєкту потрібну поведінку: Gradle починає розуміти, що потрібно компілювати Java-код, і додає можливість запускати застосунок як програму. Не сприймайте це як магію — сприймайте як «увімкнули режим Java-проєкту».
Тепер додамо репозиторій. Навіть якщо ми поки не підключаємо зовнішні бібліотеки, репозиторій — стандартна частина каркаса: згодом Gradle завантажуватиме залежності саме звідти. Ми використовуємо mavenCentral() — це найбазовіший варіант, і для всього курсу його достатньо.
repositories {
// Основний репозиторій залежностей для курсу
mavenCentral()
}
Далі йде дуже важлива частина саме для нашого навчального baseline: фіксуємо Java 25 через toolchain. Це допомагає тримати проєкт в одному «мовному коридорі» й зменшує кількість сюрпризів, якщо в когось локально стоїть інша версія JDK.
java {
toolchain {
// Фіксуємо версію Java для збирання, незалежно від локальної JDK
languageVersion = JavaLanguageVersion.of(25)
}
}
І, нарешті, «клей», який пов’язує Gradle-запуск із вашим стартовим класом. Ми заздалегідь домовимося, що точка входу застосунку називається ReadLaterApplication і лежить у пакеті com.example.readlater.
application {
// Повне ім’я класу з `public static void main(...)`
mainClass = "com.example.readlater.ReadLaterApplication"
}
Якщо зібрати все разом, вийде короткий і читабельний build.gradle.kts. Я покажу його цілком, але майте на увазі: це не «єдино правильна» версія, а мінімально достатній каркас для курсу.
plugins {
// Компілюємо Java-код
java
// Додаємо задачу `run` для запуску застосунку
application
}
repositories {
// Звідси Gradle буде завантажувати залежності
mavenCentral()
}
java {
toolchain {
// Використовуємо Java 25 в усіх середовищах
languageVersion = JavaLanguageVersion.of(25)
}
}
application {
// Точка входу застосунку
mainClass = "com.example.readlater.ReadLaterApplication"
}
Зараз найкорисніша вправа — не «запам’ятати», а навчитися розпізнавати очима ці блоки. Бачите plugins — значить, «увімкнули поведінку». Бачите repositories — значить, «знаємо, звідки завантажувати бібліотеки». Бачите java.toolchain — значить, «ми в Java 25». Бачите application.mainClass — значить, «у застосунку є зрозуміла точка входу».
Щоб зв’язати все це в одну картинку, можна уявити, що Gradle під час запуску робить приблизно такий маршрут:
flowchart TD
A["./gradlew ..."] --> B["settings.gradle.kts
імʼя збирання"]
A --> C["build.gradle.kts
правила збирання"]
C --> D["src/main/java
компіляція Java"]
C --> E["src/main/resources
ресурси на classpath"]
C --> F["application.mainClass
запуск застосунку"]
І так, це виглядає як «занадто багато кроків заради Hello World». Але backend-життя таке: краще мати передбачувані правила, ніж магію й несподіванки.
6. src/main/java: код застосунку
Папка src/main/java — стандартне місце для основного коду застосунку. І важливе слово тут — «стандартне»: Gradle за замовчуванням очікує код саме там. Якщо ви покладете ReadLaterApplication.java у корінь проєкту або в src/java, ви не «переможете систему», а просто купите собі набір дивних помилок на рівному місці.
У межах курсу ми відразу дисциплінуємо себе: код — у src/main/java, а структура папок усередині відображає структуру пакетів. Це просте правило економить величезну кількість часу, тому що IDE, Gradle й ви самі починають думати однаково.
Поки що нам не потрібен складний набір пакетів — ми лише стартуємо. Тому на поточному рівні достатньо одного базового пакета com.example.readlater і пари класів. Згодом проєкт розростеться, але старт має бути простим і впевненим.
7. Пакет com.example.readlater: package і папки
Пакети в Java — це не декоративний рядок, який пишуть «тому що так прийнято». Пакет — це адреса вашого класу всередині проєкту. І адреса має бути узгоджена: якщо клас оголошує package com.example.readlater;, то файл має лежати в папці com/example/readlater.
Створіть папки:
src/main/java/com/example/readlater
І далі кладіть туди класи. Чому ми використовуємо com.example, а не ваш реальний домен? Тому що це навчальний проєкт. У реальному продукті ви б використовували домен компанії в зворотному порядку. Зараз нам важливіше стабільно виробити звичку, ніж сперечатися про «ідеальний неймінг» (спойлер: за рік усе одно перейменовуватимете).
8. ReadLaterApplication: точка входу й банер
У проєкту має бути одна очевидна стартова точка. У нашому курсі це ReadLaterApplication. Зараз це буде простий клас із main, який друкує повідомлення «я живий» і викликає невеликий банер. Жодної бізнес-логіки й жодних «архітектурних» рішень. Нам потрібно лише довести, що структура коректна і що проєкт — це саме проєкт, а не один файл.
Створіть файл src/main/java/com/example/readlater/ReadLaterApplication.java:
package com.example.readlater;
public class ReadLaterApplication {
public static void main(String[] args) {
// Друкуємо банер — простий маркер, що застосунок запустився
ConsoleBanner.print();
// Основне повідомлення запуску (поки без логування)
System.out.println("ReadLater Starter працює"); // ReadLater Starter працює
}
}
Тепер додамо другий клас, щоб одразу відчути: проєкт — це не один файл, і це нормально. Створіть src/main/java/com/example/readlater/ConsoleBanner.java:
package com.example.readlater;
public final class ConsoleBanner {
private ConsoleBanner() {
// Забороняємо створювати екземпляри: це утилітний клас
}
public static void print() {
// Банер у консоль — щоб старт було помітно очима
System.out.println("=== ReadLater Starter ==="); // === ReadLater Starter ===
}
}
Зверніть увагу на маленьку інженерну деталь: ConsoleBanner зроблений final і з приватним конструктором. Це не «обов’язкове правило», а просто чесний спосіб показати намір: перед нами утилітний клас, екземпляри якого нам не потрібні. Так, можна було й не ускладнювати. Але краще звикати до акуратності там, де це не збільшує когнітивне навантаження.
І ще один момент: зараз ми використовуємо System.out.println() спеціально як видимий маркер запуску. Згодом, коли перейдемо до логування, ви побачите доросліший підхід. А поки нам потрібно, щоб застосунок подавав сигнал і це було видно очима.
9. src/main/resources: конфіги й ресурси
Папка src/main/resources часто сприймається як «якась загадкова річ, яку створює IDE». Насправді це дуже практична частина проєкту: сюди кладуть файли, які мають потрапити до збирання й бути доступними застосунку як ресурси. Конфігурація, шаблони, зразковий JSON для mock-режимів, logback.xml — усе це зазвичай живе саме тут.
Сьогодні ми не будемо читати ресурси з Java-коду й не будемо будувати конфігураційну систему — це буде значно пізніше. Але саму папку створюємо відразу, тому що вона частина каркаса. Навіть порожня папка працює як якір: ви заздалегідь знаєте, куди класти не-Java файли, і не розводите хаос із config/, resources2/, new_config_final/.
Створіть файл src/main/resources/application.properties з мінімальним вмістом-заглушкою:
# Ім’я застосунку (поки просто заглушка для структури ресурсів)
app.name=readlater-starter
Зараз цей файл ніяк не впливає на виконання застосунку, і це нормально. Ми робимо його не заради ефекту «прямо зараз», а заради дисципліни структури. У реальних проєктах конфігурація майже ніколи не живе в Java-коді, і чим раніше ви звикнете до окремого місця для неї, тим спокійніше піде подальший курс.
10. README.md: команди збирання й запуску
README для новачка часто виглядає як «ну, файл із текстом, можна потім». Але саме README робить ваш проєкт дружнім і для іншої людини, і для вас самих через місяць. Причому дружність тут вимірюється дуже просто: якщо я клонував репозиторій, відкрив README й одразу зрозумів, що вводити в термінал, — отже, проєкт зі мною розмовляє.
Сьогодні нам потрібен мінімум: назва, вимоги й команди збирання та запуску, записані прямо в репозиторії. Ми відразу зафіксуємо варіант для macOS/Linux і Windows, щоб проєкт не виглядав так, ніби його можна оживити лише з однієї конкретної оболонки.
Створіть README.md у корені:
# ReadLater Starter
Навчальний проєкт курсу `Java Server`.
## Вимоги
- JDK 25
## Збирання й запуск (macOS / Linux)
./gradlew build
./gradlew run
## Збирання й запуск (Windows)
gradlew.bat build
gradlew.bat run
Це виглядає просто, але вже дає величезну різницю порівняно з README з одного рядка «Відкрийте в IDE й натисніть Run». Такий README чесно прив’язує запуск до проєкту, а не до персональних звичок.
11. Типові помилки під час збирання каркаса проєкту
Помилка №1: Java-клас лежить «поруч із build.gradle.kts», а не в src/main/java.
Таке часто трапляється, коли ви створюєте файл «швидко, щоб перевірити ідею». Потім Gradle не знаходить вихідники, IDE починає вдавати, що все розуміє, а запуск через CLI розвалюється. Лікується однією звичкою: код завжди живе в src/main/java, без винятків.
Помилка №2: папка й пакет не збігаються.
Наприклад, файл лежить у src/main/java/com/example/readlater/ReadLaterApplication.java, а всередині написано package com.example.app;. IDE може це підсвітити, але якщо ви не звикли дивитися уважно, помилку легко пропустити. Далі виникають дивні «Class not found» під час запуску. Дисципліна проста: шлях папки має повторювати package.
Помилка №3: плутанина між settings.gradle.kts і build.gradle.kts.
Початківці іноді намагаються писати plugins { ... } у settings.gradle.kts або задавати rootProject.name у build.gradle.kts. Технічно є сценарії, де це можливо, але на старті це лише плутає. Запам’ятайте просту модель: ім’я проєкту — у settings, правила збирання — у build.
Помилка №4: неправильний рядок mainClass — друкарська помилка або не той пакет.
Якщо в build.gradle.kts стоїть mainClass = "com.example.ReadLaterApplication", а реальний клас — com.example.readlater.ReadLaterApplication, Gradle не зможе запустити застосунок. Ця помилка неприємна тим, що виглядає як «Gradle зламаний», хоча насправді помилився один рядок. Перевіряйте package у файлі й рядок mainClass як пару.
Помилка №5: створення «зайвих кишень» для ресурсів.
Іноді з’являються папки на кшталт resources/ у корені або src/resources. Потім туди кладуть конфіги, потім ще кудись — і за тиждень уже ніхто не пам’ятає, де що лежить. На старті найдешевша дисципліна — одразу створювати src/main/resources і класти не-Java файли туди, навіть якщо поки вони не використовуються.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ