Статьи

Внедрение значений конфигурации с использованием CDI InjectionPoint

Внедрение зависимостей — отличная технология для организации классовых зависимостей. Все экземпляры классов, которые вам нужны в текущем классе, предоставляются во время выполнения из контейнера DI. Но как насчет вашей конфигурации?

Конечно, вы можете создать класс «Конфигурация» и внедрять этот класс везде, где вам это нужно, и получать от него необходимые значения. Но CDI позволяет вам делать это еще более детально, используя концепцию InjectionPoint.

Если вы пишете метод @Produces, вы можете позволить вашему контейнеру CDI также вводить некоторую информацию о текущем коде, в который вводится вновь созданное / произведенное значение. Полный список доступных методов можно найти здесь . Интересно то, что вы можете запросить этот класс для всех аннотаций, которые имеет текущая точка внедрения:

1
2
Annotated annotated = injectionPoint.getAnnotated();
ConfigurationValue annotation = annotated.getAnnotation(ConfigurationValue.class);

Как показывает приведенный выше пример кода, мы можем ввести простую аннотацию @Qualifier, которая отмечает все точки внедрения, где нам нужно конкретное значение конфигурации. В этом посте мы просто хотим использовать строки в качестве значений конфигурации, но, конечно, всю концепцию можно распространить и на другие типы данных. Уже упомянутая аннотация @Qualifier выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface ConfigurationValue {
    @Nonbinding ConfigurationKey key();
}
 
public enum ConfigurationKey {
    DefaultDirectory, Version, BuildTimestamp, Producer
}

У аннотации, конечно, есть политика хранения RUNTIME, потому что контейнер CDI должен оценивать ее во время работы приложения. Может использоваться для полей и методов. Помимо этого мы также создаем ключевой атрибут, который поддерживается перечислением ConfigurationKey. Здесь мы можем ввести все необходимые значения конфигурации. В нашем примере это, например, значение конфигурации для каталога по умолчанию, для версии программы и так далее. Мы помечаем этот атрибут как @Nonbinding, чтобы значение этого атрибута не использовалось контейнером CDI для выбора правильного метода производителя. Если бы мы не использовали @Nonbinding, нам нужно было бы написать метод @Produces для каждого значения перечисления. Но здесь мы хотим обработать все это в рамках одного метода.

Метод @Produces для строк, аннотированных @ConfigurationKey, показан в следующем примере кода:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
@Produces
@ConfigurationValue(key=ConfigurationKey.Producer)
public String produceConfigurationValue(InjectionPoint injectionPoint) {
    Annotated annotated = injectionPoint.getAnnotated();
    ConfigurationValue annotation = annotated.getAnnotation(ConfigurationValue.class);
    if (annotation != null) {
        ConfigurationKey key = annotation.key();
        if (key != null) {
            switch (key) {
                case DefaultDirectory:
                    return System.getProperty("user.dir");
                case Version:
                    return JB5n.createInstance(Configuration.class).version();
                case BuildTimestamp:
                    return JB5n.createInstance(Configuration.class).timestamp();
            }
        }
    }
    throw new IllegalStateException("No key for injection point: " + injectionPoint);
}

Метод @Produces получает InjectionPoint, внедренный в качестве параметра, чтобы мы могли проверить его значения. Поскольку нас интересуют аннотации точки впрыска, мы посмотрим, помечена ли текущая точка впрыска @ConfigurationValue. Если это так, мы посмотрим на ключевой атрибут @ ConfigurationValue и решим, какое значение мы возвращаем. Вот и все. Конечно, в более сложном приложении мы можем загрузить конфигурацию из некоторых файлов или другого хранилища данных. Но концепция остается прежней.

Теперь мы можем легко позволить контейнеру CDI вводить нужные нам значения конфигурации просто с помощью этих двух строк кода:

1
2
@Inject @ConfigurationValue(key = ConfigurationKey.DefaultDirectory)
    private String defaultDirectory;

Вывод : сделать набор значений конфигурации доступным во всем приложении никогда не было проще.