Ответ
@Conditional — это базовая аннотация Spring для условной регистрации бинов в контексте приложения. Условие определяется реализацией интерфейса Condition.
Принцип работы: Spring проверяет условие, заданное в @Conditional, и регистрирует бин только если оно выполняется (matches() возвращает true).
Пример с пользовательским условием:
@Configuration
public class FeatureConfig {
// Бин зарегистрируется, только если выполнится MyFeatureCondition
@Bean
@Conditional(MyFeatureCondition.class)
public MyService myService() {
return new MyService();
}
}
public class MyFeatureCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// Проверяем наличие системного свойства или переменной окружения
String featureFlag = context.getEnvironment().getProperty("my.feature.enabled");
return "true".equalsIgnoreCase(featureFlag);
}
}
Встроенные производные аннотации (более удобные):
Spring Boot предоставляет множество готовых аннотаций на основе @Conditional:
@ConditionalOnProperty: Зависит от значения свойства.@Bean @ConditionalOnProperty(name = "cache.provider", havingValue = "redis") public CacheManager redisCacheManager() { ... }@ConditionalOnClass/@ConditionalOnMissingClass: Зависит от наличия/отсутствия класса в classpath.@Bean @ConditionalOnClass(name = "com.example.SomeClient") public SomeService someService() { ... }@ConditionalOnBean/@ConditionalOnMissingBean: Зависит от наличия/отсутствия другого бина в контексте.@Bean @ConditionalOnMissingBean(DataSource.class) public DataSource inMemoryDataSource() { ... }@ConditionalOnWebApplication/@ConditionalOnNotWebApplication: Зависит от типа приложения.
Основное применение: Создание автоконфигураций, включение фич по флагам, выбор реализации в зависимости от окружения.
Ответ 18+ 🔞
А, слушай, вот эта аннотация @Conditional — это же, блядь, просто песня, а не инструмент! Представь себе: ты такой сидишь, настраиваешь свой Spring-контекст, и тут тебе надо, чтобы бин появлялся не просто так, а только если какое-то условие выполнилось. Ну, например, если в настройках флажок my.feature.enabled стоит true, или если в classpath какая-то библиотека лежит. Вот тут-то она, родимая, и выручает.
По сути, это такая прищепка на контекст, которая говорит: «Стой, блядь, не регистрируй этот бин, пока не проверишь вот эту хуйню». А проверяет она её через интерфейс Condition, где ты сам пишешь логику в методе matches(). Вернул true — бин живой, вернул false — бин отправился в утиль, даже не родившись.
Вот смотри, как это выглядит в коде, чтоб ты проникся:
@Configuration
public class FeatureConfig {
// Этот бин Spring создаст только если MyFeatureCondition скажет "да, блядь, можно"
@Bean
@Conditional(MyFeatureCondition.class)
public MyService myService() {
return new MyService();
}
}
public class MyFeatureCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// Лезу в настройки, ищу свойство
String featureFlag = context.getEnvironment().getProperty("my.feature.enabled");
// Если там "true", то всё, условие выполнено, бин будет
return "true".equalsIgnoreCase(featureFlag);
}
}
Но это, понимаешь, ручное управление, как на тракторе. А Spring Boot, он же для ленивых, как я. Он посмотрел на это и говорит: «Да зачем вам каждый раз свою хуйню писать?». И наделал кучу готовых, обёрнутых аннотаций. Это просто огонь, ёпта!
@ConditionalOnProperty: Самый частый гость. Бин только если вapplication.ymlили в свойствах нужная запись есть.@Bean @ConditionalOnProperty(name = "cache.provider", havingValue = "redis") // Если cache.provider=redis, то окей public CacheManager redisCacheManager() { ... }@ConditionalOnClass/@ConditionalOnMissingClass: Тут вообще магия. Spring смотрит в classpath. «О, а библиотекаcom.example.SomeClientесть? Нет? Ну тогда и бин твой, сука, не нужен».@Bean @ConditionalOnClass(name = "com.example.SomeClient") public SomeService someService() { ... }@ConditionalOnBean/@ConditionalOnMissingBean: А это уже интриги внутри самого контекста. «Так-так, аDataSourceу нас уже есть? Есть? Ну тогда свойinMemoryDataSourceможешь, блядь, засунуть обратно, он не нужен».@Bean @ConditionalOnMissingBean(DataSource.class) // Создам бин, только если ещё нет ни одного DataSource public DataSource inMemoryDataSource() { ... }@ConditionalOnWebApplication/@ConditionalOnNotWebApplication: Ну это совсем просто. Веб-приложение у нас или консольное? От этого зависит.
Зачем это всё, спросишь? Да овердохуища применений! Автоконфигурации в Spring Boot на этом, блядь, построены. Включение экспериментальных фич по флагам, выбор реализации под разные окружения (dev, prod), чтобы не тащить в продакшен отладочные сервисы. В общем, вещь архиполезная, хоть и выглядит как магия. Главное — не переборщить, а то контекст превратится в головоломку, где ни один бин не знает, выживет ли он после старта.