Accessing application properties with Spring Boot properties API

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

If you prefer to use Spring Boot @ConfigurationProperties annotated class to access application properties instead of @ConfigMapping or a MicroProfile @ConfigProperty approach, you can do that with this extension.

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

Spring Boot @ConfigurationProperties has a few limitations. For instance, Map injection is not supported. Consider using Mapping configuration to objects.

Prerequisites

Unresolved directive in spring-boot-properties.adoc - include::{includes}/prerequisites.adoc[]

Solution

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

We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.

克隆 Git 存储库: git clone {quickstarts-clone-url},或下载 {quickstarts-archive-url}[存档]。

Clone the Git repository: git clone {quickstarts-clone-url}, or download an {quickstarts-archive-url}[archive].

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

The solution is located in the spring-boot-properties-quickstart directory.

Creating the Maven project

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

First, we need a new project. Create a new project with the following command:

Unresolved directive in spring-boot-properties.adoc - include::{includes}/devtools/create-app.adoc[]

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

This command generates a project and imports the spring-boot-properties extension.

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

If you already have your Quarkus project configured, you can add the spring-boot-properties extension to your project by running the following command in your project base directory:

Unresolved directive in spring-boot-properties.adoc - include::{includes}/devtools/extension-add.adoc[]

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

This will add the following to your build file:

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 资源:

First, create a GreetingResource Jakarta REST resource in the src/main/java/org/acme/spring/boot/properties/GreetingResource.java file that looks like:

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

Create a new class src/main/java/org/acme/spring/boot/properties/GreetingProperties.java with a message field:

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 文件中定义此属性:

Here text field is public, but it could also be a private field with getter and setter or just a public getter in an interface. Because text does not have a default value it is considered required and unless it is defined in a configuration file (application.properties by default) your application will fail to start. Define this property in your src/main/resources/application.properties file:

# Your configuration properties
greeting.text=hello

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

Now modify GreetingResource to start using the 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;
    }
}

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

Run the tests to verify that application still functions correctly.

Package and run the application

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

Run the application in dev mode with:

Unresolved directive in spring-boot-properties.adoc - include::{includes}/devtools/dev.adoc[]

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

Open your browser to [role="bare"]http://localhost:8080/greeting.

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

Changing the configuration file is immediately reflected.

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

As usual, the application can be packaged using:

Unresolved directive in spring-boot-properties.adoc - include::{includes}/devtools/build.adoc[]

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

And executed using java -jar target/quarkus-app/quarkus-run.jar.

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

You can also generate the native executable with:

Unresolved directive in spring-boot-properties.adoc - include::{includes}/devtools/build-native.adoc[]

Default values

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

Now let’s add a suffix for a greeting for which we’ll set a default value.

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

Properties with default values can be configured in a configuration file just like any other property. However, the default value will be used if the property was not defined in a configuration file.

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

Go ahead and add the new field to the GreetingProperties class:

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]

And update the GreetingResource and its test GreetingResourceTest:

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!"));
    }
}

运行测试以验证更改。

Run the tests to verify the change.

Optional values

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

Properties with optional values are the middle-ground between standard and properties with default values. While a missing property in a configuration file will not cause your application to fail, it will nevertheless not have a value set. We use java.util.Optional type to define such properties.

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

Add an optional name property to the GreetingProperties:

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]

And update the GreetingResource and its test GreetingResourceTest:

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!"));
    }
}

运行测试以验证更改。

Run the tests to verify the change.

Grouping properties

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

Now we have three properties in our GreetingProperties class. While name could be considered more of a runtime property (and maybe could be passed as an HTTP query parameter in the future), text and suffix are used to define a message template. Let’s group these two properties in a separate inner class:

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] 属性类被定义为一个内部类,但它也可以是顶级类。

Here Message properties class is defined as an inner class, but it could also be a top level class.

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

Having such property groups brings more structure to your configuration. This is especially useful when then number of properties grows.

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

Because of the additional class, our property names have changed. Let’s update the properties file and the GreetingResource class.

# 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;
    }
}