🍃 Раздел 5 · Вопрос #29

Что такое profiles in Spring

Один и тот же скомпилированный JAR-архив приложения должен работать:


🟢 Junior Level

Profiles (профили) в Spring Framework — это встроенный механизм сегментации конфигурации приложения, позволяющий регистрировать разные бины и применять различные свойства (application.properties / application.yml) в зависимости от текущего окружения запуска (например: local, dev, stage, prod).

Зачем нужны профили

Один и тот же скомпилированный JAR-архив приложения должен работать:

  • На машине разработчика (local): локальная база данных H2 или Docker-контейнер, включен подробный лог DEBUG.
  • В тестовом контуре (test): моки внешних платежных шлюзов, изолированная база данных.
  • В боевом окружении (prod): кластер PostgreSQL с пулом HikariCP, защищенные SSL-сертификаты, уровень логирования WARN или INFO.

Базовый пример с файлами настроек

Spring Boot автоматически подхватывает файл вида application-{profile}.yml в дополнение к базовому application.yml:

# application.yml (Общие настройки для всех окружений)
server:
  port: 8080

---
# application-dev.yml (Только для профиля dev)
spring:
  datasource:
    url: jdbc:h2:mem:devdb

---
# application-prod.yml (Только для профиля prod)
spring:
  datasource:
    url: jdbc:postgresql://prod-db.internal:5432/orders

Как активировать профиль

  1. Через аргумент командной строки при запуске JAR:
    java -jar app.jar --spring.profiles.active=prod
    
  2. Через переменную окружения ОС (стандарт в Docker и Kubernetes):
    export SPRING_PROFILES_ACTIVE=prod
    
  3. Через системный параметр JVM:
    java -Dspring.profiles.active=prod -jar app.jar
    

🟡 Middle Level

Аннотация @Profile над классами и @Bean-методами

Аннотация @Profile указывает контейнеру создавать бин только в том случае, если указанный профиль активен:

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
public class NotificationConfig {

    @Bean
    @Profile("dev") // Создается только при активном профиле dev
    public EmailSender mockEmailSender() {
        return new MockEmailSender(); // Выводит письма в консоль
    }

    @Bean
    @Profile("prod") // Создается только в prod
    public EmailSender realEmailSender() {
        return new SmtpEmailSender(); // Отправляет реальные письма через почтовый сервер
    }
}

Сложные логические выражения в @Profile (Spring 5.1+)

Начиная со Spring 5.1, аннотация @Profile поддерживает операторы булевой логики:

  • ! — логическое НЕ (NOT)
  • & — логическое И (AND)
  • | — логическое ИЛИ (OR)
  • Скобки () для группировки приоритетов
@Component
@Profile("prod & !cloud") // Активен в prod, но только если мы НЕ в облаке
public class OnPremiseMonitoringService { }

@Component
@Profile("dev | test") // Активен в dev ИЛИ в test
public class TestDataSeeder { }

@Component
@Profile("prod & (aws | gcp)") // Сложное составное условие
public class CloudStorageAdapter { }

Мульти-документные YAML файлы (Spring Boot 2.4+)

В современном Spring Boot все профили можно описать в одном файле application.yml, разделяя блоки разделителем --- и директивой spring.config.activate.on-profile:

server:
  port: 8080
spring:
  application:
    name: order-service

---
spring:
  config:
    activate:
      on-profile: dev
  datasource:
    url: jdbc:h2:mem:testdb

---
spring:
  config:
    activate:
      on-profile: prod
server:
  port: 443
spring:
  datasource:
    url: jdbc:postgresql://cluster.prod:5432/main

🔴 Senior Level

Группы профилей (Profile Groups)

В микросервисных системах отдельный сервис может требовать включения сразу нескольких аспектов (кэш, метрики, интеграция с Kafka). Вместо перечисления десятка профилей в строке запуска (--spring.profiles.active=prod,metrics,kafka,redis) настраивают Profile Groups:

# application.yml
spring:
  profiles:
    group:
      production:
        - prod
        - metrics
        - audit
        - redis-cache
      local-dev:
        - dev
        - h2-console
        - mock-external-apis

Теперь при передаче --spring.profiles.active=production Spring автоматически активирует все четыре связанных профиля.

Приоритет переопределения свойств (Property Precedence)

Если одно и то же свойство (например, server.port) объявлено в нескольких местах, Spring Boot применяет строгую иерархию приоритетов (от наивысшего к низшему):

1. Аргументы командной строки (--server.port=9090)
             ↓
2. Системные параметры JVM (-Dserver.port=9090)
             ↓
3. Переменные окружения ОС (SERVER_PORT=9090)
             ↓
4. Профиль-специфичные файлы вне JAR (config/application-prod.yml)
             ↓
5. Профиль-специфичные файлы внутри JAR (application-prod.yml)
             ↓
6. Дефолтные файлы конфигурации вне JAR (config/application.yml)
             ↓
7. Дефолтные файлы конфигурации внутри JAR (application.yml)

Правило: Настройки из application-prod.yml всегда переопределяют совпадающие свойства из базового application.yml.

Profiles vs Feature Flags: Архитектурная граница

Частая ошибка архитекторов — попытка использовать Spring Profiles для динамического включения бизнес-фичей.

Критерий Spring Profiles Feature Flags (Toggles)
Что настраивают Инфраструктура (БД, брокеры, окружения) Бизнес-логика (новый UI, алгоритм скидок)
Время переключения Строго при старте JVM (требует рестарта) В рантайме на лету (без рестарта за секунду)
Инструменты Spring Environment, K8s ConfigMaps LaunchDarkly, Unleash, Togglz, Redis
Гранулярность На уровне всего приложения / Pod Для конкретных пользователей или процентов трафика (Canary)

Защита от запуска Prod без профиля (Fail-Fast Validation)

Если забыть указать профиль при развертывании в Kubernetes, приложение запустится с профилем default (который часто настроен на тестовую БД или H2 в памяти), что приведет к аварии.

Паттерн обязательной валидации профиля:

package com.example.init;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import java.util.Arrays;

public class MandatoryProfileEnvironmentPostProcessor implements EnvironmentPostProcessor {

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        String[] activeProfiles = environment.getActiveProfiles();
        
        // Если запущено не в тесте и ни один профиль не активен — аварийная остановка!
        if (activeProfiles.length == 0) {
            throw new IllegalStateException(
                "ФАТАЛЬНАЯ ОШИБКА: Приложение не может быть запущено без указания активного профиля! "
                + "Задайте переменную SPRING_PROFILES_ACTIVE (например: dev, stage, prod)."
            );
        }
    }
}

🎯 Шпаргалка для интервью

30-секундный ответ

Spring Profiles — инструмент сегментации конфигурации приложения под разные окружения (dev, stage, prod).

  • Позволяет активировать профиль-специфичные файлы настроек (application-{profile}.yml), свойства которых имеют приоритет над базовым application.yml.
  • Аннотация @Profile над классами и @Bean-методами условно регистрирует бины в контексте. Начиная со Spring 5.1, поддерживаются логические выражения (prod & !cloud, dev | test).
  • В Spring Boot 2.4+ появились Profile Groups (spring.profiles.group) для объединения наборов профилей и мультидокументные YAML-файлы.
  • Профили задаются при старте JVM и не предназначены для динамического переключения бизнес-фичей на лету (для этого используются Feature Flags).

4 каверзных вопроса с ответами

  1. Как работают логические операторы в аннотации @Profile и с какой версии Spring они доступны? Ответ: Начиная со Spring Framework 5.1, в строке @Profile можно использовать операторы & (логическое И), | (логическое ИЛИ), ! (логическое НЕ) и круглые скобки для группировки условий (например: @Profile("(dev | test) & !docker")). Если условие ложно, ProfileCondition отбраковывает регистрацию BeanDefinition.

  2. В чём разница в приоритете между application.yml и application-prod.yml? Ответ: При активном профиле prod сначала загружается базовый application.yml, а затем поверх него накатывается application-prod.yml. Если одно и то же свойство объявлено в обоих файлах, значение из application-prod.yml переопределит дефолтное.

  3. Что такое Profile Groups в Spring Boot? Ответ: Механизм группировки, появившийся в Spring Boot 2.4. Позволяет через свойство spring.profiles.group.{groupName} объявить составной псевдоним для набора профилей (например: группа production автоматически активирует профили prod, db-postgres, metrics-datadog).

  4. В чём принципиальная разница между Spring Profiles и Feature Flags? Ответ: Profiles определяют статическую инфраструктурную конфигурацию и требуют обязательного перезапуска JVM для смены окружения. Feature Flags (фиче-тогглы) управляют поведением бизнес-логики и переключаются динамически в рантайме без перезапуска сервиса (часто индивидуально для конкретных пользователей).

Красные флаги (чего категорически нельзя говорить)

  • ❌ «Профили можно динамически переключать в рантайме без перезагрузки JVM» — профили определяют состав бинов в контексте, который замораживается после вызова context.refresh().
  • ❌ «Feature Flags и Spring Profiles — это одно и то же» — флаги меняют логику на лету, профили конфигурируют инфраструктуру на старте.
  • ❌ «Секретные пароли и токены продакшена безопасно коммитить в application-prod.yml» — секреты должны внедряться через переменные окружения, HashiCorp Vault или Kubernetes Secrets.
  • ❌ «Базовый application.yml имеет приоритет над application-prod.yml» — профильный файл всегда имеет приоритет над базовым.

Связанные темы