Главная фишка Spring Boot — он почти ничего не заставляет настраивать руками. Добавили одну зависимость для работы с базой — и JdbcTemplate уже есть. Добавили зависимость для веба — и приложение само поднимает встроенный сервер. Разберём с нуля, как это устроено, откуда приложение берёт настройки и как держать разные настройки для разработки и для боевого окружения.
Стартеры — наборы зависимостей одним пунктом
Раньше, чтобы подключить, скажем, веб-слой, приходилось вручную перечислять десяток библиотек: сам Spring MVC, сервер, библиотеку для JSON, валидацию — и следить, чтобы их версии были совместимы. Одна несовместимая версия — и приложение падает на старте с непонятной ошибкой. Это долго и хрупко.
Стартер (starter) — это готовый набор зависимостей под одну задачу, собранный за вас. Подключаете один пункт — получаете всё нужное в согласованных версиях.
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web") // веб целиком
implementation("org.springframework.boot:spring-boot-starter-data-jpa") // работа с БД
}
Стартеры легко узнать по имени: они начинаются с spring-boot-starter-. Внутри стартера нет почти никакого кода — это просто список других библиотек. А вот настраивает их в рабочее состояние уже следующая часть.
Как Spring Boot сам настраивает бины
Подключили стартер для базы — и в приложении откуда-то появился готовый DataSource и JdbcTemplate, хотя вы их не создавали. Раньше каждый такой объект пришлось бы собирать руками: прочитать адрес базы, логин, пароль, создать пул соединений, связать всё вместе. Десятки строк однотипной настройки в каждом проекте.
Auto-configuration — это механизм, которым Spring Boot делает эту настройку за вас. Идея простая: «посмотри, какие библиотеки лежат в проекте, и настрой их разумным образом по умолчанию».
Включается всё одной аннотацией на главном классе:
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
@SpringBootApplication — это три аннотации в одной: пометка класса как конфигурации, сканирование ваших пакетов на @Component и @Service, и @EnableAutoConfiguration — та самая команда «настрой всё, что найдёшь в проекте».
Внутри стартеров лежат заранее написанные классы настройки. При старте Spring Boot перебирает их и для каждого решает: применять или нет.
Условные аннотации — настройка «если есть»
Откуда Spring Boot знает, что именно настраивать? Ведь в одном проекте есть база, а в другом — нет, и настройка базы там только мешала бы.
Ответ — условные аннотации. Каждый класс настройки помечен условиями, и применяется он, только если условия выполнены.
@AutoConfiguration
@ConditionalOnClass(DataSource.class) // только если класс DataSource есть в проекте
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean // только если вы не создали свой DataSource
@ConditionalOnProperty(name = "spring.datasource.url") // только если задан адрес базы
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
Самые частые условия и их смысл:
@ConditionalOnClass— применить, только если нужный класс есть в проекте. Нет библиотеки для базы — настройка базы просто пропускается.@ConditionalOnMissingBean— применить, только если такого бина ещё нет. Это значит ваши настройки всегда главнее: объявили свойDataSource— Spring Boot отступает и оставляет ваш.@ConditionalOnProperty— применить, только если в настройках стоит определённое значение. Удобно, чтобы включать и выключать что-то одной строкой.
Итог: вы кладёте в проект зависимости, а Spring Boot собирает из них работающий контекст, не мешая там, где вы что-то задали сами.
Почему бин не создался
Когда «должен был появиться бин, а его нет», не нужно гадать. Включите режим отладки:
debug=true
В логах при старте появится отчёт о принятых решениях: что подошло (совпавшие условия) и что отклонено и почему. Это первое место, куда стоит смотреть.
Настройки снаружи кода
Адрес базы, пароли, номер порта нельзя зашивать в код: для разработки одно значение, для боевого сервера другое, а пароли вообще не должны лежать в репозитории. Менять код и пересобирать приложение ради смены порта — плохой путь.
Spring Boot позволяет держать все такие значения снаружи — в файле настроек или в переменных окружения. Основной файл — application.yml (или application.properties) в проекте:
server:
port: 8080
spring:
datasource:
url: jdbc:postgresql://localhost:5432/shop
Одно и то же значение можно задать в нескольких местах, и у них есть приоритет. Запоминать весь список не нужно, достаточно главного правила: чем «ближе» к запуску задано значение, тем оно главнее. Аргумент командной строки перебивает переменную окружения, а та — файл application.yml.
java -jar app.jar --server.port=9000 # перебьёт порт из application.yml
На практике обычно так: общие настройки лежат в application.yml в репозитории; пароли и адреса для боевого сервера приходят через переменные окружения; а личные локальные правки — в application-local.yml, который не попадает в репозиторий.
@ConfigurationProperties и @Value
Настройки из файла нужно как-то прочитать в коде. Есть два способа.
@Value подходит для одного значения:
@Value("${app.timeout:5000}") // 5000 — значение по умолчанию, если в настройках пусто
private long timeoutMs;
Но когда связанных значений много, @Value приходится повторять у каждого поля, и легко ошибиться в имени.
@ConfigurationProperties связывает целую группу настроек с объектом — одним махом и с проверкой типов:
app:
retries:
max-attempts: 3
backoff-ms: 1000
timeout-ms: 5000
@ConfigurationProperties(prefix = "app.retries")
public record RetryProperties(int maxAttempts, long backoffMs, long timeoutMs) {}
Теперь RetryProperties можно внедрить как обычную зависимость, и все три значения уже на месте. Имена в YAML записываются через дефис (max-attempts), а в коде — как обычные поля (maxAttempts); Spring сам сопоставляет одно с другим.
Простое правило: одно-два значения — @Value; группа связанных настроек — @ConfigurationProperties.
Проверка настроек на старте
К группе настроек можно добавить правила, и тогда Spring проверит их при запуске:
@ConfigurationProperties(prefix = "app.retries")
@Validated
public record RetryProperties(
@Min(1) @Max(10) int maxAttempts,
@Positive long backoffMs,
@Positive long timeoutMs
) {}
Если кто-то задаст max-attempts: 0, приложение не запустится и сразу скажет, что не так. Это лучше, чем поймать ошибку позже, когда приложение уже работает и падает в неожиданном месте.
Профили — разные настройки для разных окружений
Для разработки вы хотите отправлять письма «понарошку» и писать подробные логи. На боевом сервере — отправлять настоящие письма и логировать сдержанно. Держать для этого разные сборки приложения неудобно и опасно.
Профиль — это пометка-режим. Бин или настройку можно привязать к профилю, и работать она будет только тогда, когда этот профиль включён.
@Service
@Profile("prod")
public class RealEmailService implements EmailService { } // только на боевом
@Service
@Profile({"dev", "test"})
public class FakeEmailService implements EmailService { } // в разработке и в тестах
Включить профиль можно настройкой spring.profiles.active:
spring:
profiles:
active: prod
Или через переменную окружения SPRING_PROFILES_ACTIVE=prod, или аргументом запуска --spring.profiles.active=prod. Можно включить сразу несколько через запятую.
Под профили есть и отдельные файлы настроек. Файл application-prod.yml накладывается поверх общего application.yml, когда активен профиль prod. Так удобно держать общие значения в одном месте, а различия между окружениями — в файлах по профилям: application-dev.yml, application-prod.yml.
Своя auto-configuration в библиотеке
Допустим, у вас есть переиспользуемый кусок инфраструктуры — обёртка над метрики или общий набор фильтров, — и вы хотите подключать его в разные проекты одной зависимостью, без копирования настроек. Тогда стоит оформить его как собственный стартер: он сам себя настроит у того, кто его подключил.
Принцип ровно тот же, что у встроенных стартеров: класс настройки с условными аннотациями плюс один служебный файл, который говорит Spring Boot «вот мой класс настройки, примени его».
@AutoConfiguration
@ConditionalOnClass(MyService.class)
@ConditionalOnMissingBean(MyService.class)
public class MyAutoConfiguration {
@Bean
public MyService myService() {
return new MyService();
}
}
Файл-указатель кладётся в src/main/resources по фиксированному пути META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports, и в нём — просто имя вашего класса:
com.example.mystarter.MyAutoConfiguration
После этого любой, кто добавит вашу библиотеку, получит MyService в контексте автоматически — и точно так же сможет переопределить его своим бином благодаря @ConditionalOnMissingBean. Именно так устроена, например, библиотека usecase-pattern: одна зависимость — и нужные бины уже на месте.
Коротко
- Стартер — готовый набор зависимостей под одну задачу в согласованных версиях; узнаётся по имени
spring-boot-starter-*. - Auto-configuration — механизм, который сам настраивает бины, глядя на подключённые библиотеки; включается через
@SpringBootApplication. - Условные аннотации решают, применять ли настройку:
@ConditionalOnClass(есть ли класс),@ConditionalOnMissingBean(нет ли уже бина),@ConditionalOnProperty(стоит ли значение). - Ваши собственные бины всегда главнее автоматических — за это отвечает
@ConditionalOnMissingBean. - Не создался бин — поставьте
debug=trueи посмотрите отчёт о принятых решениях при старте. - Настройки держат снаружи кода (
application.yml, переменные окружения); чем ближе значение к запуску, тем оно главнее. @Value— для одного значения;@ConfigurationProperties— для группы связанных настроек с проверкой типов.@Validatedна группе настроек роняет приложение на старте при неверном значении — ошибку видно сразу.- Профили дают разные настройки для разработки и боевого окружения;
application-<профиль>.ymlнакладывается поверх общего файла.
Что почитать дальше
- DI/IoC, bean lifecycle, scopes — как Spring создаёт бины, на которых строится auto-configuration.
- Spring AOP —
@EnableAsync,@EnableCaching,@EnableTransactionManagementтоже включаются через auto-configuration. - Spring Testing — как проверять
@ConfigurationPropertiesи профили в тестах. - Библиотека usecase-pattern — пример реального стартера со своей auto-configuration.