Конфигурация
Модуль конфигурации отвечает за отображение значений из файлов конфигурации на классы в Kora и их последующее использование для настройки приложения.
HOCON¶
Поддержка HOCON реализована с помощью Typesafe Config. HOCON — это формат конфиг-файлов, основанный на JSON. Формат менее строгий нежели JSON и обладает слегка другим синтаксисом.
services {
foo {
bar = "SomeValue" //(1)!
baz = 10 //(2)!
propRequired = ${REQUIRED_ENV_VALUE} //(3)!
propOptional = ${?OPTIONAL_ENV_VALUE} //(4)!
propDefault = 10
propDefault = ${?NON_DEFAULT_ENV_VALUE} //(5)!
propReference = ${services.foo.bar}Other${services.foo.baz} //(6)!
propArray = ["v1", "v2"] //(7)!
propArrayAsString = "v1, v2" //(8)!
propMap = { //(9)!
"k1" = "v1"
"k2" = "v2"
}
propObject = { //(10)!
p1 = "v1"
p2 = "v2"
}
propObjects = [ //(11)!
{
p1 = "v1"
p2 = "v2"
},
{
p1 = "v3"
p2 = "v4"
}
]
}
}
- Cтроковое значение конфигурации
- Числовое значение конфигурации
- Обязательное значение конфигурации которое подставляется из переменной окружения
REQUIRED_ENV_VALUE - Необязательное значение конфигурации которое подставляется из переменной окружения
OPTIONAL_ENV_VALUE, если таковой переменной не найдено то значение конфигурации будет опущено - Значение конфигурации со значением по умолчанию, значение по умолчанию указывается в
propDefault = 10и если будет найдена переменная окруженияNON_DEFAULT_ENV_VALUEто ее значение заменит значение по умолчанию - Значение конфигурации собранное из подстановок других частей конфигурации и значением
Otherмежду - Значение конфигурации списка строк, значение задается как массив строк либо также можно задать как строку, где значения разделены запятыми
- Значение конфигурации списка строк, значение задается как строка, где значения разделены запятыми либо также можно задать как массив строк
- Значение конфигурации в виде словаря ключ и значение
- Значение конфигурации в виде отображенного класса
- Значение конфигурации в виде списка отображенных классов
Отображение конфигурации в коде:
@ConfigSource("services.foo")
public interface FooConfig {
String bar();
Integer baz();
String propRequired();
@Nullable
String propOptional();
Integer propDefault();
String propReference();
List<String> propArray();
List<String> propArrayAsString();
Map<String, String> propMap();
@ConfigValueExtractor
public interface ObjectConfig {
String p1();
String p2();
}
ObjectConfig propObject();
List<ObjectConfig> propObjects();
}
@ConfigSource("services.foo")
interface FooConfig {
fun bar(): String
fun baz(): Int
fun propRequired(): String
fun propOptional(): String?
fun propDefault(): Int
fun propReference(): String
fun propArray(): List<String>
fun propArrayAsString(): List<String>
fun propMap(): Map<String, String>
@ConfigValueExtractor
interface ObjectConfig {
fun p1(): String
fun p2(): String
}
fun propObject(): ObjectConfig
fun propObjects(): List<ObjectConfig>
}
Подключение¶
Зависимость build.gradle:
Модуль:
Зависимость build.gradle.kts:
Модуль:
Файл¶
По умолчанию ожидаются файлы конфигурации reference.conf и application.conf
Во-первых, все файлы reference.conf объединяются, во-вторых, файл application.conf накладывается на неразрешенный
файл reference.conf, результат вычисляется и проверяется что все значения переменных доступны.
Предполагается что конфигурация приложения находится в файле application.conf, а конфигурации библиотек в reference.conf.
Приоритет считывания application.conf файла конфигурации:
- Использовать файл из
config.resourceесли указан (файл изresourcesдиректории) - Использовать файл из
config.fileесли указан (файл из файловой системы) - Использовать файл
application.confесли имеется (файл изresourcesдиректории) - Используется пустой файл конфигурации если все указанное выше отсутствует
YAML¶
Поддержка YAML реализована с помощью SnakeYAML.
services:
foo:
bar: "SomeValue" #(1)!
baz: 10 #(2)!
propRequired: ${REQUIRED_ENV_VALUE} #(3)!
propOptional: ${?OPTIONAL_ENV_VALUE} #(4)!
propDefault: ${?NON_DEFAULT_ENV_VALUE:10} #(5)!
propReference: ${services.foo.bar}Other${services.foo.baz} #(6)!
propArray: ["v1", "v2"] #(7)!
propArrayAsString: "v1, v2" #(8)!
propMap: #(9)!
k1: "v1"
k2: "v2"
propObject: #(10)!
p1: "v1"
p2: "v2"
propObjects: #(11)!
- p1: "v1"
p2: "v2"
- p1: "v1"
p2: "v2"
- Cтроковое значение конфигурации
- Числовое значение конфигурации
- Обязательное значение конфигурации которое подставляется из переменной окружения
REQUIRED_ENV_VALUE - Необязательное значение конфигурации которое подставляется из переменной окружения
OPTIONAL_ENV_VALUE, если таковой переменной не найдено то значение конфигурации будет опущено - Значение конфигурации со значением по умолчанию, значение по умолчанию равно
10и если будет найдена переменная окруженияNON_DEFAULT_ENV_VALUEто ее значение заменит значение по умолчанию - Значение конфигурации собранное из подстановок других частей конфигурации и значением
Otherмежду - Значение конфигурации списка строк, значение задается как массив строк либо также можно задать как строку, где значения разделены запятыми
- Значение конфигурации списка строк, значение задается как строка, где значения разделены запятыми либо также можно задать как массив строк
- Значение конфигурации в виде словаря ключ и значение
- Значение конфигурации в виде отображенного класса
- Значение конфигурации в виде списка отображенных классов
Отображение конфигурации в коде:
@ConfigSource("services.foo")
public interface FooConfig {
String bar();
Integer baz();
String propRequired();
@Nullable
String propOptional();
Integer propDefault();
String propReference();
List<String> propArray();
List<String> propArrayAsString();
Map<String, String> propMap();
@ConfigValueExtractor
public interface ObjectConfig {
String p1();
String p2();
}
ObjectConfig propObject();
List<ObjectConfig> propObjects();
}
@ConfigSource("services.foo")
interface FooConfig {
fun bar(): String
fun baz(): Int
fun propRequired(): String
fun propOptional(): String?
fun propDefault(): Int
fun propReference(): String
fun propArray(): List<String>
fun propArrayAsString(): List<String>
fun propMap(): Map<String, String>
@ConfigValueExtractor
interface ObjectConfig {
fun p1(): String
fun p2(): String
}
fun propObject(): ObjectConfig
fun propObjects(): List<ObjectConfig>
}
Подключение¶
Зависимость build.gradle:
Модуль:
Зависимость build.gradle.kts:
Модуль:
Файл¶
По умолчанию ожидаются файлы конфигурации reference.yaml и application.yaml.
Во-первых, все файлы reference.yaml объединяются, во-вторых, файл application.yaml накладывается на неразрешенный
файл reference.yaml, результат вычисляется и проверяется что все значения переменных доступны.
Предполагается что конфигурация приложения находится в файле application.yaml, а конфигурации библиотек в reference.yaml.
Приоритет считывания application.yaml файла конфигурации:
- Использовать файл из
config.resourceесли указан (файл изresourcesдиректории) - Использовать файл из
config.fileесли указан (файл из файловой системы) - Использовать файл
application.yamlесли имеется (файл изresourcesдиректории) - Используется пустой файл конфигурации если все указанное выше отсутствует
Пользовательские конфигурации¶
Пользовательская конфигурация предоставляет собой отображение файла конфигурации на пользовательский интерфейс. Такой пользовательский интерфейс в последствии может быть внедрен как зависимость наравне с другими компонентами.
В приложении¶
Для создания пользовательских конфигураций следует использовать аннотацию @ConfigSource:
Этот пример кода добавит в контейнер экземпляр класса FooServiceConfig, который при создании будет ожидать конфигурацию следующего вида:
После этого класс FooServiceConfig уже можно использовать как зависимость в других классах:
В библиотеке¶
Для создания пользовательских конфигураций в рамках пользовательских библиотеках следует использовать аннотацию @ConfigValueExtractor
которая создаст правила обработки файла конфигурации в экземпляр класса конфигурации.
Рассмотрим пример когда есть такой класс конфигурации:
Для того чтобы библиотека предоставляла конфигурацию, требуется реализовать фабрику в модуле:
Фабрика будет ожидать конфигурацию следующего вида:
Затем подключив модуль FooLibraryModule в приложении, конфиг FooServiceConfig можно использовать как зависимость в других классах.
Обязательные значения¶
По умолчанию все значения объявленные в конфиге считаются обязательными (NotNull) и должны присутствовать в файле конфигурации.
Необязательные значения¶
Если есть необходимость указать значение из файла конфигурации как необязательное, то можно воспользоваться таким форматом:
Предлагается использовать аннотацию @Nullable над сигнатурой метода:
@ConfigSource("services.foo")
public interface FooServiceConfig {
@Nullable//(1)!
String bar();
int baz();
}
- Подойдет любая аннотация
@Nullable, такие какjavax.annotation.Nullable/jakarta.annotation.Nullable/org.jetbrains.annotations.Nullable/ и т.д.
Предполагается использовать Kotlin Nullability синтаксис и помечать такой параметр как Nullable:
Значения по умолчанию¶
Если есть необходимость использовать задать в отображении значение по умолчанию, то можно воспользоваться default модификатором:
Внедрение конфигурации¶
Можно внедрять базовый класс ru.tinkoff.kora.config.common.Config который предоставляет из себя общую абстракцию над
отображением файла конфигурации. Результирующие отображение конфигурации состоит из нескольких слоев которые представляют из себя:
- Переменные окружения
- Системные переменные
- Файл конфигурации
Переменные окружения¶
В случае если требуется внедрить конфигурацию только переменных окружения,
то для этого можно использовать аннотацию @Environment как тег для класса конфигурации:
Системные переменные¶
В случае если требуется внедрить конфигурацию только системных переменных,
то для этого можно использовать аннотацию @SystemProperties как тег для класса конфигурации:
Файл конфигурации¶
В случае если требуется внедрить полную конфигурацию приложения которая состоит только из файла конфигурации,
то для этого можно использовать аннотацию @ApplicationConfig как тег для класса конфигурации:
Результирующая конфигурация¶
В случае если требуется внедрить полную конфигурацию приложения которая состоит из файла конфигурации, переменных окружения и системных переменных, то для этого требуется просто внедрить класс конфигурации без тега:
Совет¶
Совет
Мы не советуем использовать напрямую ru.tinkoff.kora.config.common.Config как зависимость в компонентах,
так как при обновлении конфигурации это повлечет обновление всех компонент графа которые используют его у себя,
рекомендуется всегда создавать пользовательские конфигурации.
Наблюдатель¶
По умолчанию в Kora работает наблюдатель за файлом конфигурации который обновляет его содержимое, что влечет в случае изменения файла конфигурации обновление графа зависимостей для компонент которые затронули изменения.
Можно отключить наблюдатель с помощь:
- Переменной окружения
KORA_CONFIG_WATCHER_ENABLED - Системного свойства
kora.config.watcher.enabled
Поддерживаемые типы¶
Экстракторы конфигурации предоставляют обширный список поддерживаемых типов, который охватывает большинство из того,
что вам может понадобиться для указания в пользовательских конфигурациях, либо вы можете расширить поведение собственным ConfigValueExtractor<T> компонентом.
Список поддерживаемых типов
- boolean / Boolean
- short / Short
- int / Integer
- long / Long
- double / Double
- float / Float
- double[]
- String
- BigInteger
- BigDecimal
- Period
- Duration
- Size
- Properties
- Pattern
- UUID
- Properties
- LocalDate
- LocalTime
- LocalDateTime
- OffsetTime
- OffsetDateTime
- Enum (любой пользовательский ENUM тип) (Переопределить соответствие можно через переопределение
toString()) List<T>(гдеTлюбой из выше перечисленных типов)Set<T>(гдеTлюбой из выше перечисленных типов)Map<K, V>(гдеKилиVлюбой из выше перечисленных типов)Either<A, B>(гдеAиBлюбой из выше перечисленных типов)
Размер¶
Size - специальный тип который позволяет задавать размер байт в удобной человеку системе исчислений по стандарту IEEE 1541—2002 (двоичный), так и в стандарте СИ (десятичный).
Примеры значений:
1Mb- 1 мегабайт (1.000.000байт)1Mib- 1 мегабит (1.048.576байт)1024b- 1024 байт1024- 1024 байт
Если указано просто число без суффикса, то считается что указаны байты.