编辑此页面

扩展配置支持

1. 自定义 ConfigSource

可以根据 MicroProfile Config 的规定创建自定义 ConfigSource

使用自定义 ConfigSource,可以读取其他配置值,并以定义的顺序将它们添加到 Config 实例中。这允许覆盖其他源的值或回退到其他值。

config sources

自定义 ConfigSource 需要实现 org.eclipse.microprofile.config.spi.ConfigSourceorg.eclipse.microprofile.config.spi.ConfigSourceProvider。每个实现都需要通过 ServiceLoader 机制进行注册,无论是通过 META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource 还是 META-INF/services/org.eclipse.microprofile.config.spi.ConfigSourceProvider 文件。

1.1. 示例

考虑一个简单的内存中 ConfigSource

org.acme.config.InMemoryConfigSource
package org.acme.config;

import org.eclipse.microprofile.config.spi.ConfigSource;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class InMemoryConfigSource implements ConfigSource {
    private static final Map<String, String> configuration = new HashMap<>();

    static {
        configuration.put("my.prop", "1234");
    }

    @Override
    public int getOrdinal() {
        return 275;
    }

    @Override
    public Set<String> getPropertyNames() {
        return configuration.keySet();
    }

    @Override
    public String getValue(final String propertyName) {
        return configuration.get(propertyName);
    }

    @Override
    public String getName() {
        return InMemoryConfigSource.class.getSimpleName();
    }
}

以及在以下位置的注册

META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource
org.acme.config.InMemoryConfigSource

由于 275 的顺序,InMemoryConfigSource 将排在 .env 源和 application.properties 源之间

ConfigSource 顺序

系统属性

400

系统环境变量

300

来自 .env 文件的环境变量

295

内存ConfigSource

275

来自 /configapplication.properties

260

来自应用程序的 application.properties

250

来自应用程序的 microprofile-config.properties

100

在这种情况下,只有当配置引擎无法按此顺序在 系统属性系统环境变量来自 .env 文件的环境变量 中找到值时,才会使用 InMemoryConfigSource 中的 my.prop

1.2. ConfigSource 初始化

Quarkus 应用程序启动时,ConfigSource 可以初始化两次。一次用于静态初始化,第二次用于运行时初始化

1.2.1. 静态初始化

Quarkus 在静态初始化期间启动一些服务,而 Config 通常是创建的第一批事物之一。在某些情况下,可能无法添加自定义 ConfigSource。例如,如果 ConfigSource 需要其他服务,例如数据库访问,它在这个阶段将不可用,并导致鸡生蛋蛋生鸡的问题。因此,任何自定义 ConfigSource 都需要 @io.quarkus.runtime.configuration.StaticInitSafe 注释来标记该源在此阶段是安全的。

1.2.1.1. 示例

考虑

org.acme.config.InMemoryConfigSource
package org.acme.config;

import org.eclipse.microprofile.config.spi.ConfigSource;
import io.quarkus.runtime.annotations.StaticInitSafe;

@StaticInitSafe
public class InMemoryConfigSource implements ConfigSource {

}

以及在以下位置的注册

META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource
org.acme.config.InMemoryConfigSource

InMemoryConfigSource 将在静态初始化期间可用。

自定义 ConfigSource 不会在 Quarkus静态初始化期间自动添加。它需要用 @io.quarkus.runtime.configuration.StaticInitSafe 注释进行标记。

1.2.2. 运行时初始化

运行时初始化阶段发生在静态初始化之后。在此阶段,ConfigSource 可以再次初始化。此阶段没有限制,自定义源会按预期添加到 Config 实例。

2. ConfigSourceFactory

创建 ConfigSource 的另一种方法是通过 SmallRye Configio.smallrye.config.ConfigSourceFactory API。 SmallRye Config 工厂与根据 MicroProfile Config 规范创建 ConfigSource 的标准方法之间的区别在于,工厂能够提供一个具有对可用配置的访问权限的上下文。

io.smallrye.config.ConfigSourceFactory 的每个实现都需要通过 ServiceLoader 机制在 META-INF/services/io.smallrye.config.ConfigSourceFactory 文件中进行注册。

2.1. 示例

考虑

org.acme.config.URLConfigSourceFactory
package org.acme.config;

import java.util.Collections;
import java.util.OptionalInt;

import org.eclipse.microprofile.config.spi.ConfigSource;

import io.smallrye.config.ConfigSourceContext;
import io.smallrye.config.ConfigSourceFactory;
import io.smallrye.config.ConfigValue;
import io.smallrye.config.PropertiesConfigSource;

public class URLConfigSourceFactory implements ConfigSourceFactory {
    @Override
    public Iterable<ConfigSource> getConfigSources(final ConfigSourceContext context) {
        final ConfigValue value = context.getValue("config.url");
        if (value == null || value.getValue() == null) {
            return Collections.emptyList();
        }

        try {
            return Collections.singletonList(new PropertiesConfigSource(new URL(value.getValue())));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public OptionalInt getPriority() {
        return OptionalInt.of(290);
    }
}

以及在以下位置的注册

META-INF/services/io.smallrye.config.ConfigSourceFactory
org.acme.config.URLConfigSourceFactory

通过实现 io.smallrye.config.ConfigSourceFactory,可以通过 Iterable<ConfigSource> getConfigSources(ConfigSourceContext context) 方法提供一个 ConfigSource 列表。ConfigSourceFactory 还可以通过覆盖 OptionalInt getPriority() 方法来分配优先级。优先级值用于对多个 io.smallrye.config.ConfigSourceFactory (如果找到) 进行排序。

io.smallrye.config.ConfigSourceFactory 的优先级不会影响 ConfigSource 的顺序。它们是独立排序的。

当工厂初始化时,提供的 ConfigSourceContext 可以调用 ConfigValue getValue(String name) 方法。此方法会在 Config 实例已初始化的所有 ConfigSource 中查找配置名称,包括顺序低于 ConfigSourceFactory 中定义的顺序的源。由较低优先级的 ConfigSourceFactory 生成的其他源的配置不考虑 ConfigSourceFactory 提供的 ConfigSource 列表。

3. 自定义 Converter

可以根据 MicroProfile Config 的规定创建自定义 Converter 类型。

自定义 Converter 需要实现 org.eclipse.microprofile.config.spi.Converter<T>。每个实现都需要通过 ServiceLoader 机制在 META-INF/services/org.eclipse.microprofile.config.spi.Converter 文件中进行注册。考虑

package org.acme.config;

public class MicroProfileCustomValue {

    private final int number;

    public MicroProfileCustomValue(int number) {
        this.number = number;
    }

    public int getNumber() {
        return number;
    }
}

相应的转换器将类似于下面的内容。

package org.acme.config;

import org.eclipse.microprofile.config.spi.Converter;

public class MicroProfileCustomValueConverter implements Converter<MicroProfileCustomValue> {

    @Override
    public MicroProfileCustomValue convert(String value) {
        return new MicroProfileCustomValue(Integer.parseInt(value));
    }
}
自定义转换器类必须是 public 的,必须有一个没有参数的 public 构造函数,并且不能是 abstract 的。

自定义配置类型会自动转换配置值

@ConfigProperty(name = "configuration.value.name")
MicroProfileCustomValue value;

3.1. 转换器优先级

jakarta.annotation.Priority 注释会覆盖 Converter 的优先级,并改变转换器的优先顺序以微调执行顺序。默认情况下,如果 Converter 没有指定 @Priority,则该转换器以 100 的优先级注册。考虑

package org.acme.config;

import jakarta.annotation.Priority;
import org.eclipse.microprofile.config.spi.Converter;

@Priority(150)
public class MyCustomConverter implements Converter<MicroProfileCustomValue> {

    @Override
    public MicroProfileCustomValue convert(String value) {

        final int secretNumber;
        if (value.startsFrom("OBF:")) {
            secretNumber = Integer.parseInt(SecretDecoder.decode(value));
        } else {
            secretNumber = Integer.parseInt(value);
        }

        return new MicroProfileCustomValue(secretNumber);
    }
}

由于它转换相同的值类型 (MicroProfileCustomValue) 并且优先级为 150,因此将使用它而不是优先级默认为 100MicroProfileCustomValueConverter

所有 Quarkus 核心转换器都使用 200 的优先级值。要覆盖任何 Quarkus 特定的转换器,优先级值应高于 200

4. 配置拦截器

SmallRye Config 提供了一个拦截器链,该链钩入配置值的解析。这对于实现配置文件属性表达式等功能非常有用,或者仅仅是为了日志记录以找出配置值是从哪里加载的。

拦截器需要实现 io.smallrye.config.ConfigSourceInterceptor。每个实现都需要通过 ServiceLoader 机制在 META-INF/services/io.smallrye.config.ConfigSourceInterceptor 文件中进行注册。

io.smallrye.config.ConfigSourceInterceptor 能够通过 ConfigValue getValue(ConfigSourceInterceptorContext context, String name) 方法拦截配置名称的解析。ConfigSourceInterceptorContext 用于继续执行拦截器链。可以通过返回 io.smallrye.config.ConfigValue 实例来截断链。ConfigValue 对象保存关于键名、值、配置源来源和顺序的信息。

拦截器链在对配置值执行任何转换之前应用。

拦截器也可以通过实现 io.smallrye.config.ConfigSourceInterceptorFactory 来创建。每个实现都需要通过 ServiceLoader 机制在 META-INF/services/io.smallrye.config.ConfigSourceInterceptorFactory 文件中进行注册。

ConfigSourceInterceptorFactory 可以在访问当前链的情况下初始化拦截器(这样就可以用于配置拦截器并检索配置值),并设置优先级。

4.1. 示例

org.acme.config.LoggingConfigSourceInterceptor
package org.acme.config;

import static io.smallrye.config.SecretKeys.doLocked;

import jakarta.annotation.Priority;

import io.smallrye.config.ConfigSourceInterceptor;
import io.smallrye.config.ConfigLogging;

@Priority(Priorities.LIBRARY + 200)
public class LoggingConfigSourceInterceptor implements ConfigSourceInterceptor {
    private static final long serialVersionUID = 367246512037404779L;

    @Override
    public ConfigValue getValue(final ConfigSourceInterceptorContext context, final String name) {
        ConfigValue configValue = doLocked(() -> context.proceed(name));
        if (configValue != null) {
            ConfigLogging.log.lookup(configValue.getName(), configValue.getLocation(), configValue.getValue());
        } else {
            ConfigLogging.log.notFound(name);
        }
        return configValue;
    }
}

以及在以下位置的注册

META-INF/services/io.smallrye.config.ConfigSourceInterceptor
org.acme.config.LoggingConfigSourceInterceptor

LoggingConfigSourceInterceptor 在提供的日志平台中记录查找配置名称。日志信息包括配置名称和值,以及配置源的来源和位置(如果存在)。

相关内容