Micronaut 配置屬性

2023-03-01 16:03 更新

您可以通過創(chuàng)建使用 @ConfigurationProperties 注釋的類來創(chuàng)建類型安全的配置。

Micronaut 會產(chǎn)生一個無反射的@ConfigurationProperties bean,并且還會在編譯時計算屬性路徑進(jìn)行評估,大大提高加載@ConfigurationProperties 的速度和效率。

例如:

@ConfigurationProperties Example

 Java Groovy  Kotlin 
import io.micronaut.context.annotation.ConfigurationProperties;

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

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

    public String getManufacturer() {
        return manufacturer;
    }

    public void setManufacturer(String manufacturer) {
        this.manufacturer = manufacturer;
    }

    public int getCylinders() {
        return cylinders;
    }

    public void setCylinders(int cylinders) {
        this.cylinders = cylinders;
    }

    public CrankShaft getCrankShaft() {
        return crankShaft;
    }

    public void setCrankShaft(CrankShaft crankShaft) {
        this.crankShaft = crankShaft;
    }

    @NotBlank // (2)
    private String manufacturer = "Ford"; // (3)

    @Min(1L)
    private int cylinders;

    private CrankShaft crankShaft = new CrankShaft();

    @ConfigurationProperties("crank-shaft")
    public static class CrankShaft { // (4)

        private Optional<Double> rodLength = Optional.empty(); // (5)

        public Optional<Double> getRodLength() {
            return rodLength;
        }

        public void setRodLength(Optional<Double> rodLength) {
            this.rodLength = rodLength;
        }
    }
}
import io.micronaut.context.annotation.ConfigurationProperties

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

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

    @NotBlank // (2)
    String manufacturer = "Ford" // (3)

    @Min(1L)
    int cylinders

    CrankShaft crankShaft = new CrankShaft()

    @ConfigurationProperties('crank-shaft')
    static class CrankShaft { // (4)
        Optional<Double> rodLength = Optional.empty() // (5)
    }
}
import io.micronaut.context.annotation.ConfigurationProperties
import java.util.Optional
import javax.validation.constraints.Min
import javax.validation.constraints.NotBlank

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

    @NotBlank // (2)
    var manufacturer = "Ford" // (3)

    @Min(1L)
    var cylinders: Int = 0

    var crankShaft = CrankShaft()

    @ConfigurationProperties("crank-shaft")
    class CrankShaft { // (4)
        var rodLength: Optional<Double> = Optional.empty() // (5)
    }
}
  1. @ConfigurationProperties 注解采用配置前綴

  2. 您可以使用 javax.validation 注釋來驗證配置

  3. 可以為屬性分配默認(rèn)值

  4. 靜態(tài)內(nèi)部類可以提供嵌套配置

  5. 可選的配置值可以包裝在 java.util.Optional 中

一旦你準(zhǔn)備好了一個類型安全的配置,它就可以像任何其他 bean 一樣注入到你的 bean 中:

@ConfigurationProperties 依賴注入

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

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

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

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

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

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

    @Override
    int getCylinders() {
        config.cylinders
    }

    @Override
    String start() { // (2)
        "$config.manufacturer Engine Starting V$config.cylinders [rodLength=${config.crankShaft.rodLength.orElse(6.0d)}]"
    }
}
@Singleton
class EngineImpl(val config: EngineConfig) : Engine {// (1)

    override val cylinders: Int
        get() = config.cylinders

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

  2. 使用配置屬性

然后可以從 PropertySource 實例之一提供配置值。例如:

供應(yīng)配置

 Java Groovy  Kotlin 
Map<String, Object> map = new LinkedHashMap<>(1);
map.put("my.engine.cylinders", "8");
ApplicationContext applicationContext = ApplicationContext.run(map, "test");

Vehicle vehicle = applicationContext.getBean(Vehicle.class);
System.out.println(vehicle.start());
ApplicationContext applicationContext = ApplicationContext.run(
        ['my.engine.cylinders': '8'],
        "test"
)

def vehicle = applicationContext.getBean(Vehicle)
println(vehicle.start())
val map = mapOf( "my.engine.cylinders" to "8")
val applicationContext = ApplicationContext.run(map, "test")

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

上面的示例打?。骸癋ord Engine Starting V8 [rodLength=6.0]”

您可以使用以下語法直接引用@Requires 注釋中的配置屬性以有條件地加載bean:@Requires(bean=Config.class, beanProperty="property", value="true")

請注意,對于更復(fù)雜的配置,您可以通過繼承構(gòu)造 @ConfigurationProperties bean。

例如,使用 @ConfigurationProperties('bar') 創(chuàng)建 EngineConfig 的子類將解析路徑 my.engine.bar 下的所有屬性。

包括/排除

對于配置屬性類從父類繼承屬性的情況,可能需要從父類中排除屬性。 @ConfigurationProperties 注釋的包含和排除成員允許該功能。該列表適用于本地屬性和繼承屬性。

提供給包含/排除列表的名稱必須是“屬性”名稱。例如,如果注入了 setter 方法,則屬性名稱是去大寫的 setter 名稱(setConnectionTimeout → connectionTimeout)。

更改訪問器樣式

從 3.3 開始,Micronaut 支持為 getter 和 setter 定義不同的訪問器前綴,而不是為 Java Beans 定義的默認(rèn) get 和 set。使用@AccessorsStyle 注釋注釋您的POJO 或@ConfigurationProperties 類。

當(dāng)您以流暢的方式編寫 getter 和 setter 時,這很有用。例如:

使用@AccessorsStyle

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

@AccessorsStyle(readPrefixes = "", writePrefixes = "") (1)
@ConfigurationProperties("my.engine")
public class EngineConfig {

    private String manufacturer;
    private int cylinders;

    public EngineConfig(String manufacturer, int cylinders) {
        this.manufacturer = manufacturer;
        this.cylinders = cylinders;
    }

    public String manufacturer() { (2)
        return manufacturer;
    }

    public void manufacturer(String manufacturer) { (2)
        this.manufacturer = manufacturer;
    }

    public int cylinders() { (2)
        return cylinders;
    }

    public void cylinders(int cylinders) { (2)
        this.cylinders = cylinders;
    }

}
  1. Micronaut 將為 getter 和 setter 使用空前綴。

  2. 使用空前綴定義 getter 和 setter。

現(xiàn)在您可以注入 EngineConfig 并將其與 engineConfig.manufacturer() 和 engineConfig.cylinders() 一起使用以從配置中檢索值。

屬性類型轉(zhuǎn)換

Micronaut 在解析屬性時使用 ConversionService bean 來轉(zhuǎn)換值。您可以通過定義實現(xiàn) TypeConverter 接口的 bean 來為 Micronaut 不支持的類型注冊額外的轉(zhuǎn)換器。

Micronaut 具有一些有用的內(nèi)置轉(zhuǎn)換,下面將詳細(xì)介紹。

持續(xù)時間轉(zhuǎn)換

可以通過在單位后面附加一個數(shù)字來指定持續(xù)時間。支持的單位有 s、ms、m 等。下表總結(jié)了示例:

表 1. 持續(xù)時間轉(zhuǎn)換
配置值 結(jié)果值

10ms

持續(xù)時間為 10 毫秒

10m

持續(xù)時間為 10 分鐘

10s

持續(xù)時間為 10 秒

10d

持續(xù)時間為 10 天

10h

持續(xù)時間為 10 小時

10ns

持續(xù)時間為 10 納秒

PT15M

使用ISO-8601格式,持續(xù)時間為15分鐘

例如配置默認(rèn)的 HTTP 客戶端讀取超時:

使用持續(xù)時間值

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.http.client.read-timeout=15s
micronaut:
  http:
    client:
      read-timeout: 15s
[micronaut]
  [micronaut.http]
    [micronaut.http.client]
      read-timeout="15s"
micronaut {
  http {
    client {
      readTimeout = "15s"
    }
  }
}
{
  micronaut {
    http {
      client {
        read-timeout = "15s"
      }
    }
  }
}
{
  "micronaut": {
    "http": {
      "client": {
        "read-timeout": "15s"
      }
    }
  }
}

列表/數(shù)組轉(zhuǎn)換

列表和數(shù)組可以在 Java 屬性文件中指定為逗號分隔值,或者在 YAML 中使用本機(jī) YAML 列表。通用類型用于轉(zhuǎn)換值。例如在 YAML 中:

在 YAML 中指定列表或數(shù)組

 Properties Yaml  Toml  Groovy  Hocon  JSON 
my.app.integers[0]=1
my.app.integers[1]=2
my.app.urls[0]=http://foo.com
my.app.urls[1]=http://bar.com
my:
  app:
    integers:
      - 1
      - 2
    urls:
      - http://foo.com
      - http://bar.com
[my]
  [my.app]
    integers=[
      1,
      2
    ]
    urls=[
      "http://foo.com",
      "http://bar.com"
    ]
my {
  app {
    integers = [1, 2]
    urls = ["http://foo.com", "http://bar.com"]
  }
}
{
  my {
    app {
      integers = [1, 2]
      urls = ["http://foo.com", "http://bar.com"]
    }
  }
}
{
  "my": {
    "app": {
      "integers": [1, 2],
      "urls": ["http://foo.com", "http://bar.com"]
    }
  }
}

對于上面的示例配置,您可以定義屬性以綁定到通過泛型提供的目標(biāo)類型:

List<Integer> integers;
List<URL> urls;

可讀字節(jié)

您可以使用 @ReadableBytes 注釋任何設(shè)置器參數(shù),以允許使用用于指定字節(jié)、千字節(jié)等的簡寫語法設(shè)置值。例如,以下內(nèi)容取自 HttpClientConfiguration:

使用@ReadableBytes

public void setMaxContentLength(@ReadableBytes int maxContentLength) {
    this.maxContentLength = maxContentLength;
}

準(zhǔn)備好上述內(nèi)容后,您可以使用以下值設(shè)置 micronaut.http.client.max-content-length:

表 2. @ReadableBytes 轉(zhuǎn)換
配置值 結(jié)果值

10mb

10 MB

10kb

10 KB

10gb

10 GB

1024

原始字節(jié)長度

格式化日期

@Format 注釋可用于 setter 以指定綁定 java.time 日期對象時要使用的日期格式。

對日期使用@Format

public void setMyDate(@Format("yyyy-MM-dd") LocalDate date) {
    this.myDate = date;
}

配置生成器

許多框架和工具已經(jīng)使用構(gòu)建器風(fēng)格的類來構(gòu)造配置。

您可以使用 @ConfigurationBuilder 注釋來使用配置值填充構(gòu)建器樣式的類。 ConfigurationBuilder 可以應(yīng)用于用@ConfigurationProperties 注釋的類中的字段或方法。

由于在 Java 世界中沒有一致的方法來定義構(gòu)建器,因此可以在注釋中指定一個或多個方法前綴以支持構(gòu)建器方法,如 withXxx 或 setXxx。如果構(gòu)建器方法沒有前綴,則為參數(shù)分配一個空字符串。

還可以指定配置前綴來告訴 Micronaut 在哪里查找配置值。默認(rèn)情況下,構(gòu)建器方法使用在類級 @ConfigurationProperties 注釋中指定的配置前綴。

例如:

@ConfigurationBuilder 示例

 Java Groovy  Kotlin 
import io.micronaut.context.annotation.ConfigurationBuilder;
import io.micronaut.context.annotation.ConfigurationProperties;

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

    @ConfigurationBuilder(prefixes = "with") // (2)
    EngineImpl.Builder builder = EngineImpl.builder();

    @ConfigurationBuilder(prefixes = "with", configurationPrefix = "crank-shaft") // (3)
    CrankShaft.Builder crankShaft = CrankShaft.builder();

    private SparkPlug.Builder sparkPlug = SparkPlug.builder();

    SparkPlug.Builder getSparkPlug() {
        return sparkPlug;
    }

    @ConfigurationBuilder(prefixes = "with", configurationPrefix = "spark-plug") // (4)
    void setSparkPlug(SparkPlug.Builder sparkPlug) {
        this.sparkPlug = sparkPlug;
    }
}
import io.micronaut.context.annotation.ConfigurationBuilder
import io.micronaut.context.annotation.ConfigurationProperties

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

    @ConfigurationBuilder(prefixes = "with") // (2)
    EngineImpl.Builder builder = EngineImpl.builder()

    @ConfigurationBuilder(prefixes = "with", configurationPrefix = "crank-shaft") // (3)
    CrankShaft.Builder crankShaft = CrankShaft.builder()

    SparkPlug.Builder sparkPlug = SparkPlug.builder()

    @ConfigurationBuilder(prefixes = "with", configurationPrefix = "spark-plug") // (4)
    void setSparkPlug(SparkPlug.Builder sparkPlug) {
        this.sparkPlug = sparkPlug
    }
}
import io.micronaut.context.annotation.ConfigurationBuilder
import io.micronaut.context.annotation.ConfigurationProperties

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

    @ConfigurationBuilder(prefixes = ["with"])  // (2)
    val builder = EngineImpl.builder()

    @ConfigurationBuilder(prefixes = ["with"], configurationPrefix = "crank-shaft") // (3)
    val crankShaft = CrankShaft.builder()

    @set:ConfigurationBuilder(prefixes = ["with"], configurationPrefix = "spark-plug") // (4)
    var sparkPlug = SparkPlug.builder()
}
  1. @ConfigurationProperties 注解采用配置前綴

  2. 第一個構(gòu)建器可以在沒有類配置前綴的情況下進(jìn)行配置;它繼承自上面的。

  3. 第二個構(gòu)建器可以配置類配置前綴 + configurationPrefix 值。

  4. 第三個構(gòu)建器演示了注釋可以應(yīng)用于方法和屬性。

默認(rèn)情況下,僅支持單參數(shù)構(gòu)建器方法。對于沒有參數(shù)的方法,將注釋的 allowZeroArgs 參數(shù)設(shè)置為 true。

與前面的示例一樣,我們可以構(gòu)造一個 EngineImpl。由于我們使用的是構(gòu)建器,因此我們可以使用工廠類從構(gòu)建器構(gòu)建引擎。

Factory Bean

 Java Groovy  Kotlin 
import io.micronaut.context.annotation.Factory;

import jakarta.inject.Singleton;

@Factory
class EngineFactory {

    @Singleton
    EngineImpl buildEngine(EngineConfig engineConfig) {
        return engineConfig.builder.build(engineConfig.crankShaft, engineConfig.getSparkPlug());
    }
}
import io.micronaut.context.annotation.Factory

import jakarta.inject.Singleton

@Factory
class EngineFactory {

    @Singleton
    EngineImpl buildEngine(EngineConfig engineConfig) {
        engineConfig.builder.build(engineConfig.crankShaft, engineConfig.sparkPlug)
    }
}
import io.micronaut.context.annotation.Factory
import jakarta.inject.Singleton

@Factory
internal class EngineFactory {

    @Singleton
    fun buildEngine(engineConfig: EngineConfig): EngineImpl {
        return engineConfig.builder.build(engineConfig.crankShaft, engineConfig.sparkPlug)
    }
}

然后可以將返回的發(fā)動機(jī)注入需要發(fā)動機(jī)的任何地方。

可以從 PropertySource 實例之一提供配置值。例如:

供應(yīng)配置

 Java Groovy  Kotlin 
        Map<String, Object> properties = new HashMap<>();
        properties.put("my.engine.cylinders"             ,"4");
        properties.put("my.engine.manufacturer"          , "Subaru");
        properties.put("my.engine.crank-shaft.rod-length", 4);
        properties.put("my.engine.spark-plug.name"       , "6619 LFR6AIX");
        properties.put("my.engine.spark-plug.type"       , "Iridium");
        properties.put("my.engine.spark-plug.companyName", "NGK");
        ApplicationContext applicationContext = ApplicationContext.run(properties, "test");

        Vehicle vehicle = applicationContext.getBean(Vehicle.class);
        System.out.println(vehicle.start());
        ApplicationContext applicationContext = ApplicationContext.run(
                ['my.engine.cylinders'             : '4',
                 'my.engine.manufacturer'          : 'Subaru',
                 'my.engine.crank-shaft.rod-length': 4,
                 'my.engine.spark-plug.name'       : '6619 LFR6AIX',
                 'my.engine.spark-plug.type'       : 'Iridium',
                 'my.engine.spark-plug.companyName': 'NGK'
                ],
                "test"
        )

        Vehicle vehicle = applicationContext.getBean(Vehicle)
        println(vehicle.start())
        val applicationContext = ApplicationContext.run(
                mapOf(
                        "my.engine.cylinders" to "4",
                        "my.engine.manufacturer" to "Subaru",
                        "my.engine.crank-shaft.rod-length" to 4,
                        "my.engine.spark-plug.name" to "6619 LFR6AIX",
                        "my.engine.spark-plug.type" to "Iridium",
                        "my.engine.spark-plug.company" to "NGK"
                ),
                "test"
        )

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

上面的示例打?。骸癝ubaru Engine Starting V4 [rodLength=4.0, sparkPlug=Iridium(NGK 6619 LFR6AIX)]”

MapFormat

對于某些用例,可能需要接受可以提供給 bean 的任意配置屬性的映射,尤其是當(dāng) bean 代表第三方 API 時,其中并非所有可能的配置屬性都是已知的。例如,數(shù)據(jù)源可以接受特定于特定數(shù)據(jù)庫驅(qū)動程序的配置屬性映射,允許用戶在映射中指定任何所需的選項,而無需顯式編碼每個屬性。

為此,MapFormat 注釋允許您將映射綁定到單個配置屬性,并指定是接受鍵到值的平面映射,還是接受嵌套映射(其中值可能是附加映射)。

@MapFormat 示例

 Java Groovy  Kotlin 
import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.core.convert.format.MapFormat;

import javax.validation.constraints.Min;
import java.util.Map;

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

    @Min(1L)
    private int cylinders;

    @MapFormat(transformation = MapFormat.MapTransformation.FLAT) //(1)
    private Map<Integer, String> sensors;

    public int getCylinders() {
        return cylinders;
    }

    public void setCylinders(int cylinders) {
        this.cylinders = cylinders;
    }

    public Map<Integer, String> getSensors() {
        return sensors;
    }

    public void setSensors(Map<Integer, String> sensors) {
        this.sensors = sensors;
    }
}
import io.micronaut.context.annotation.ConfigurationProperties
import io.micronaut.core.convert.format.MapFormat

import javax.validation.constraints.Min

@ConfigurationProperties('my.engine')
class EngineConfig {

    @Min(1L)
    int cylinders

    @MapFormat(transformation = MapFormat.MapTransformation.FLAT) //(1)
    Map<Integer, String> sensors
}
import io.micronaut.context.annotation.ConfigurationProperties
import io.micronaut.core.convert.format.MapFormat
import javax.validation.constraints.Min

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

    @Min(1L)
    var cylinders: Int = 0

    @MapFormat(transformation = MapFormat.MapTransformation.FLAT) //(1)
    var sensors: Map<Int, String>? = null
}
  1. 注意注釋的轉(zhuǎn)換參數(shù);可能的值是 MapTransformation.FLAT(對于平面地圖)和 MapTransformation.NESTED(對于嵌套地圖)

EngineImpl

 Java Groovy  Kotlin 
@Singleton
public class EngineImpl implements Engine {

    @Inject
    EngineConfig config;

    @Override
    public Map getSensors() {
        return config.getSensors();
    }

    @Override
    public String start() {
        return "Engine Starting V" + getConfig().getCylinders() +
               " [sensors=" + getSensors().size() + "]";
    }

    public EngineConfig getConfig() {
        return config;
    }

    public void setConfig(EngineConfig config) {
        this.config = config;
    }
}
@Singleton
class EngineImpl implements Engine {

    @Inject EngineConfig config

    @Override
    Map getSensors() {
        config.sensors
    }

    @Override
    String start() {
        "Engine Starting V$config.cylinders [sensors=${sensors.size()}]"
    }
}
@Singleton
class EngineImpl : Engine {

    override val sensors: Map<*, *>?
        get() = config!!.sensors

    @Inject
    var config: EngineConfig? = null

    override fun start(): String {
        return "Engine Starting V${config!!.cylinders} [sensors=${sensors!!.size}]"
    }
}

現(xiàn)在可以將屬性映射提供給 my.engine.sensors 配置屬性。

使用地圖配置

 Java Groovy  Kotlin 
Map<String, Object> map = new LinkedHashMap<>(2);
map.put("my.engine.cylinders", "8");

Map<Integer, String> map1 = new LinkedHashMap<>(2);
map1.put(0, "thermostat");
map1.put(1, "fuel pressure");

map.put("my.engine.sensors", map1);

ApplicationContext applicationContext = ApplicationContext.run(map, "test");

Vehicle vehicle = applicationContext.getBean(Vehicle.class);
System.out.println(vehicle.start());
ApplicationContext applicationContext = ApplicationContext.run(
        ['my.engine.cylinders': '8',
         'my.engine.sensors'  : [0: 'thermostat',
                                 1: 'fuel pressure']],
        "test"
)

def vehicle = applicationContext.getBean(Vehicle)
println(vehicle.start())
val subMap = mapOf(
    0 to "thermostat",
    1 to "fuel pressure"
)
val map = mapOf(
    "my.engine.cylinders" to "8",
    "my.engine.sensors" to subMap
)

val applicationContext = ApplicationContext.run(map, "test")

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

上面的示例打?。骸癊ngine Starting V8 [sensors=2]”


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號