Micronaut 不可变配置

2023-03-01 16:32 更新

从 1.3 开始,Micronaut 支持不可变配置的定义。

有两种方法可以定义不可变配置。首选方法是定义一个用@ConfigurationProperties 注释的接口。例如:

@ConfigurationProperties 示例

 Java Groovy  Kotlin 
import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.core.bind.annotation.Bindable;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Optional;

@ConfigurationProperties("my.engine") // (1)
public interface EngineConfig {

    @Bindable(defaultValue = "Ford") // (2)
    @NotBlank // (3)
    String getManufacturer();

    @Min(1L)
    int getCylinders();

    @NotNull
    CrankShaft getCrankShaft(); // (4)

    @ConfigurationProperties("crank-shaft")
    interface CrankShaft { // (5)
        Optional<Double> getRodLength(); // (6)
    }
}
import io.micronaut.context.annotation.ConfigurationProperties
import io.micronaut.core.bind.annotation.Bindable

import javax.validation.constraints.Min
import javax.validation.constraints.NotBlank
import javax.validation.constraints.NotNull

@ConfigurationProperties("my.engine") // (1)
interface EngineConfig {

    @Bindable(defaultValue = "Ford") // (2)
    @NotBlank // (3)
    String getManufacturer()

    @Min(1L)
    int getCylinders()

    @NotNull
    CrankShaft getCrankShaft() // (4)

    @ConfigurationProperties("crank-shaft")
    static interface CrankShaft { // (5)
        Optional<Double> getRodLength() // (6)
    }
}
import io.micronaut.context.annotation.ConfigurationProperties
import io.micronaut.core.bind.annotation.Bindable
import javax.validation.constraints.Min
import javax.validation.constraints.NotBlank
import javax.validation.constraints.NotNull

@ConfigurationProperties("my.engine") // (1)
interface EngineConfig {

    @get:Bindable(defaultValue = "Ford") // (2)
    @get:NotBlank // (3)
    val manufacturer: String

    @get:Min(1L)
    val cylinders: Int

    @get:NotNull
    val crankShaft: CrankShaft // (4)

    @ConfigurationProperties("crank-shaft")
    interface CrankShaft { // (5)
        val rodLength: Double? // (6)
    }
}
  1. @ConfigurationProperties 注释采用配置前缀并在接口上声明

  2. 您可以使用@Bindable 设置默认值

  3. 也可以使用验证注解

  4. 您还可以指定对其他 @ConfigurationProperties beans 的引用。

  5. 您可以嵌套不可变配置

  6. 可以通过返回 Optional 或指定 @Nullable 来指示可选配置

在这种情况下,Micronaut 提供了一个编译时实现,它委托所有 getter 调用 Environment 接口的 getProperty(..) 方法。

这样做的好处是,如果应用程序配置被刷新(例如通过调用 /refresh 端点),注入的接口会自动看到新值。

如果您尝试指定除 getter 之外的任何其他抽象方法,则会发生编译错误(支持默认方法)。

另一种实现不可变配置的方法是定义一个类并在 @ConfigurationProperties 或 @EachProperty bean 的构造函数上使用 @ConfigurationInject 注释。

例如:

@ConfigurationProperties 示例

 Java Groovy  Kotlin 
import io.micronaut.core.bind.annotation.Bindable;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.context.annotation.ConfigurationInject;
import io.micronaut.context.annotation.ConfigurationProperties;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Optional;

@ConfigurationProperties("my.engine") // (1)
public class EngineConfig {

    private final String manufacturer;
    private final int cylinders;
    private final CrankShaft crankShaft;

    @ConfigurationInject // (2)
    public EngineConfig(
            @Bindable(defaultValue = "Ford") @NotBlank String manufacturer, // (3)
            @Min(1L) int cylinders, // (4)
            @NotNull CrankShaft crankShaft) {
        this.manufacturer = manufacturer;
        this.cylinders = cylinders;
        this.crankShaft = crankShaft;
    }

    public String getManufacturer() {
        return manufacturer;
    }

    public int getCylinders() {
        return cylinders;
    }

    public CrankShaft getCrankShaft() {
        return crankShaft;
    }

    @ConfigurationProperties("crank-shaft")
    public static class CrankShaft { // (5)
        private final Double rodLength; // (6)

        @ConfigurationInject
        public CrankShaft(@Nullable Double rodLength) {
            this.rodLength = rodLength;
        }

        public Optional<Double> getRodLength() {
            return Optional.ofNullable(rodLength);
        }
    }
}
import io.micronaut.context.annotation.ConfigurationInject
import io.micronaut.context.annotation.ConfigurationProperties
import io.micronaut.core.bind.annotation.Bindable

import javax.annotation.Nullable
import javax.validation.constraints.Min
import javax.validation.constraints.NotBlank
import javax.validation.constraints.NotNull

@ConfigurationProperties("my.engine") // (1)
class EngineConfig {

    final String manufacturer
    final int cylinders
    final CrankShaft crankShaft

    @ConfigurationInject // (2)
    EngineConfig(
            @Bindable(defaultValue = "Ford") @NotBlank String manufacturer, // (3)
            @Min(1L) int cylinders, // (4)
            @NotNull CrankShaft crankShaft) {
        this.manufacturer = manufacturer
        this.cylinders = cylinders
        this.crankShaft = crankShaft
    }

    @ConfigurationProperties("crank-shaft")
    static class CrankShaft { // (5)
        private final Double rodLength // (6)

        @ConfigurationInject
        CrankShaft(@Nullable Double rodLength) {
            this.rodLength = rodLength
        }

        Optional<Double> getRodLength() {
            Optional.ofNullable(rodLength)
        }
    }
}
import io.micronaut.context.annotation.ConfigurationInject
import io.micronaut.context.annotation.ConfigurationProperties
import io.micronaut.core.bind.annotation.Bindable
import java.util.Optional
import javax.validation.constraints.Min
import javax.validation.constraints.NotBlank
import javax.validation.constraints.NotNull

@ConfigurationProperties("my.engine") // (1)
data class EngineConfig @ConfigurationInject // (2)
    constructor(
        @Bindable(defaultValue = "Ford") @NotBlank val manufacturer: String, // (3)
        @Min(1) val cylinders: Int, // (4)
        @NotNull val crankShaft: CrankShaft) {

    @ConfigurationProperties("crank-shaft")
    data class CrankShaft @ConfigurationInject
    constructor(// (5)
            private val rodLength: Double? // (6)
    ) {

        fun getRodLength(): Optional<Double> {
            return Optional.ofNullable(rodLength)
        }
    }
}
  1. @ConfigurationProperties 注解采用配置前缀

  2. @ConfigurationInject 注释在构造函数上定义

  3. 您可以使用@Bindable 设置默认值

  4. 也可以使用验证注解

  5. 您可以嵌套不可变配置

  6. 可选配置可以用 @Nullable 表示

@ConfigurationInject 注释向 Micronaut 提供了一个提示,以优先考虑来自配置的绑定值而不是注入 bean。

使用这种方法,要使配置可刷新,还要将 @Refreshable 注释添加到类中。这允许在运行时配置刷新事件的情况下重新创建 bean。

这条规则有一些例外。如果满足以下任何条件,Micronaut 将不会对参数执行配置绑定:

  • 参数用@Value注解(显式绑定)

  • 参数用@Property注解(显式绑定)

  • 参数用@Parameter注解(参数化bean处理)

  • 参数用@Inject注解(generic bean injection)

  • 参数的类型用bean作用域注解(比如@Singleton)

一旦你准备好了一个类型安全的配置,它就可以像任何其他 bean 一样注入到你的 bean 中:

@ConfigurationProperties 依赖注入

 Java Groovy  Kotlin 
@Singleton
public class Engine {
    private final EngineConfig config;

    public Engine(EngineConfig config) {// (1)
        this.config = config;
    }

    public int getCylinders() {
        return config.getCylinders();
    }

    public String start() {// (2)
        return getConfig().getManufacturer() + " Engine Starting V" + getConfig().getCylinders() +
                " [rodLength=" + getConfig().getCrankShaft().getRodLength().orElse(6.0d) + "]";
    }

    public final EngineConfig getConfig() {
        return config;
    }
}
@Singleton
class Engine {
    private final EngineConfig config

    Engine(EngineConfig config) {// (1)
        this.config = config
    }

    int getCylinders() {
        return config.cylinders
    }

    String start() {// (2)
        return "$config.manufacturer Engine Starting V$config.cylinders [rodLength=${config.crankShaft.rodLength.orElse(6.0d)}]"
    }

    final EngineConfig getConfig() {
        return config
    }
}
@Singleton
class Engine(val config: EngineConfig)// (1)
{
    val cylinders: Int
        get() = config.cylinders

    fun start(): String {// (2)
        return  "${config.manufacturer} Engine Starting V${config.cylinders} [rodLength=${config.crankShaft.getRodLength().orElse(6.0)}]"
    }
}
  1. 注入 EngineConfig bean

  2. 使用配置属性

然后可以在运行应用程序时提供配置值。例如:

供应配置

 Java Groovy  Kotlin 
ApplicationContext applicationContext = ApplicationContext.run(CollectionUtils.mapOf(
        "my.engine.cylinders", "8",
        "my.engine.crank-shaft.rod-length", "7.0"
));

Vehicle vehicle = applicationContext.getBean(Vehicle.class);
System.out.println(vehicle.start());
ApplicationContext applicationContext = ApplicationContext.run(
        "my.engine.cylinders": "8",
        "my.engine.crank-shaft.rod-length": "7.0"
)

Vehicle vehicle = applicationContext.getBean(Vehicle)
System.out.println(vehicle.start())
val map = mapOf(
        "my.engine.cylinders" to "8",
        "my.engine.crank-shaft.rod-length" to "7.0"
)
val applicationContext = ApplicationContext.run(map)

val vehicle = applicationContext.getBean(Vehicle::class.java)
println(vehicle.start())

上面的示例打印:“Ford Engine Starting V8 [rodLength=7B.0]”

自定义访问器

正如更改访问器样式中已经解释的那样,也可以在创建不可变配置属性时自定义访问器:

import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.core.annotation.AccessorsStyle;
import io.micronaut.core.bind.annotation.Bindable;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Optional;

@ConfigurationProperties("my.engine") (1)
@AccessorsStyle(readPrefixes = "read") (2)
public interface EngineConfigAccessors {

    @Bindable(defaultValue = "Ford")
    @NotBlank
    String readManufacturer(); (3)

    @Min(1L)
    int readCylinders(); (3)

    @NotNull
    CrankShaft readCrankShaft(); (3)

    @ConfigurationProperties("crank-shaft")
    @AccessorsStyle(readPrefixes = "read") (4)
    interface CrankShaft {
        Optional<Double> readRodLength(); (5)
    }
}
  1. @ConfigurationProperties 注释采用配置前缀并在接口上声明

  2. @AccessorsStyle 注解将 readPrefixes 定义为已读。

  3. getter 都以 read 为前缀。

  4. 嵌套的不可变配置也可以使用 @ConfigurationProperties 进行注解。

  5. getter 以 read 为前缀。


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号