Accessing application properties with Spring Boot properties API

如果你希望使用 Spring Boot @ConfigurationProperties 注解类来访问应用程序属性,而不是 @ConfigMapping 或 MicroProfile @ConfigProperty 方法,你可以使用此扩展。

Spring Boot @ConfigurationProperties 有些局限。例如,不支持注入 Map。请考虑使用 Mapping configuration to objects

Prerequisites

如要完成本指南,您需要:

  • Roughly 15 minutes

  • An IDE

  • 安装了 JDK 17+,已正确配置 JAVA_HOME

  • Apache Maven ${proposed-maven-version}

  • 如果你想使用 Quarkus CLI, 则可以选择使用

  • 如果你想构建一个本机可执行文件(或如果你使用本机容器构建,则使用 Docker),则可以选择安装 Mandrel 或 GraalVM 以及 configured appropriately

Solution

我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。

克隆 Git 存储库: git clone $${quickstarts-base-url}.git,或下载 $${quickstarts-base-url}/archive/main.zip[存档]。

该解法位于 spring-boot-properties-quickstart…​ directory

Creating the Maven project

首先,我们需要一个新项目。使用以下命令创建一个新项目:

CLI
quarkus create app {create-app-group-id}:{create-app-artifact-id} \
    --no-code
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 --gradle--gradle-kotlin-dsl 选项。 有关如何安装和使用 Quarkus CLI 的详细信息,请参见 Quarkus CLI 指南。

Maven
mvn {quarkus-platform-groupid}:quarkus-maven-plugin:{quarkus-version}:create \
    -DprojectGroupId={create-app-group-id} \
    -DprojectArtifactId={create-app-artifact-id} \
    -DnoCode
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 -DbuildTool=gradle-DbuildTool=gradle-kotlin-dsl 选项。

适用于 Windows 用户:

  • 如果使用 cmd,(不要使用反斜杠 \ ,并将所有内容放在同一行上)

  • 如果使用 Powershell,将 -D 参数用双引号引起来,例如 "-DprojectArtifactId={create-app-artifact-id}"

此命令会生成一个项目并导入 spring-boot-properties 扩展。

如果你已经配置好了 Quarkus 项目,则可以按如下方式在项目基本目录中运行以下命令,将 spring-boot-properties 扩展添加到项目:

CLI
quarkus extension add {add-extension-extensions}
Maven
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
Gradle
./gradlew addExtension --extensions='{add-extension-extensions}'

这会将以下内容添加到构建文件中:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-spring-boot-properties</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-spring-boot-properties")

GreetingController

首先,以类似如下所示方式在 src/main/java/org/acme/spring/boot/properties/GreetingResource.java 文件中创建一个 GreetingResource Jakarta REST 资源:

package org.acme.spring.boot.properties;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/hello")
public class GreetingResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }
}

Injecting properties

创建一个带有 message 字段的新类 src/main/java/org/acme/spring/boot/properties/GreetingProperties.java

package org.acme.spring.boot.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("greeting")
public class GreetingProperties {

    public String text;
}

此处的 text 字段是公共字段,但也可以是带 getter 和 setter 方法的私有字段,也可以是接口中的公共 getter。因为 text 没有默认值,因此被视为必需字段,而且除非在配置文件中定义(默认情况下为 application.properties),否则你的应用程序将启动失败。在你的 src/main/resources/application.properties 文件中定义此属性:

# Your configuration properties
greeting.text=hello

现在修改 GreetingResource,以开始使用 GreetingProperties

package org.acme.spring.boot.properties;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/greeting")
public class GreetingResource {

    @Inject
    GreetingProperties properties;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return properties.text;
    }
}

运行测试,验证应用程序仍能正确运行。

Package and run the application

按如下方式在开发模式下运行应用程序:

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

用浏览器打开 [role="bare"][role="bare"]http://localhost:8080/greeting.

修改配置文件会立即反映出来。

和往常一样,可以使用以下命令打包应用程序:

CLI
quarkus build
Maven
./mvnw install
Gradle
./gradlew build

并使用 `java -jar target/quarkus-app/quarkus-run.jar`执行。

你还可以按如下方式生成本机可执行文件:

CLI
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.native.enabled=true

Default values

现在我们添加一个我们设置默认值的问候语后缀。

具有默认值的属性可以像任何其他属性一样在配置文件中配置。但是,如果属性未在配置文件中定义,则将使用默认值。

继续并将新字段添加到 @ [1] 类:

package org.acme.spring.boot.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("greeting")
public class GreetingProperties {

    public String text;

    public String suffix = "!";
}

并更新 @ [2] 及其测试 @ [3]

package org.acme.spring.boot.properties;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/greeting")
public class GreetingResource {

    @Inject
    GreetingProperties properties;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return properties.text + properties.suffix;
    }
}
package org.acme.spring.boot.properties;

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;

@QuarkusTest
public class GreetingResourceTest {

    @Test
    public void testHelloEndpoint() {
        given()
          .when().get("/greeting")
          .then()
             .statusCode(200)
             .body(is("hello!"));
    }
}

运行测试以验证更改。

Optional values

具有可选值的属性是标准和具有默认值的属性之间的中间值。虽然配置文件中缺少的属性不会导致你的应用程序失败,但它仍然没有设置值。我们使用 @ [4] 类型来定义此类属性。

@ [6] 添加一个可选的 @ [5] 属性:

package org.acme.spring.boot.properties;

import java.util.Optional;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("greeting")
public class GreetingProperties {

    public String text;

    public String suffix = "!";

    public Optional<String> name;
}

并更新 @ [2] 及其测试 @ [3]

package org.acme.spring.boot.properties;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/greeting")
public class GreetingResource {

    @Inject
    GreetingProperties properties;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return properties.text + ", " + properties.name.orElse("You") + properties.suffix;
    }
}
package org.acme.spring.boot.properties;

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;

@QuarkusTest
public class GreetingResourceTest {

    @Test
    public void testHelloEndpoint() {
        given()
          .when().get("/greeting")
          .then()
             .statusCode(200)
             .body(is("hello, You!"));
    }
}

运行测试以验证更改。

Grouping properties

现在我们的 @ [7] 类中有三个属性。虽然 @ [8] 可以被认为更多是一个运行时属性(并且将来可能可以作为 HTTP 查询参数传递),但 @ [9]@ [10] 用于定义一个消息模板。让我们将这两个属性分组到一个单独的内部类:

package org.acme.spring.boot.properties;

import java.util.Optional;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("greeting")
public class GreetingProperties {

    public Message message;

    public Optional<String> name;

    public static class Message {

        public String text;

        public String suffix = "!";
    }
}

这里 @ [11] 属性类被定义为一个内部类,但它也可以是顶级类。

具有这样的属性组将为你的配置带来更多的结构。当属性数增长时,这一点特别有用。

由于添加了类,我们的属性名称发生了变化。让我们更新属性文件和 @ [12] 类。

# Your configuration properties
greeting.message.text=hello
package org.acme.spring.boot.properties;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/greeting")
public class GreetingResource {

    @Inject
    GreetingProperties properties;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return properties.message.text + ", " + properties.name.orElse("You") + properties.message.suffix;
    }
}