JavaRush /Курсы /Spring Core /Минимальный Gradle‑проект для Spring

Минимальный Gradle‑проект для Spring

Spring Core
3 уровень , 0 лекция
Открыта

1. Минимальный Gradle‑проект

Если начинать знакомство со Spring с фразы «вот вам аннотация @Something — и всё само заработало», очень быстро появляется ощущение магии и шаманства. Поэтому сегодня идём от земли: фиксируем сборку, версии и структуру проекта. Нам нужен проект, который у всех собирается одинаково и не меняет поведение из‑за того, что «у меня тут локально Java другая».

Есть и ещё одна причина. В предыдущие дни мы работали с кодом и архитектурой, а теперь меняем точку входа: вместо ручной сборки зависимостей в main постепенно передадим эту ответственность контейнеру. Если в этот момент сборка мутная, перегруженная или постоянно «случайно ломается», вы начнёте путать проблемы Gradle с проблемами Spring. Нам нужно ровно обратное: чтобы Spring‑механика была видна как на рентгене — без лишнего шума.

Single‑module проект

Когда человек впервые видит Spring‑проект, у него часто включаются две крайности. Первая — «всё в одном файле, потому что так проще». Вторая — «давайте сделаем 12 модулей Gradle, потому что я видел так на YouTube». На старте мешают обе: первый подход превращает проект в кашу, второй — в бюрократию, где вы больше объясняете сборку, чем контейнер.

Поэтому в курсе мы держим формат single‑module: один Gradle‑модуль, один понятный main, одна папка src/main/java, и весь домен ContextFlow живёт здесь же. Это не «игрушка», а нормальный формат для учебного non‑web приложения. Позже, когда вы дойдёте до модульности, будет гораздо легче: вы уже будете понимать, что делить и зачем, а не просто «как настроить модуль в Gradle».

Минимальная структура на сегодня выглядит так:

spring-core-contextflow/
├─ settings.gradle.kts
├─ build.gradle.kts
├─ gradlew
├─ gradlew.bat
├─ gradle/
│  └─ wrapper/...
├─ src/
│  ├─ main/
│  │  └─ java/...
│  └─ test/
│     └─ java/...

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

3. settings.gradle.kts: имя проекта

Очень хочется воспринимать любой .gradle файл как место, где можно «настроить вообще всё». Но у settings.gradle.kts роль предельно простая: он описывает набор проектов в сборке и базовые настройки. В single‑module варианте нам нужен буквально один параметр — имя проекта. Его потом будет видно в Gradle‑задачах и, например, в имени jar‑файла.

Именно поэтому важно не превращать settings.gradle.kts в «конфигурацию приложения». Это не Spring и не DI. Это сборка. Никаких бинов тут не будет, даже если очень попросить.

Файл: settings.gradle.kts

// Имя корневого проекта (будет видно в задачах Gradle и в имени артефакта)
rootProject.name = "spring-core-contextflow"

Пока всё. Серьёзно. Можно закрывать файл и не чувствовать вины.

4. build.gradle.kts: три базовых ответа

build.gradle.kts — это не «просто список зависимостей». Это ответ на три базовых вопроса: чем мы собираем код, откуда берём библиотеки и какие именно библиотеки нам нужны. Если держать в голове эти три вопроса, build.gradle.kts перестаёт быть мистическим свитком и превращается во вполне логичный документ.

Для курса важно, чтобы сборка была прозрачной. Поэтому мы используем обычный java‑плагин и, для удобного запуска, application. Мы не подключаем Spring Boot Gradle plugin, не используем spring-boot-starter-* как «стартовый набор» и не делаем ничего, что прячет от вас состав runtime. Spring Boot будет в следующем курсе. Здесь мы закладываем фундамент.

Начнём с минимума: плагины, Java toolchain и main class.

Файл: build.gradle.kts (кусок 1 — плагины и application)

plugins {
    // Базовая сборка Java-проекта (компиляция, тесты, jar)
    java
    // Добавляет задачи типа run + настройку mainClass
    application
}

application {
    // Точка входа для задачи ./gradlew run
    mainClass.set("com.example.contextflow.Application")
}

application‑плагин нужен не потому, что без него нельзя жить, а потому, что он делает жизнь проще: появится задача run, и вы сможете запускать проект одинаково у всех через Gradle, а не в режиме «у меня в IDE работает, а у тебя нет».

Теперь зафиксируем Java.

5. Java toolchain: фиксируем версию

Одна из самых частых проблем в учебных проектах звучит так: «У меня не компилируется, но я ничего не менял». А потом выясняется, что у одного Java 17, у другого 21, у третьего 25, а у четвёртого вообще «какая была». В такой ситуации вы изучаете не Spring, а археологию локального окружения.

Решение — Java toolchain. Это настройка Gradle, которая говорит: «собирай этот проект Java версии X». Gradle сам подберёт установленную Java или, в зависимости от настроек и окружения, скачает подходящую. Для курса фиксируем Java 25.

Файл: build.gradle.kts (кусок 2 — toolchain)

java {
    toolchain {
        // Фиксируем версию компилятора: сборка должна быть воспроизводимой у всех
        languageVersion.set(JavaLanguageVersion.of(25))
    }
}

Это не магия. Это просто способ сделать сборку воспроизводимой. И да, это тот редкий случай, когда одно число в конфиге экономит несколько часов жизни.

6. Репозитории: mavenCentral()

Когда вы пишете implementation("org.springframework:spring-context"), Gradle должен понять, где искать эту библиотеку. Ответ «в интернете» не подходит. Нужен конкретный репозиторий. В учебном проекте мы не поднимаем корпоративные Nexus/Artifactory, не подключаем десять репозиториев и не устраиваем конкурс «кто найдёт зависимость в более странном месте».

Для курса достаточно одного стабильного источника — Maven Central. Это такой «официальный супермаркет» для большинства Java‑библиотек.

Файл: build.gradle.kts (кусок 3 — repositories)

repositories {
    // Основной публичный репозиторий для зависимостей JVM-мира
    mavenCentral()
}

И всё. Если в учебном проекте внезапно оказывается 5 репозиториев, это почти всегда признак того, что кто-то пытался «быстро починить», а не аккуратно собрать.

7. Зависимости: Spring Core и BOM

С зависимостями в Spring есть две типичные неприятности. Первая — подключить слишком много и утонуть в «starter‑ах» и автоконфигурации, так и не поняв контейнер. Вторая — подключить слишком мало или не те версии и получить «класс не найден» там, где вы этого вообще не ждали. Поэтому сегодня фиксируем технический baseline курса.

Координаты зависимости: group:name:version

Зависимость в Gradle в строковой записи выглядит так: group:name:version. Это как адрес книги в огромной библиотеке. Например, org.springframework:spring-context:7.0.5 означает: «возьми модуль spring-context из группы org.springframework, версия 7.0.5».

Но есть проблема: когда библиотек много, держать версии в одном состоянии вручную сложно. Сегодня вы обновили spring-context, завтра забыли обновить spring-aop, и получаете идеальную лабораторную работу по теме «classpath hell». Эта лабораторная нам не нужна.

Spring Boot BOM как таблица версий

Мы используем Spring Boot BOM 4.0.3 как механизм выравнивания версий (dependency alignment). Важно: это не значит, что мы запускаем Spring Boot. BOM — это просто набор согласованных версий зависимостей. Можно воспринимать его как «таблицу совместимости», чтобы Spring‑модули не жили каждый в своей версии.

В Gradle это делается без дополнительных плагинов — через dependency platform.

Файл: build.gradle.kts (кусок 4 — подключаем BOM)

dependencies {
    // BOM фиксирует согласованные версии для экосистемы Spring
    implementation(platform("org.springframework.boot:spring-boot-dependencies:4.0.3"))
}

После этого можно подключать Spring‑модули без явного указания версий: BOM уже всё зафиксировал. Выглядит чуть менее «явно», зато резко снижает риск рассинхронизации.

Минимальный набор библиотек Spring

Нам нужен модуль, который даёт ApplicationContext и базовые контейнерные возможности, — это spring-context. Дополнительно сразу фиксируем пару модулей, которые пригодятся дальше по курсу: spring-aop и spring-expression. Плюс aspectjweaver как runtime‑компонент для AOP‑части. И сразу добавим spring-test на будущее тестирование контекста: сегодня его не используем, но baseline фиксируем сейчас.

Чтобы это не выглядело как «мы накидали рандомных строк», вот компактная таблица:

Зависимость Зачем она нам в курсе
org.springframework:spring-context ApplicationContext, AnnotationConfigApplicationContext, базовый запуск контейнера
org.springframework:spring-aop прокси‑механика и AOP‑возможности (позже в курсе)
org.springframework:spring-expression SpEL (позже в курсе, в ограниченном объёме)
org.aspectj:aspectjweaver инфраструктура для AOP‑части (подключаем заранее, используем позже)
org.springframework:spring-test тестирование Spring‑контекста (будет ближе к концу курса)
org.junit.jupiter:junit-jupiter тестовый движок JUnit 6

Файл: build.gradle.kts (кусок 5 — зависимости)

dependencies {
    // Таблица совместимых версий для Spring-экосистемы
    implementation(platform("org.springframework.boot:spring-boot-dependencies:4.0.3"))

    // Базовый контейнер Spring (ApplicationContext и friends)
    implementation("org.springframework:spring-context")
    // Инфраструктура AOP (прокси, advisors и т.д.)
    implementation("org.springframework:spring-aop")
    // Spring Expression Language (SpEL)
    implementation("org.springframework:spring-expression")

    // Нужен в рантайме для части сценариев AOP
    runtimeOnly("org.aspectj:aspectjweaver")

    // Тестовая поддержка Spring-контекста (ApplicationContext в тестах и т.п.)
    testImplementation("org.springframework:spring-test")
    // JUnit Jupiter для тестов
    testImplementation("org.junit.jupiter:junit-jupiter")
}

Обратите внимание: версию JUnit мы здесь тоже не фиксируем отдельной строкой. Смысл baseline как раз в том, чтобы не разбрасывать управление версиями по случайным местам и не собирать потом свой маленький classpath‑квест.

И ещё один важный момент: тут нет spring-boot-starter-* и нет Spring Boot Gradle plugin. Мы намеренно остаёмся в мире «чистого Spring Framework» и будем запускать контейнер вручную через AnnotationConfigApplicationContext. Так вы увидите механизм, а не «кнопку сделать красиво».

8. Флаг компилятора -parameters: включаем заранее

На этом месте уже хочется сказать: «ладно, давайте писать Spring‑код». Но есть одна компиляторная настройка, которую лучше включить сразу, пока проект маленький и не успел стать «непонятно почему не работает». Речь о флаге -parameters.

По умолчанию Java не обязана сохранять имена параметров методов и конструкторов в байткоде. Для человека это не страшно: вы в исходниках всё видите. А вот для фреймворка иногда важно различать параметры не только по типу, но и по имени, особенно когда дальше появятся более интересные сценарии связывания зависимостей. Поэтому мы включаем -parameters как часть baseline.

Файл: build.gradle.kts (кусок 6 — compilerArgs)

tasks.withType<JavaCompile>().configureEach {
    // Сохраняем имена параметров в байткоде (важно для DI/рефлексии/диагностики)
    options.compilerArgs.add("-parameters")
}

Сейчас эта настройка не даст вам «вау‑эффекта». Зато позже она предотвратит ситуации вида «почему Spring ведёт себя странно, когда кандидаты похожи». В инженерии это называется «прививка».

9. Точка входа: Application.java и ./gradlew run

Даже если мы собираемся запускать Spring‑контейнер, всё начинается с обычного Java main. На этом курсе мы не используем SpringApplication.run(...) — это уже история Spring Boot, — поэтому main остаётся нашей честной точкой входа. Сегодня он будет совсем простым: просто проверим, что проект собирается и запускается.

Важно: сразу привыкайте запускать проект через Gradle Wrapper (./gradlew), а не через «какой-то Gradle, который установлен в системе». Wrapper гарантирует, что у всех одна и та же версия Gradle (в курсе — 9.4), и избавляет от сюрпризов окружения.

package com.example.contextflow;

public class Application {

    public static void main(String[] args) {
        System.out.println("ContextFlow starting..."); // ContextFlow starting...
    }
}

Теперь запуск.

Команда:

./gradlew run

Если всё хорошо, вы увидите вывод:

ContextFlow starting...

Эта маленькая проверка важна: мы отделили «проект нормально собирается» от «Spring‑контейнер работает». В следующих лекциях мы добавим контейнерный старт, и если что-то сломается, вы будете понимать, где именно.

Заодно можно проверить и полную сборку:

./gradlew clean build

Gradle положит результаты в папку build/. И это хорошо: проект ничего не разбрасывает по системе, всё лежит в одном месте и легко чистится.

10. Типичные ошибки при подготовке Gradle‑baseline

Ошибка №1: подключить Spring Boot plugin или spring-boot-starter-*, «потому что так проще».
Это правда проще — но только в смысле «быстрее получить работающий результат». Цена такой простоты — потеря понимания того, что именно вы подключили и как стартует приложение. В этом курсе нам важнее увидеть механизмы контейнера без слоя автоконфигурации, поэтому мы сознательно остаёмся на spring-context и ручном bootstrap.

Ошибка №2: не фиксировать Java версию и собирать проект тем, что «нашлось в системе».
Выглядит безобидно, пока не окажется, что у одного студента var работает везде, у другого — не компилируется, а у третьего — неожиданно ругается на API, которого «вроде бы» нет. Java toolchain решает проблему на входе: мы выбираем Java 25 и собираем одинаково.

Ошибка №3: смешать версии Spring вручную.
Иногда хочется написать так: spring-context:7.0.5, а рядом spring-aop:7.0.2, потому что «оно у меня так скачалось». Потом возникают странные ошибки в рантайме, и вы тратите время не на Spring, а на борьбу с несовместимостью. BOM как раз нужен, чтобы такие вещи не происходили случайно.

Ошибка №4: забыть про -parameters, а потом удивляться странным сообщениям контейнера.
Сейчас всё будет работать и без этого флага. Но позже, когда контейнеру станет важнее различать зависимости, отсутствие имён параметров может сделать диагностику менее понятной, а поведение — более неожиданным для новичка. Включите флаг один раз и забудьте.

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

1
Задача
Spring Core, 3 уровень, 0 лекция
Недоступна
Минимальный Gradle-проект с одной точкой входа
Минимальный Gradle-проект с одной точкой входа
1
Задача
Spring Core, 3 уровень, 0 лекция
Недоступна
Baseline с BOM и флагом `-parameters`
Baseline с BOM и флагом `-parameters`
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
14 июня 2026
Всё в курсе мне пока нравится, но задачи, в которых фигурируют gradle, пакеты и всяческие пути - это дно. Например, здесь во второй задаче написал всё как по условию: System.out.println("springApi=org.springframework.context.ApplicationContext"); System.out.println("baseline=ready"); Не проходит. Поменял пакет в начале файла на свой (он даже не соответствовал тому, что написано в mainClass build.gradle) - прошло. Сдавать такие задачи это как тыкать пальцем в небо. У тебя и в IDE запускается, и ./gradlew run запускается. А чтобы пройти валидатор, нужно постараться.