Для чего используются аннотации @ConditionalOn в Spring Boot? Приведите пример.

Ответ

Аннотации @ConditionalOn... в Spring Boot — это часть механизма автоконфигурации. Они позволяют условно регистрировать бины или конфигурационные классы в контексте приложения в зависимости от состояния среды (наличие класса, свойства, бина и т.д.).

Распространённые аннотации:

  • @ConditionalOnClass — если указанный класс присутствует в classpath.
  • @ConditionalOnMissingClass — если класс отсутствует.
  • @ConditionalOnBean / @ConditionalOnMissingBean — если бин присутствует/отсутствует.
  • @ConditionalOnProperty — если определённое свойство имеет заданное значение.
  • @ConditionalOnWebApplication / @ConditionalOnNotWebApplication — в зависимости от типа приложения.

Пример 1: Создание бина только при наличии класса в classpath

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyAutoConfiguration {
    // Бин будет создан, только если в classpath есть класс 'com.example.ExternalService'
    @Bean
    @ConditionalOnClass(name = "com.example.ExternalService")
    public MyService myService() {
        return new MyService();
    }
}

Пример 2: Условная конфигурация на основе свойства application.yml

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeatureConfiguration {
    // Бин будет зарегистрирован, только если свойство 'app.feature.enabled' равно 'true'
    @Bean
    @ConditionalOnProperty(prefix = "app.feature", name = "enabled", havingValue = "true")
    public AdvancedService advancedService() {
        return new AdvancedService();
    }
}
# application.yml
app:
  feature:
    enabled: true # Если false, бин advancedService не будет создан.

Пример 3: Создание бина по умолчанию, если другой отсутствует

@Configuration
public class CacheConfig {
    // Создаём бин defaultCache, только если бин с именем 'redisCache' НЕ найден.
    @Bean
    @ConditionalOnMissingBean(name = "redisCache")
    public Cache defaultCache() {
        return new SimpleInMemoryCache();
    }
}

Зачем это нужно? Позволяет Spring Boot автоматически настраивать приложение в зависимости от добавленных зависимостей (spring-boot-starter-*) и параметров, избегая конфликтов и лишней конфигурации.

Ответ 18+ 🔞

Да ты посмотри, какие у них, блядь, штуки придумали, эти спринговые мудрецы! Аннотации @ConditionalOn... — это ж как хитрая жопа, которая решает, что в твоём приложении жить будет, а что — в пизду отправится. Автоконфигурация, ёпта, магия!

Основные приколюхи:

  • @ConditionalOnClass — работает, только если нужный класс где-то в закромах classpath'а валяется.
  • @ConditionalOnMissingClass — наоборот, сработает, если класса нихуя нет.
  • @ConditionalOnBean / @ConditionalOnMissingBean — тут уже смотрят, есть ли в контексте какой-то конкретный бин или нет. Чисто интриги, блядь.
  • @ConditionalOnProperty — это вообще песня. Всё от настроек в application.yml зависит. Сказал true — живёшь, сказал false — иди нахуй.
  • @ConditionalOnWebApplication / @ConditionalOnNotWebApplication — чтобы понимать, веб-приложение у тебя или консольная поделка.

Пример первый: Хитрый бин, который прячется

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyAutoConfiguration {
    // Этот сервис вылезет наружу, только если найдёт в classpath'е 'com.example.ExternalService'.
    // Нет класса — нет сервиса. Всё просто, как три копейки.
    @Bean
    @ConditionalOnClass(name = "com.example.ExternalService")
    public MyService myService() {
        return new MyService();
    }
}

Пример второй: Включаем фичи через конфиг, а не кувалдой

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeatureConfiguration {
    // Смотри, какая хитрая жопа: пока ты в настройках не напишешь 'app.feature.enabled=true',
    // этот `AdvancedService` даже не подумает рождаться. Сидит, блядь, в засаде.
    @Bean
    @ConditionalOnProperty(prefix = "app.feature", name = "enabled", havingValue = "true")
    public AdvancedService advancedService() {
        return new AdvancedService();
    }
}
# application.yml
app:
  feature:
    enabled: true # Вот тут написал 'true' — и всё, поздравляю, бин на месте. Написал бы 'false' — и хуй с горы.

Пример третий: Запасной аэродром на случай пиздеца

@Configuration
public class CacheConfig {
    // Это гениально, ёпта! Спринг смотрит: "А есть тут у нас бин с именем 'redisCache'?"
    // Если нет — охуенно, создаём простенький `defaultCache`. Если вдруг какой-то умник уже зарегистрировал 'redisCache' —
    // то мы, скромные такие, отходим в сторонку. Никаких конфликтов, всё цивильно.
    @Bean
    @ConditionalOnMissingBean(name = "redisCache")
    public Cache defaultCache() {
        return new SimpleInMemoryCache();
    }
}

А нахуя это всё? Да чтобы голова не болела, вот нахуя! Добавил ты какую-нибудь зависимость spring-boot-starter-что-то-там — а фреймворк сам, как хитрая мартышлюшка, уже подобрал кучу умных @ConditionalOn... аннотаций в своих автоконфигах. Они смотрят: "А, так у тебя есть библиотека для Redis? О, отлично, вот тебе бин redisCache. Нету? Ну и хуй с тобой, живи без него". Волшебство, блядь, а не разработка.