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 файлы: конфиги, шаблоны, sample 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 is running"); // ReadLater Starter is running
}
}
Теперь добавим второй класс, чтобы сразу почувствовать: проект — это не один файл, и это нормально. Создайте 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». На самом деле это очень практичная часть проекта: сюда кладут файлы, которые должны попасть в сборку и быть доступны приложению как ресурсы. Конфигурация, шаблоны, sample 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`.
## Requirements
- JDK 25
## Build & Run (macOS / Linux)
./gradlew build
./gradlew run
## Build & 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 файлы туда, даже если пока они не используются.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ