阅读(3108) (0)

Micronaut 使用 PropertySources 的外部化配置

2023-02-23 11:02:14 更新

在初始化 ApplicationContext 之前,可以将其他 PropertySource 实例添加到环境中。

初始化环境

 Java Groovy  Kotlin 
ApplicationContext applicationContext = ApplicationContext.run(
        PropertySource.of(
                "test",
                CollectionUtils.mapOf(
                    "micronaut.server.host", "foo",
                    "micronaut.server.port", 8080
                )
        ),
        "test", "android");
Environment environment = applicationContext.getEnvironment();

assertEquals(
        "foo",
        environment.getProperty("micronaut.server.host", String.class).orElse("localhost")
);
when:
ApplicationContext applicationContext = ApplicationContext.run(
        PropertySource.of(
                "test",
                [
                    "micronaut.server.host": "foo",
                    "micronaut.server.port": 8080
                ]
        ),
        "test", "android")
Environment environment = applicationContext.getEnvironment()

then:
"foo" == environment.getProperty("micronaut.server.host", String.class).orElse("localhost")
val applicationContext = ApplicationContext.run(
    PropertySource.of(
        "test",
        mapOf(
            "micronaut.server.host" to "foo",
            "micronaut.server.port" to 8080
        )
    ),
    "test", "android"
)
val environment = applicationContext.environment

assertEquals(
    "foo",
    environment.getProperty("micronaut.server.host", String::class.java).orElse("localhost")
)

PropertySource.of 方法可用于从值映射创建 PropertySource。

或者,可以通过创建包含对 PropertySourceLoader 类名的引用的 META-INF/services/io.micronaut.context.env.PropertySourceLoader 文件来注册 PropertySourceLoader。

包含的 PropertySource 加载器

默认情况下,Micronaut 包含从给定位置和优先级加载属性的 PropertySourceLoader 实现:

  1. 命令行参数

  2. 来自 SPRING_APPLICATION_JSON 的属性(为了与 Spring 兼容)

  3. 来自 MICRONAUT_APPLICATION_JSON 的属性

  4. Java 系统属性

  5. 操作系统环境变量

  6. 配置文件从系统属性“micronaut.config.files”或环境变量 MICRONAUT_CONFIG_FILES 中按顺序加载。该值可以是以逗号分隔的路径列表,最后一个文件具有优先权。这些文件可以作为路径从文件系统中引用,或者作为带有 classpath: 前缀的类路径引用。

  7. 来自 application-{environment}.{extension} 的环境特定属性

  8. 来自 application.{extension} 的特定于应用程序的属性

开箱即用地支持 .properties、.json、.yml。对于 Groovy 用户,也支持 .groovy。

请注意,如果您想完全控制应用程序从何处加载配置,您可以通过在启动应用程序时调用 ApplicationContextBuilder 接口的 enableDefaultPropertySources(false) 方法来禁用上面列出的默认 PropertySourceLoader 实现。

在这种情况下,只会使用您通过 ApplicationContextBuilder 接口的 propertySources(..) 方法添加的显式 PropertySource 实例。

通过命令行提供配置

可以使用 Gradle 或我们的 Maven 插件在命令行中提供配置。例如:

Gradle

$ ./gradlew run --args="-endpoints.health.enabled=true -config.property=test"

Maven

$ ./mvnw mn:run -Dmn.appArgs="-endpoints.health.enabled=true -config.property=test"

为了使配置成为上下文的一部分,必须将 main 方法中的参数传递给上下文构建器。例如:

import io.micronaut.runtime.Micronaut;

public class Application {

    public static void main(String[] args) {
        Micronaut.run(Application.class, args); // passing args
    }
}

秘密和敏感配置

重要的是要注意,不建议将敏感配置(例如密码和令牌)存储在可能被检入源代码控制系统的配置文件中。

最好使用外部秘密管理器系统(这里有很多选项,许多由云提供商提供)或在应用程序部署期间设置的环境变量,而不是从应用程序代码中完全外部化敏感配置。您还可以使用属性占位符(请参阅下一节)来自定义要使用的环境变量的名称并提供默认值:

使用属性值占位符定义安全配置

 Properties Yaml  Toml  Groovy  Hocon  JSON 
datasources.default.url=${JDBC_URL:`jdbc:mysql://localhost:3306/db`}
datasources.default.username=${JDBC_USER:root}
datasources.default.password=${JDBC_PASSWORD:}
datasources.default.dialect=MYSQL
datasources.default.driverClassName=${JDBC_DRIVER:com.mysql.cj.jdbc.Driver}
datasources:
  default:
    url: ${JDBC_URL:`jdbc:mysql://localhost:3306/db`}
    username: ${JDBC_USER:root}
    password: ${JDBC_PASSWORD:}
    dialect: MYSQL
    driverClassName: ${JDBC_DRIVER:com.mysql.cj.jdbc.Driver}
[datasources]
  [datasources.default]
    url="${JDBC_URL:`jdbc:mysql://localhost:3306/db`}"
    username="${JDBC_USER:root}"
    password="${JDBC_PASSWORD:}"
    dialect="MYSQL"
    driverClassName="${JDBC_DRIVER:com.mysql.cj.jdbc.Driver}"
datasources {
  'default' {
    url = "${JDBC_URL:`jdbc:mysql://localhost:3306/db`}"
    username = "${JDBC_USER:root}"
    password = "${JDBC_PASSWORD:}"
    dialect = "MYSQL"
    driverClassName = "${JDBC_DRIVER:com.mysql.cj.jdbc.Driver}"
  }
}
{
  datasources {
    default {
      url = "${JDBC_URL:`jdbc:mysql://localhost:3306/db`}"
      username = "${JDBC_USER:root}"
      password = "${JDBC_PASSWORD:}"
      dialect = "MYSQL"
      driverClassName = "${JDBC_DRIVER:com.mysql.cj.jdbc.Driver}"
    }
  }
}
{
  "datasources": {
    "default": {
      "url": "${JDBC_URL:`jdbc:mysql://localhost:3306/db`}",
      "username": "${JDBC_USER:root}",
      "password": "${JDBC_PASSWORD:}",
      "dialect": "MYSQL",
      "driverClassName": "${JDBC_DRIVER:com.mysql.cj.jdbc.Driver}"
    }
  }
}

为了安全地将配置外部化,请考虑使用 Micronaut 支持的秘密管理器系统,例如:

属性值占位符

如前一节所述,Micronaut 包含一个属性占位符语法,用于在配置值中和任何 Micronaut 注释中引用配置属性。

也可以通过 PropertyPlaceholderResolver 接口以编程方式使用。

基本语法是将对属性的引用包装在 ${... } 中。例如:

定义属性占位符

 Properties Yaml  Toml  Groovy  Hocom  JSON 
myapp.endpoint=http://${micronaut.server.host}:${micronaut.server.port}/foo
myapp:
  endpoint: http://${micronaut.server.host}:${micronaut.server.port}/foo
[myapp]
  endpoint="http://${micronaut.server.host}:${micronaut.server.port}/foo"
myapp {
  endpoint = "http://${micronaut.server.host}:${micronaut.server.port}/foo"
}
{
  myapp {
    endpoint = "http://${micronaut.server.host}:${micronaut.server.port}/foo"
  }
}
{
  "myapp": {
    "endpoint": "http://${micronaut.server.host}:${micronaut.server.port}/foo"
  }
}

上面的示例嵌入了对 micronaut.server.host 和 micronaut.server.port 属性的引用。

您可以通过在 : 字符后定义一个值来指定默认值。例如:

使用默认值

 Properties Yaml  Toml  Groovy  Hocom  JSON 
myapp.endpoint=http://${micronaut.server.host:localhost}:${micronaut.server.port:8080}/foo
myapp:
  endpoint: http://${micronaut.server.host:localhost}:${micronaut.server.port:8080}/foo
[myapp]
  endpoint="http://${micronaut.server.host:localhost}:${micronaut.server.port:8080}/foo"
myapp {
  endpoint = "http://${micronaut.server.host:localhost}:${micronaut.server.port:8080}/foo"
}
{
  myapp {
    endpoint = "http://${micronaut.server.host:localhost}:${micronaut.server.port:8080}/foo"
  }
}
{
  "myapp": {
    "endpoint": "http://${micronaut.server.host:localhost}:${micronaut.server.port:8080}/foo"
  }
}

如果没有找到值(而不是抛出异常),上面的示例默认为 localhost 和端口 8080。请注意,如果默认值包含 : 字符,则必须使用反引号对其进行转义:

使用反引号

 Properties Yaml  Toml  Groovy  Hocom  JSON 
myapp.endpoint=${server.address:`http://localhost:8080`}/foo
myapp:
  endpoint: ${server.address:`http://localhost:8080`}/foo
[myapp]
  endpoint="${server.address:`http://localhost:8080`}/foo"
myapp {
  endpoint = "${server.address:`http://localhost:8080`}/foo"
}
{
  myapp {
    endpoint = "${server.address:`http://localhost:8080`}/foo"
  }
}
{
  "myapp": {
    "endpoint": "${server.address:`http://localhost:8080`}/foo"
  }
}

上面的示例查找 server.address 属性并默认为 http://localhost:8080。这个默认值用反引号转义,因为它有一个 : 字符。

属性值绑定

请注意,在代码或占位符值中放置引用时,这些属性引用应采用 kebab 大小写(小写和连字符分隔)。例如,使用 micronaut.server.default-charset 而不是 micronaut.server.defaultCharset。

Micronaut 仍然允许在配置中指定后者,但将属性规范化为 kebab case 形式,以优化内存消耗并降低解析属性时的复杂性。下表总结了如何从不同来源规范化属性:

表 1. 属性值规范化
配置值 结果属性 属性来源

myApp.myStuff

my-app.my-stuff

Properties, YAML etc.

my-app.myStuff

my-app.my-stuff

Properties, YAML etc.

myApp.my-stuff

my-app.my-stuff

Properties, YAML etc.

MYAPP_MYSTUFF

myapp.mystuffmyapp-mystuff

Environment Variable

MY_APP_MY_STUFF

my.app.my.stuffmy.app.my-stuffmy.app-my.stuffmy.app-my-stuffmy-app.my.stuffmy-app.my-stuffmy-app-my.stuffmy-app-my-stuff

Environment Variable

环境变量经过特殊处理以提供更大的灵活性。请注意,无法使用驼峰式大小写来引用环境变量。

由于生成的属性数量根据环境变量中_字符的数量呈指数增长,因此如果具有多个下划线的环境变量数量较多,建议细化配置中包含的环境变量(如果有)。

要控制环境属性如何参与配置,请在 Micronaut 构建器上调用相应的方法。

应用类

 Java  Groovy Kotlin 
import io.micronaut.runtime.Micronaut;

public class Application {

    public static void main(String[] args) {
        Micronaut.build(args)
                .mainClass(Application.class)
                .environmentPropertySource(false)
                //or
                .environmentVariableIncludes("THIS_ENV_ONLY")
                //or
                .environmentVariableExcludes("EXCLUDED_ENV")
                .start();
    }
}
import io.micronaut.runtime.Micronaut

class Application {

    static void main(String[] args) {
        Micronaut.build()
                .mainClass(Application)
                .environmentPropertySource(false)
                //or
                .environmentVariableIncludes("THIS_ENV_ONLY")
                //or
                .environmentVariableExcludes("EXCLUDED_ENV")
                .start()
    }
}
import io.micronaut.runtime.Micronaut

object Application {

    @JvmStatic
    fun main(args: Array<String>) {
        Micronaut.build(null)
                .mainClass(Application::class.java)
                .environmentPropertySource(false)
                //or
                .environmentVariableIncludes("THIS_ENV_ONLY")
                //or
                .environmentVariableExcludes("EXCLUDED_ENV")
                .start()
    }
}

上面的配置对属性占位符没有任何影响。无论是否禁用环境配置,或者即使明确排除特定属性,仍然可以在占位符中引用环境变量。

使用随机属性

您可以通过使用以下属性来使用随机值。这些可以在配置文件中用作变量,如下所示。

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.application.name=myapplication
micronaut.application.instance.id=${random.shortuuid}
micronaut:
  application:
    name: myapplication
    instance:
      id: ${random.shortuuid}
[micronaut]
  [micronaut.application]
    name="myapplication"
    [micronaut.application.instance]
      id="${random.shortuuid}"
micronaut {
  application {
    name = "myapplication"
    instance {
      id = "${random.shortuuid}"
    }
  }
}
{
  micronaut {
    application {
      name = "myapplication"
      instance {
        id = "${random.shortuuid}"
      }
    }
  }
}
{
  "micronaut": {
    "application": {
      "name": "myapplication",
      "instance": {
        "id": "${random.shortuuid}"
      }
    }
  }
}
表 2. 随机值
属性

random.port

一个可用的随机端口号

random.int

随机整数

random.integer

随机整数

random.long

随机长整数

random.float

随机浮点数

random.shortuuid

长度仅为 10 个字符的随机 UUID(注意:由于这不是完整的 UUID,可能会发生冲突)

random.uuid

带破折号的随机 UUID

random.uuid2

没有破折号的随机 UUID

random.int、random.integer、random.long 和 random.float 属性支持范围后缀,其语法为以下之一:

  • (max) 其中 max 是一个独占值

  • [min,max] 其中 min 是包容性的,max 是独占的。

 Properties Yaml  Toml   Groovy Hocon  JSON 
instance.id=${random.int[5,10]}
instance.count=${random.int(5)}
instance:
  id: ${random.int[5,10]}
  count: ${random.int(5)}
[instance]
  id="${random.int[5,10]}"
  count="${random.int(5)}"
instance {
  id = "${random.int[5,10]}"
  count = "${random.int(5)}"
}
{
  instance {
    id = "${random.int[5,10]}"
    count = "${random.int(5)}"
  }
}
{
  "instance": {
    "id": "${random.int[5,10]}",
    "count": "${random.int(5)}"
  }
}

该范围也可以从负到正变化。

快速失败属性注入

对于注入所需属性的 bean,在请求 bean 之前不会发生注入和潜在的失败。要在启动时验证属性是否存在并且可以注入,可以使用 @Context 对 bean 进行注释。上下文范围的 beans 在启动时注入,如果缺少任何必需的属性或无法转换为所需的类型,则启动将失败。

建议谨慎使用此功能以确保快速启动。