Как работать с переменными окружения (Environment) в Spring?

Ответ

В Spring доступ к переменным окружения и свойствам приложения предоставляет интерфейс org.springframework.core.env.Environment. Это центральный абстракция для работы с конфигурацией.

Основные способы доступа:

  1. Инъекция через @Autowired:

    @Component
    public class MyService {
        @Autowired
        private Environment env;
    
        public void someMethod() {
            String dbUrl = env.getProperty("database.url");
            int port = env.getProperty("server.port", Integer.class, 8080); // Значение по умолчанию
            boolean isFeatureEnabled = env.getProperty("feature.flag", Boolean.class, false);
        }
    }
  2. Использование @Value:

    @Component
    public class MyComponent {
        // Получение значения свойства
        @Value("${api.key}")
        private String apiKey;
    
        // Со значением по умолчанию
        @Value("${cache.ttl:3600}")
        private int cacheTtl;
    
        // Получение переменной окружения ОС
        @Value("${JAVA_HOME}")
        private String javaHome;
    }

Источники свойств (PropertySources): Spring загружает свойства из различных источников в определенном порядке (поздние переопределяют ранние):

  1. Аргументы командной строки (--server.port=9090).
  2. Переменные окружения ОС (JAVA_HOME, DATABASE_URL).
  3. Файлы свойств из application-{profile}.properties / .yml.
  4. Файл application.properties / .yml.
  5. Аннотация @PropertySource.

Пример работы с профилями:

@Configuration
public class AppConfig {
    @Autowired
    Environment env;

    @Bean
    public DataSource dataSource() {
        // Создание бина в зависимости от активного профиля
        if (env.acceptsProfiles(Profiles.of("prod"))) {
            return createProductionDataSource();
        } else {
            return createEmbeddedDataSource();
        }
    }

    // Проверка наличия свойства
    public boolean isPropertySet(String key) {
        return env.containsProperty(key);
    }
}

Практические рекомендации:

  • Конфиденциальные данные: Храните пароли, API-ключи в переменных окружения, а не в файлах конфигурации, которые могут попасть в репозиторий.
  • 12-факторное приложение: Использование переменных окружения — один из ключевых принципов для конфигурации, зависящей от среды (dev, staging, prod).
  • Docker/Kubernetes: Конфигурация через env: в Docker Compose или ConfigMaps/Secrets в Kubernetes идеально ложится на эту модель Spring.

Ответ 18+ 🔞

А, ну ты про эту хуйню, про конфиги в Спринге! Слушай, тут всё просто, как три копейки, но если не знать — можно ебучу словить. Так вот, есть у них там такая штука — Environment. Это типа главный занюха в мире настроек твоего приложения, знает всё: от порта сервера до того, какой пароль у твоего продакшен-постгреса.

Как к нему подъебаться:

  1. Воткнуть напрямую, по-пацански (@Autowired):

    @Component
    public class MyService {
        @Autowired
        private Environment env; // Взял и впендюрил
    
        public void someMethod() {
            // Тянешь свойство, как из кармана
            String dbUrl = env.getProperty("database.url");
            // А если свойства нет — подставляем дефолт, чтоб не вылетало в пизду
            int port = env.getProperty("server.port", Integer.class, 8080);
            boolean isFeatureEnabled = env.getProperty("feature.flag", Boolean.class, false);
        }
    }
  2. Через аннотацию @Value — для ленивых и точечных:

    @Component
    public class MyComponent {
        // Просто говоришь — дай сюда api.key, и оно прилетает
        @Value("${api.key}")
        private String apiKey;
    
        // А если в конфигах пусто, то будет 3600. Красота, блядь.
        @Value("${cache.ttl:3600}")
        private int cacheTtl;
    
        // И даже системные переменные ОС вытащить можно, вот же ж хитрая жопа!
        @Value("${JAVA_HOME}")
        private String javaHome;
    }

Откуда эта тварь свойства берёт? (Источники, они же PropertySources) Тут, блядь, чёткая иерархия, как в армии: кто последний пришёл, тот и прав. Источники грузятся в порядке, и поздние перебивают ранние:

  1. Аргументы командной строки (типа --server.port=9090). Самый крутой пацан, всех перебивает.
  2. Переменные окружения ОС (JAVA_HOME, DATABASE_URL).
  3. Файлы профилей (application-prod.properties).
  4. Главный файл (application.properties).
  5. Что ты там ещё через @PropertySource наколхозил.

Пример с профилями, чтобы не облажаться на продакшене:

@Configuration
public class AppConfig {
    @Autowired
    Environment env; // Наш чёрный ящик

    @Bean
    public DataSource dataSource() {
        // Спрашиваем: "Чувак, мы на проде?"
        if (env.acceptsProfiles(Profiles.of("prod"))) {
            return createProductionDataSource(); // Да? Тогда серьёзную базу давай
        } else {
            return createEmbeddedDataSource(); // Нет? Ну хуй с ним, H2 сойдёт
        }
    }

    // Простая проверка: а есть ли такое свойство вообще, или я уже брежу?
    public boolean isPropertySet(String key) {
        return env.containsProperty(key);
    }
}

Жизненные советы, чтобы не было мучительно больно:

  • Пароли, ключи, секреты: Ты что, охуел? В файлы application.properties и в гит их пихать? Только переменные окружения, ёпта! Или специальные vault'ы. Иначе потом будешь плакать, как Герасим над Муму.
  • 12-факторное приложение: Это не просто модное словечко. Конфиг через переменные окружения — это святое. Один бинарник, а поведение меняется в зависимости от среды (dev, staging, prod). Красота.
  • Docker/Kubernetes: Тут эта модель просто пиздец как хорошо ложится. В Docker Compose пишешь environment:, в кубере — ConfigMaps да Secrets, и твоё приложение даже не заметит подмены. Как будто так и было задумано.