Конфигурация: application.yml + профили
Цель урока
Сделать полноценный CRUD для задач с правильными HTTP-статусами (201/204/404), обработкой ошибок, и подготовить Task к подключению JPA.
Теория · для собеса
1 Откуда Spring Boot берёт настройки (приоритет, от высокого к низкому) ▸
- Devtools global settings (~/.spring-boot-devtools.properties)
- @TestPropertySource` на тестах
- Command line args (
--server.port=9090) - Environment variables (
SERVER_PORT=9090) application-{profile}.yml(например,application-prod.yml)application.yml(общий)- Defaults (из кода библиотек)
2
Файл application.yml vs application.properties
▸
.yml— читабельнее, иерархия.properties— проще, плоский- Можно использовать оба одновременно (
.ymlзагружается первым)
3 Профили ▸
Профиль = набор конфигурации для конкретного окружения (dev, test, prod).
Активация:
application.yml:spring.profiles.active: dev- Env var:
SPRING_PROFILES_ACTIVE=prod - JVM arg:
-Dspring.profiles.active=prod - В
@SpringBootTest:@ActiveProfiles("test")
При запуске с dev Spring читает:
application.yml(все настройки)application-dev.yml(специфичные для dev, перезаписывают общие)
---
Практика: улучшаем CRUD
-
1
application.yml(общий)spring: application: name: taskflow profiles: active: dev # профиль по умолчанию server: port: 8080 servlet: context-path: / taskflow: app: version: 0.1.0 pagination: default-page-size: 20 max-page-size: 100 -
2
application-dev.ymlserver: port: 8080 logging: level: com.taskflow: DEBUG org.springframework.web: DEBUG org.hibernate.SQL: DEBUG org.hibernate.orm.jdbc.bind: TRACE spring: datasource: url: jdbc:postgresql://localhost:5432/taskflow_dev username: taskflow password: dev_password driver-class-name: org.postgresql.Driver jpa: hibernate: ddl-auto: validate show-sql: true -
3
application-prod.ymlserver: port: 80 compression: enabled: true mime-types: application/json,text/html,text/plain logging: level: com.taskflow: INFO root: WARN file: name: /var/log/taskflow/app.log spring: datasource: url: ${DB_URL} # из ENV-переменной username: ${DB_USERNAME} password: ${DB_PASSWORD} hikari: maximum-pool-size: 20 minimum-idle: 5 jpa: hibernate: ddl-auto: validate show-sql: false open-in-view: false -
4
application-test.ymlspring: datasource: url: jdbc:tc:postgresql:16:///taskflow_test jpa: hibernate: ddl-auto: create-drop -
5Типизированная конфигурация:
@ConfigurationPropertiesПродолжаем тот же TaskFlow из урока 3 и добавляем новый файл конфигурации:
config/AppProperties.java.package com.taskflow.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @Configuration @ConfigurationProperties(prefix = "taskflow.app") public class AppProperties { private String version = "0.0.0"; private Pagination pagination = new Pagination(); public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public Pagination getPagination() { return pagination; } public void setPagination(Pagination pagination) { this.pagination = pagination; } public static class Pagination { private int defaultPageSize = 20; private int maxPageSize = 100; public int getDefaultPageSize() { return defaultPageSize; } public void setDefaultPageSize(int defaultPageSize) { this.defaultPageSize = defaultPageSize; } public int getMaxPageSize() { return maxPageSize; } public void setMaxPageSize(int maxPageSize) { this.maxPageSize = maxPageSize; } } }Использование в уже существующем
TaskService: не меняем репозиторий, а только нормализуем входной параметр.@Service public class TaskService { private final AppProperties props; public TaskService(AppProperties props) { this.props = props; } public int normalizePageSize(int requestedSize) { int max = props.getPagination().getMaxPageSize(); int defaultSize = props.getPagination().getDefaultPageSize(); if (requestedSize <= 0) { return defaultSize; } return Math.min(requestedSize, max); } } -
6Показываем version в
/api/health@RestController @RequestMapping("/api/health") public class HealthController { private final AppProperties props; public HealthController(AppProperties props) { this.props = props; } @GetMapping public Maphealth() { return Map.of( "status", "UP", "version", props.getVersion(), "timestamp", Instant.now().toString() ); } } -
7Запуск с разными профилями
# dev (по умолчанию) ./gradlew bootRun # prod SPRING_PROFILES_ACTIVE=prod ./gradlew bootRun # С аргументами JVM ./gradlew bootRun --args='--spring.profiles.active=prod --server.port=80' # С переменными окружения (как в Docker/K8s) SPRING_PROFILES_ACTIVE=prod \ DB_URL=jdbc:postgresql://prod-db:5432/taskflow \ DB_USERNAME=app \ DB_PASSWORD=secret \ ./gradlew bootRun---
Зачем это на собесе
После урока ты должен уметь ответить на:
env: в compose / Secret в k8s — нельзя хардкодить.@ConfigurationProperties — типизированная конфигурация, проверяется на этапе запуска (если поля нет в yaml — приложение упадёт сразу, а не в рантайме).5 вопросов на углубление
Раскрой вопрос и нажми «🤔 Хочу разобрать подробнее» — он попадёт в страницу ответов.
Разбор внутри: одиночные настройки против типизированного объекта конфигурации.
Разбор внутри: дефолты, fail-fast и защита от опечаток в YAML.
Разбор внутри: placeholders, дефолты и relaxed binding ENV-переменных.
Разбор внутри: граница транзакции, lazy loading и неожиданные SQL-запросы из JSON-сериализации.
Разбор внутри: production/staging/local как удобные группы профилей.
Готов идти дальше?
Выбери вопросы, которые тебе интересны, и изучи их. Потом — к следующему уроку.
🚀 Перейти к Уроку 5Сначала пройди все секции и выбери хотя бы 1 вопрос
⬅️ Назад к Уроку 3