Конфигурация
Модуль конфигурации читает настройки приложения из файлов HOCON или YAML, переменных окружения, системных свойств
Java и отображает их на типизированные классы в Kora. Полученные объекты конфигурации становятся обычными
компонентами графа зависимостей и могут внедряться в сервисы, клиенты, серверы и другие интеграции.
В Kora конфигурация приложения обычно описывается интерфейсом с аннотацией @ConfigSource: путь в файле указывает на
читаемую секцию, а методы интерфейса описывают обязательные значения, необязательные значения и значения по умолчанию.
Библиотеки и переиспользуемые формы конфигурации используют @ConfigMapper, который создаёт только правило
отображения, тогда как конкретный путь выбирается в модуле библиотеки.
Для пошагового разбора перед справочным описанием смотрите Конфигурация HOCON и Конфигурация YAML.
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"
}
]
}
}
- Строковое значение конфигурации
- Числовое значение конфигурации
- Обязательное значение конфигурации, подставляемое из переменной окружения
REQUIRED_ENV_VALUE - Необязательное значение конфигурации, подставляемое из переменной окружения
OPTIONAL_ENV_VALUE; если переменная не найдена, значение конфигурации отсутствует - Значение конфигурации со значением по умолчанию: значение по умолчанию задано как
propDefault = 10, аNON_DEFAULT_ENV_VALUE, если найдено, заменяет его - Значение конфигурации, собранное из подстановок других частей конфигурации со значением
Otherмежду ними - Значение конфигурации в виде списка строк; значение можно задать массивом строк либо строкой с разделителем-запятой
- Значение конфигурации в виде списка строк; значение можно задать строкой с разделителем-запятой либо массивом строк
- Значение конфигурации в виде словаря «ключ-значение»
- Значение конфигурации в виде отображаемого класса
- Значение конфигурации в виде списка отображаемых классов
Значения могут ссылаться и на другие ключи конфигурации (внутренние и перекрёстные ссылки) через ${path}, и на
переменные окружения через ${VAR} (обязательная подстановка) или ${?VAR} (необязательная подстановка). Значение по
умолчанию задаётся принятым в HOCON способом: ключ присваивается дважды — сначала запасным литеральным значением, затем
необязательной подстановкой, как это сделано для propDefault выше. Подстановки внутри файла HOCON разрешает
Typesafe Config уже после слияния всех слоёв этого файла, поэтому ссылка может указывать на ключ, объявленный в другом
файле HOCON, подключённом через include.
Отображение конфигурации в коде:
@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();
@ConfigMapper
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>
@ConfigMapper
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 из classpath, затем поверх неразрешённого reference.conf накладывается
application.conf, а поверх него — системные свойства Java. Только после сборки всего стека результат разрешается и
проверяются обязательные подстановки.
Конфигурация приложения ожидается в application.conf, а конфигурация библиотек — в reference.conf.
HOCON также поддерживает директиву include:
подключённые через include файлы участвуют в том же слиянии и разрешении подстановок, что и основной файл.
Подключения, которые указывают на реальные файлы на диске, дополнительно отслеживаются наблюдателем за конфигурацией,
поэтому изменение подключённого файла тоже обновляет граф; подключения ресурсов classpath и URL не отслеживаются.
Приоритет выбора файла приложения для HOCON:
- Используется файл из
config.resource, если он указан (файл из директорииresources) - Используется файл из
config.file, если он указан (файл из файловой системы) - Используется
application.conf, если он присутствует (файл из директорииresources) - Используется пустая конфигурация, если ничего из вышеперечисленного нет
Одновременно можно указать только одно свойство: config.resource либо config.file. Если указаны оба свойства,
приложение упадёт при старте с ошибкой Application config source is ambiguous.
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"
- Строковое значение конфигурации
- Числовое значение конфигурации
- Обязательное значение конфигурации, подставляемое из переменной окружения
REQUIRED_ENV_VALUE - Необязательное значение конфигурации, подставляемое из переменной окружения
OPTIONAL_ENV_VALUE; если переменная не найдена, значение конфигурации отсутствует - Значение конфигурации со значением по умолчанию: значение по умолчанию —
10, аNON_DEFAULT_ENV_VALUE, если найдено, заменяет его - Значение конфигурации, собранное из подстановок других частей конфигурации со значением
Otherмежду ними - Значение конфигурации в виде списка строк; значение можно задать массивом строк либо строкой с разделителем-запятой
- Значение конфигурации в виде списка строк; значение можно задать строкой с разделителем-запятой либо массивом строк
- Значение конфигурации в виде словаря «ключ-значение»
- Значение конфигурации в виде отображаемого класса
- Значение конфигурации в виде списка отображаемых классов
У YAML нет собственного синтаксиса подстановок, поэтому ссылки разрешает сама Kora уже после слияния всех слоёв
конфигурации. Поддерживаются три формы:
${path}— обязательная: ссылка должна разрешиться, иначе приложение упадёт при старте${?path}— необязательная: неразрешённая ссылка не даёт значения, и ключ ведёт себя так, будто он отсутствует${path:defaultValue}— неразрешённая ссылка заменяется наdefaultValue
Те же формы работают и для переменных окружения, и для ссылок на другие ключи конфигурации, потому что переменные
окружения и системные свойства сами являются слоями конфигурации. В одну строку можно встроить несколько ссылок, как это
сделано для propReference выше.
Внимание
Символ ? и значение по умолчанию нельзя комбинировать: в ${?path:defaultValue} весь текст path:defaultValue
воспринимается как имя ссылки, и ключ не получит значения. Используйте ${path:defaultValue} — эта форма и так
подставляет запасное значение, когда ссылка не разрешилась.
Отображение конфигурации в коде:
@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();
@ConfigMapper
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>
@ConfigMapper
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 из classpath, затем поверх reference.yaml накладывается
application.yaml, после чего результат разрешается и проверяются обязательные подстановки.
Конфигурация приложения ожидается в application.yaml, а конфигурация библиотек — в reference.yaml.
Каждый reference.yaml должен разрешаться самостоятельно, без файла приложения: он проверяется при старте, и
неразрешимая ссылка роняет приложение с ошибкой Reference config ... cannot be resolved without external application config.
Задайте таким ключам литеральное значение по умолчанию, сделайте ссылку необязательной через ${?path} либо укажите
запасное значение через ${path:defaultValue}.
Приоритет выбора файла приложения для YAML:
- Используется файл из
config.resource, если он указан (файл из директорииresources) - Используется файл из
config.file, если он указан (файл из файловой системы) - Используется
application.yaml, если он присутствует (файл из директорииresources) - Используется пустая конфигурация, если ничего из вышеперечисленного нет
Одновременно можно указать только одно свойство: config.resource либо config.file. Если указаны оба свойства,
приложение упадёт при старте с ошибкой Application config source is ambiguous.
Пользовательская конфигурация¶
Пользовательская конфигурация отображает секцию файла конфигурации на пользовательский тип. Этот тип затем внедряется как зависимость наравне с любым другим компонентом.
И @ConfigSource, и @ConfigMapper генерируют реализацию ConfigValueMapper<T> во время компиляции.
Допустимые формы объявления:
Java—interface,recordлибо класс. Класс должен быть неабстрактным и переопределятьequalsиhashCodeKotlin—interfaceлибоdata class
Методы интерфейса конфигурации описывают поля, поэтому они не должны принимать параметры, не должны быть обобщёнными и
обязаны возвращать значение. Всё остальное оформляется методом default. Поля, объявленные в родительских интерфейсах,
наследуются в отображение.
Конфигурация приложения¶
Для создания пользовательских конфигураций в приложении используется аннотация @ConfigSource.
Она генерирует ConfigValueMapper для типа и модуль, который добавляет готовый объект конфигурации в граф
зависимостей. Значение аннотации указывает путь к секции внутри итоговой конфигурации:
Такой код добавит в контейнер зависимостей экземпляр класса FooServiceConfig, который при создании будет ожидать конфигурацию следующего вида:
После этого класс FooServiceConfig можно использовать как зависимость в других классах:
Конфигурация библиотеки¶
Для создания пользовательских конфигураций в библиотеках используется аннотация @ConfigMapper.
Она создаёт правило отображения ConfigValue<?> на тип, но не привязывает его к конкретному пути конфигурации.
Путь выбирается в фабричном методе модуля библиотеки, поэтому одну и ту же форму конфигурации можно переиспользовать для разных секций.
У аннотации есть параметр mapNullAsEmptyObject (по умолчанию: true). Когда он включён, отсутствующая секция
трактуется как пустой объект: обязательные поля всё так же падают, а необязательные поля и значения по умолчанию ведут
себя так, будто пустая секция присутствовала. При mapNullAsEmptyObject = false отсутствующая секция отображается в
null для всего объекта конфигурации.
Тип, помеченный только @ConfigSource, всегда ведёт себя как при mapNullAsEmptyObject = true.
Рассмотрим такой класс конфигурации:
Чтобы библиотека предоставляла конфигурацию, реализуйте фабрику в модуле:
Фабрика будет ожидать конфигурацию следующего вида:
После подключения FooLibraryModule в приложении FooLibraryConfig можно использовать как зависимость в других классах.
У ConfigValueMapper<T> два метода чтения: map(...) может вернуть null — у сгенерированного маппера это
происходит при mapNullAsEmptyObject = false и отсутствующей секции, — а mapOrThrow(...) превращает такой null в
ConfigValueException. В фабричных методах обычно используют mapOrThrow(...), потому что отсутствие секции
библиотеки — это ошибка старта, а не допустимое состояние.
Одну и ту же форму можно привязать сразу к нескольким секциям, добавив тег каждому фабричному методу:
public interface FooLibraryModule {
final class Lib1Tag {}
final class Lib2Tag {}
@Tag(Lib1Tag.class)
default FooLibraryConfig lib1Config(Config config, ConfigValueMapper<FooLibraryConfig> mapper) {
return mapper.mapOrThrow(config.get("libs.lib1"));
}
@Tag(Lib2Tag.class)
default FooLibraryConfig lib2Config(Config config, ConfigValueMapper<FooLibraryConfig> mapper) {
return mapper.mapOrThrow(config.get("libs.lib2"));
}
}
interface FooLibraryModule {
class Lib1Tag private constructor()
class Lib2Tag private constructor()
@Tag(Lib1Tag::class)
fun lib1Config(config: Config, mapper: ConfigValueMapper<FooLibraryConfig>): FooLibraryConfig {
return mapper.mapOrThrow(config.get("libs.lib1"))
}
@Tag(Lib2Tag::class)
fun lib2Config(config: Config, mapper: ConfigValueMapper<FooLibraryConfig>): FooLibraryConfig {
return mapper.mapOrThrow(config.get("libs.lib2"))
}
}
Обязательные значения¶
По умолчанию все значения, объявленные в конфигурации, считаются обязательными и должны присутствовать в итоговой
конфигурации. Если обязательное значение отсутствует либо равно null, приложение упадёт при создании объекта
конфигурации с ошибкой Config expected value, but got null at path: '...'.
Необязательные значения¶
Если требуется указать значение из файла конфигурации как необязательное, используется такой формат:
Рекомендуется использовать аннотацию @Nullable над сигнатурой метода:
@ConfigSource("services.foo")
public interface FooServiceConfig {
@Nullable//(1)!
String bar();
int baz();
}
- JSpecify
org.jspecify.annotations.Nullable— аннотация, на которой построена самаKora.
Используйте синтаксис null-safety в Kotlin и пометьте параметр как nullable:
@Nullable из JSpecify — аннотация над типом, поэтому для квалифицированного или обобщённого типа она пишется
непосредственно перед именем типа, а не перед всем объявлением:
Также поддерживается возвращаемый тип Optional<T> (отсутствующее значение отображается в Optional.empty()), но
рекомендуемый стиль — значение с @Nullable (или nullable-тип в Kotlin).
Значения по умолчанию¶
Если требуется задать значение по умолчанию при отображении конфигурации, используйте метод default:
Значения по умолчанию доступны и для остальных форм объявления, но механизм отличается по языкам: data class
в Kotlin берёт их из значений по умолчанию у параметров конструктора, а класс в Java — из инициализаторов
полей, поэтому ему нужны публичный конструктор без аргументов и методы доступа:
@ConfigMapper
public class FooServiceConfig {
private String bar;
private int baz = 42;
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
public int getBaz() {
return this.baz;
}
public void setBaz(int baz) {
this.baz = baz;
}
@Override
public boolean equals(Object o) {
return o instanceof FooServiceConfig that
&& Objects.equals(this.bar, that.bar)
&& this.baz == that.baz;
}
@Override
public int hashCode() {
return Objects.hash(this.bar, this.baz);
}
}
У record в Java механизма значений по умолчанию нет: каждый компонент читается как обязательное значение,
если он не помечен @Nullable. Когда для record нужны значения по умолчанию, используйте интерфейс с методами
default.
Валидация¶
Тип конфигурации можно дополнительно проверять ограничениями валидации. Пометьте его аннотацией @Valid
и расставьте ограничения на полях: сгенерированный маппер вызовет Validator сразу после сборки объекта, поэтому
некорректная конфигурация уронит приложение на старте, а не при первом использовании.
Гибкие имена ключей¶
Ключи конфигурации сопоставляются по гибким правилам именования. Имя метода сравнивается с ключом в файле не только в
точной форме, но и в вариантах kebab-case и snake_case. Это значит, что метод someBarString() одинаково
разрешается из someBarString, some-bar-string или some_bar_string в файле конфигурации, поэтому команды,
предпочитающие kebab-case или snake_case, могут сохранить свой стиль без переименования методов.
Все три написания ключа ниже читаются в someBarString():
Цифры и подряд идущие заглавные буквы также считаются отдельными частями, поэтому someFieldWithCAPSAnd42Numbers()
читается ещё и из some-field-with-caps-and-42-numbers и some_field_with_caps_and_42_numbers.
Рекомендуемый стиль¶
Обычно удобнее описывать конфигурацию отдельным типом для конкретной интеграции или подсистемы: HTTP-клиента, подключения к внешнему сервису, обработчика очереди и так далее. Такой тип должен явно разделять обязательные значения, необязательные значения и значения, приходящие из переменных окружения.
В примере ниже:
baseUrl— обязательное значение из файла конфигурацииclientName— необязательное значение из переменной окруженияORDERS_CLIENT_NAMEtoken— обязательное значение из переменной окруженияORDERS_API_TOKENrequestTimeoutимеет значение по умолчанию2sи может быть переопределён необязательной переменной окруженияORDERS_REQUEST_TIMEOUT
Так структура конфигурации остаётся читаемой: обязательные настройки видны в типе конфигурации, секреты передаются через переменные окружения, а безопасные значения по умолчанию остаются прямо в файле конфигурации.
Внедрение конфигурации¶
Можно внедрить базовый класс io.koraframework.config.common.Config, который представляет дерево конфигурации и даёт
доступ к значениям через метод get(...). Итоговая конфигурация состоит из нескольких слоёв:
- Переменные окружения
- Системные свойства
Java - Файл конфигурации
Слои сливаются так, что более ранний слой побеждает более поздний: переменная окружения переопределяет системное
свойство, а системное свойство переопределяет значение из файла конфигурации. Переменные окружения становятся плоскими
ключами с именем самой переменной (ORDERS_API_TOKEN), а системные свойства разбиваются по . в дерево, поэтому
-Dservices.foo.bar=value напрямую переопределяет ключ конфигурации services.foo.bar.
После слияния Kora разрешает ссылки ${...} по всему дереву. Значения, пришедшие из переменных окружения, повторно не
разрешаются, поэтому символ $ внутри секрета безопасен. Разрешение значений, пришедших из системных свойств, можно
отключить переменной окружения KORA_SYSTEM_PROPERTIES_RESOLVE_ENABLED либо системным свойством
kora.system.properties.resolve.enabled (по умолчанию: true).
Переменные окружения¶
Если требуется внедрить конфигурацию, состоящую только из переменных окружения,
используйте аннотацию @EnvironmentConfig как тег для класса конфигурации:
Системные свойства¶
Если требуется внедрить конфигурацию, состоящую только из системных свойств Java,
используйте аннотацию @SystemPropertiesConfig как тег для класса конфигурации:
Конфигурационный файл¶
Если требуется внедрить конфигурацию приложения, состоящую только из файла конфигурации,
используйте аннотацию @ApplicationConfig как тег для класса конфигурации:
Итоговая конфигурация¶
Если требуется внедрить полную итоговую конфигурацию приложения, состоящую из файла конфигурации, переменных окружения и системных свойств, просто внедрите класс конфигурации без тега:
Чтение сырого Config¶
Когда внедрён сырой Config, значения читаются методом get(...), который возвращает узел ConfigValue<?> по
запрошенному пути. ConfigValue<?> — sealed-тип с типизированными методами доступа: asString(), asNumber(),
asBoolean(), asObject(), asArray() и isNull(). Если значение имеет неожиданный тип, метод доступа бросает
ConfigValueException. Отсутствующий путь сам по себе исключения не бросает — он возвращает ConfigValue.NullValue.
Элементы массива адресуются индексом внутри пути, например config.get("services.foo.propObjects[0].p1").
Как отмечено в разделе Рекомендуемый стиль, предпочитайте типизированные
пользовательские конфигурации чтению сырого Config.
Используйте API сырого чтения только для динамического или обобщённого доступа, когда иного выбора нет, и применяйте
ValueOf<Config>, чтобы избежать обновления компонента.
Внимание
Не рекомендуется использовать io.koraframework.config.common.Config напрямую как зависимость в компонентах,
потому что при обновлении конфигурации будут обновлены и все компоненты графа, которые его используют.
Рекомендуется всегда создавать пользовательские конфигурации.
Наблюдатель за конфигурацией¶
По умолчанию в Kora есть наблюдатель за файлом конфигурации, который проверяет файл приложения на изменения и
запускает обновление графа зависимостей, если файл изменился. Проверка выполняется каждые 1000 миллисекунд на
виртуальном потоке.
Для HOCON наблюдатель отслеживает и файлы, подключённые через include внутри основного файла конфигурации.
Если такой подключённый файл изменился, конфигурация перечитывается и граф зависимостей также обновляется.
Наблюдатель работает только для файловой конфигурации с отслеживаемым источником. Если конфигурация пришла из ресурса
внутри архива либо была собрана без файла приложения, обновлять на диске нечего.
Подмена символической ссылки, на которую указывает файл конфигурации, тоже считается изменением — именно это делает
видимыми без перезапуска смонтированные секреты и обновления ConfigMap.
Отключить наблюдатель можно с помощью:
- Переменной окружения
KORA_CONFIG_WATCHER_ENABLED(по умолчанию:true) - Системного свойства
kora.config.watcher.enabled(по умолчанию:true)
Поддерживаемые типы¶
Мапперы конфигурации предоставляют обширный список поддерживаемых типов, который покрывает большинство значений,
нужных в пользовательских конфигурациях. Если стандартного преобразования недостаточно, поведение расширяется
собственным компонентом ConfigValueMapper<T>.
Список поддерживаемых типов
- boolean / Boolean
- int / Integer
- long / Long
- double / Double
- float / Float
- double[]
- String
- BigInteger
- BigDecimal
- Period
- Duration
- Duration[]
- Size
- Properties
- Pattern
- UUID
- LocalDate
- LocalTime
- LocalDateTime
- OffsetTime
- OffsetDateTime
- ConfigValue.ObjectValue
- Enum (любой пользовательский
enum; отображение можно переопределить черезtoString()) Optional<T>(гдеT— любой поддерживаемый тип)List<T>(гдеT— любой поддерживаемый тип)Set<T>(гдеT— любой поддерживаемый тип)Map<String, V>илиMap<K, V>(гдеKиVподдерживаются соответствующими мапперами)Either<A, B>(гдеAиB— любые поддерживаемые типы)
List<T> и Set<T> принимают и массив, и строку с разделителем-запятой, поэтому ["v1", "v2"] и "v1, v2" дают одно
и то же значение. Map<K, V> читается из объекта: ключи берутся как строки и проходят через маппер ключа, значения —
через маппер значения.
Пользовательский маппер¶
Если для типа нет стандартного преобразования или требуется особая логика разбора, добавьте собственный компонент
ConfigValueMapper<T>. Метод map(...) получает значение конфигурации как ConfigValue<?> и должен вернуть готовое
значение нужного типа либо null, если значение отсутствует.
Зарегистрированный так маппер используется для каждого поля конфигурации типа Token.
Если конкретный маппер должен применяться только к одному полю, укажите его через @Mapping:
Откуда берётся экземпляр маппера, зависит от его объявления. Если класс final (Java) / не open (Kotlin),
имеет публичный конструктор без аргументов и не помечен @Tag, Kora создаёт его сама для этого поля.
В любом другом случае — есть зависимости конструктора, класс open либо рядом с @Mapping стоит @Tag — маппер
берётся из графа зависимостей и должен быть там зарегистрирован. Маппер, используемый без @Mapping, то есть как маппер
типа для всего графа, всегда берётся из графа и обязан быть компонентом.
Duration¶
Duration можно задать числом или строкой.
Если указано число, оно трактуется как миллисекунды.
Если указана строка, поддерживается формат java.time.Duration, например PT10S, а также стиль HOCON:
500ms10 seconds2 minutes1h1d
Поддерживаемые суффиксы единиц: ns / nanos / nanoseconds, us / micros / microseconds, ms / millis / milliseconds,
s / seconds, m / minutes, h / hours, d / days. Значение без суффикса читается как миллисекунды.
Period¶
Period можно задать числом или строкой.
Если указано число, оно трактуется как дни.
Если указана строка, поддерживаются такие единицы:
d/daysw/weeksm/mo/monthsy/years
Например, 7d, 2 weeks, 3mo или 1 year. Значение без суффикса читается как дни.
Size¶
Size — специальный тип, который позволяет задавать размеры в байтах в удобной для человека нотации: по стандарту
IEEE 1541-2002 (двоичной) либо по стандарту
SI (десятичной).
Примеры значений:
1Mb- 1 мегабайт (1.000.000байт)1Mib- 1 мебибайт (1.048.576байт)1024b- 1024 байта1024- 1024 байта
Если указано просто число без суффикса, считается, что указаны байты.
Суффиксы сопоставляются без учёта регистра и покрывают b, kb / kib, mb / mib, gb / gib, tb / tib,
pb / pib, eb / eib.
Either¶
Either<A, B> позволяет одному полю принимать две альтернативные формы. Маппер сначала пробует левый тип A, и если
отображение падает с любым исключением, откатывается к правому типу B. Это удобно, когда значение может быть либо
простым скаляром, либо структурированным объектом.
Обе формы ниже допустимы для поля endpoint:
Используйте isLeft() / isRight(), чтобы проверить, какая сторона была разрешена, и left() / right(), чтобы прочитать значение.