@SpringBootApplication объединяет три аннотации: @SpringBootConfiguration + @ComponentScan + @EnableAutoConfiguration. Первые две очевидны. Третья — магия, которая объясняет, почему JdbcTemplate, MongoTemplate, KafkaTemplate появляются в контексте сами, как только вы добавили starter.
Как работает auto-configuration
Старая модель (до Boot 2.7) — через spring.factories. Новая (с 2.7+, стандарт в 3.x) — через файл META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports, в котором перечислены классы auto-configuration.
# spring-boot-autoconfigure-3.x.jar содержит:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration
... (сотни классов)
Каждый класс — обычный @Configuration с @Bean-методами, обвешанный @ConditionalOn*-аннотациями:
@AutoConfiguration
@ConditionalOnClass(DataSource.class)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "spring.datasource.url")
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
При старте Boot читает все AutoConfiguration.imports, прогоняет условия (@ConditionalOnClass, @ConditionalOnMissingBean, @ConditionalOnProperty), оставляет только те, что соответствуют, и регистрирует их @Bean-ы. Бины пользователя имеют приоритет — @ConditionalOnMissingBean пропускает auto-configuration, если вы уже объявили свой.
Посмотреть, что было применено:
debug=true
И в логах появится CONDITIONS EVALUATION REPORT — Positive matches / Negative matches. Это первое место, куда смотреть, когда «почему-то бин не создался».
Externalized configuration
Источники свойств в порядке приоритета (от высокого к низкому):
- Command-line arguments:
--server.port=9000 SPRING_APPLICATION_JSONenv var- ServletConfig init params
- JNDI
- System properties (
-Dserver.port=9000) - OS environment (
SERVER_PORT=9000) application-{profile}.properties/.ymlapplication.properties/.yml@PropertySource-аннотации- Default properties (
SpringApplication.setDefaultProperties)
В практике: prod-конфиг — через env vars (Kubernetes ConfigMap/Secret), dev — application.yml в репозитории, локальный override — application-local.yml (в .gitignore).
@ConfigurationProperties vs @Value
@Value — для одиночного значения с поддержкой SpEL:
@Value("${app.timeout:5000}") // default 5000
private long timeoutMs;
@ConfigurationProperties — для группы свойств с type-safe биндингом:
# application.yml
app:
retries:
max-attempts: 3
backoff-ms: 1000
timeout-ms: 5000
@ConfigurationProperties(prefix = "app.retries")
public record RetryProperties(int maxAttempts, long backoffMs, long timeoutMs) {}
@Configuration
@EnableConfigurationProperties(RetryProperties.class)
public class AppConfig { }
@Service
public class OrderService {
public OrderService(RetryProperties props) { /* ... */ }
}
Правило: больше одного-двух связанных свойств → @ConfigurationProperties. Это даёт валидацию (@Validated), IDE-completion в YAML (через annotation processor spring-boot-configuration-processor), и метаданные для Spring Boot Configuration Metadata.
Валидация конфигурации
@ConfigurationProperties(prefix = "app.retries")
@Validated
public record RetryProperties(
@Min(1) @Max(10) int maxAttempts,
@Positive long backoffMs,
@Positive long timeoutMs
) {}
При старте Boot валидирует, если что-то не соответствует — приложение падает с понятной ошибкой, а не позже в рантайме на NPE.
Профили
Профиль — пометка на бине/конфигурации, активная или нет. Бины с неактивным профилем в контекст не попадают.
@Service
@Profile("prod")
public class RealEmailService implements EmailService { ... }
@Service
@Profile({"dev", "test"})
public class FakeEmailService implements EmailService { ... }
Активация:
spring.profiles.active=prod,metrics-on(несколько одновременно).SPRING_PROFILES_ACTIVE=prod(env var).--spring.profiles.active=prod(CLI).
application-{profile}.yml подгружается поверх application.yml для активных профилей. Полезно для per-environment различий: application-prod.yml, application-test.yml.
@Profile на @Configuration vs условные бины
Если выбор между двумя реализациями завязан на переключатель — лучше @ConditionalOnProperty, а не @Profile:
@Service
@ConditionalOnProperty(name = "email.backend", havingValue = "ses")
public class SesEmailService implements EmailService { ... }
@Service
@ConditionalOnProperty(name = "email.backend", havingValue = "smtp", matchIfMissing = true)
public class SmtpEmailService implements EmailService { ... }
Свойство explicit, документируется в application.yml, легко переопределяется без перепаковки артефакта.
Custom starter
Когда у вас есть переиспользуемая инфраструктура (библиотека для метрик, обвязка над брокером, security-фильтры), оформляйте её как starter — это снимает burden настройки с пользователей.
Структура минимального starter'а:
my-starter/
├── build.gradle.kts
├── src/main/java/com/example/mystarter/
│ ├── MyAutoConfiguration.java
│ └── MyProperties.java
└── src/main/resources/META-INF/spring/
└── org.springframework.boot.autoconfigure.AutoConfiguration.imports
@AutoConfiguration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService(MyProperties props) {
return new MyService(props.getApiUrl(), props.getTimeoutMs());
}
}
# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.mystarter.MyAutoConfiguration
Пользователь добавляет одну зависимость, и MyService появляется в контексте сам. Параметры — через my.api-url=... и my.timeout-ms=....
В open-source — это то, что библиотека usecase-pattern даёт для UseCase/Handler/Dispatcher: starter подключается одной зависимостью, auto-config регистрирует Dispatcher, метрики, validators.
Configuration metadata для IDE
При @ConfigurationProperties подключите annotation processor:
dependencies {
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
}
После сборки в META-INF/spring-configuration-metadata.json появится JSON с описаниями свойств, типов, default'ов. IntelliJ читает его и даёт autocomplete для YAML/properties.
Что почитать дальше
- DI/IoC, bean lifecycle, scopes — как Spring создаёт бины, на которых строится auto-configuration.
- Spring AOP —
@EnableAsync,@EnableCaching,@EnableTransactionManagement— это auto-configuration + AOP-прокси. - Spring Testing — тестирование
@ConfigurationPropertiesчерез@SpringBootTest. - Библиотека usecase-pattern — пример реального custom starter.