Quarkus - 配置应用程序
在代码中硬编码值是不可行的(这种事我们都干过)。本指南我们将学习如何配置应用
代码
我们建议你跟着下边介绍一步一步创建应用。 但是你也可以直接看完整的例子。
克隆 Git 仓库: git clone https://github.com/quarkusio/quarkus-quickstarts.git
, 或下载 存档].
代码在 config-quickstart
目录.
创建 Maven 项目
首先,我们需要创建一个新项目,用下边命令创建:
mvn io.quarkus:quarkus-maven-plugin:1.3.1.Final:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=config-quickstart \
-DclassName="org.acme.config.GreetingResource" \
-Dpath="/greeting"
cd config-quickstart
它会生成:
-
Maven 项目目录结构
-
可访问的页面
http://localhost:8080
-
native
及jvm
模式的Dockerfile
样例文件 -
应用配置文件
-
org.acme.config.GreetingResource
资源 -
一个相关测试
注入配置项
Quarkus 使用 MicroProfile Config 将配置注入到应用。
注入使用 @ConfigProperty
注解
@ConfigProperty(name = "greeting.message")
String message;
注入配置时 可以使用 @Inject @ConfigProperty 或仅 @ConfigProperty
对于标注了 @ConfigProperty 的成员, 注解 @Inject 不是必须的, 这点与 MicroProfile Config 不一样
|
编辑 org.acme.config.GreetingResource
并加入下列配置属性:
@ConfigProperty(name = "greeting.message") (1)
String message;
@ConfigProperty(name = "greeting.suffix", defaultValue="!") (2)
String suffix;
@ConfigProperty(name = "greeting.name")
Optional<String> name; (3)
1 | 如果没有为属性配置值,应用会启动失败报错 javax.enterprise.inject.spi.DeploymentException: No config value of type [class java.lang.String] exists for: greeting.message . |
2 | 如果没有配置 greeting.suffix 的值会注入默认值. |
3 | 可选属性 - 如果没有配置 greeting.name 值会注入 Optional.empty() . |
现在修改 hello
方法使用注入的属性:
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return message + " " + name.orElse("world") + suffix;
}
创建配置
默认 Quarkus 读取 application.properties
.
用下列内容编辑 src/main/resources/application.properties
# Your configuration properties
greeting.message = hello
greeting.name = quarkus
一旦设置,用下列方式检查:
$ curl http://localhost:8080/greeting
hello quarkus!
如果应用需要配置值但是没有配置,会抛出错误。 所以你可以快速知道配置是否完整。 |
更新和测试
我们也需要相应调整功能测试。
编辑 src/test/java/org/acme/config/GreetingResourceTest.java
文件修改方法 testHelloEndpoint
的内容为:
package org.acme.config;
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 quarkus!")); // Modified line
}
}
打包并运行应用
使用 ./mvnw compile quarkus:dev
运行应用.
并用浏览器打开 http://localhost:8080/greeting.
修改配置文件后马上就能看到变化。
你可以添加 greeting.suffix
, 删除其它属性,修改值,等等。
一般,应用可以使用 ./mvnw clean package
打包并用 -runner.jar
文件来运行。
你也可以用 ./mvnw clean package -Pnative
生成原生执行程序。
编程访问配置
可以编程访问配置。(QFY译者: 相对上边的注解方式) 这可以实现动态查询,或从 CDI beans 或 JAX-RS 资源类中获取配置值。
你可以如下所示用 org.eclipse.microprofile.config.ConfigProvider.getConfig()
通过编程方式访问配置:
String databaseName = ConfigProvider.getConfig().getValue("database.name", String.class);
Optional<String> maybeDatabaseName = ConfigProvider.getConfig().getOptionalValue("database.name", String.class);
使用 @ConfigProperties
上边例子示范的是注入多个相关配置值的一种方式,
用户也可以使用 @io.quarkus.arc.config.ConfigProperties
注解将这些属性分组。
对上边的 greeting 属性,可以创建一个 GreetingConfiguration
类:
package org.acme.config;
import io.quarkus.arc.config.ConfigProperties;
import java.util.Optional;
@ConfigProperties(prefix = "greeting") (1)
public class GreetingConfiguration {
private String message;
private String suffix = "!"; (2)
private Optional<String> name;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public Optional<String> getName() {
return name;
}
public void setName(Optional<String> name) {
this.name = name;
}
}
1 | prefix 是可选的。如果没有设置前缀(prefix)会使用类名推测。这里还是使用 greeting (现在 Configuration 的后缀已经删除了)。如果类名是 GreetingExtraConfiguration 默认前缀是 greeting-extra . |
2 | 如果没有设置 greeting.suffix 会使用默认值 ! 。 |
稍后可以将这个类用熟悉的 CDI @Inject
注解注入到 GreetingResource
:
@Inject
GreetingConfiguration greetingConfiguration;
Quarkus 还提供了另外一种风格是将 GreetingConfiguration
作为接口:
package org.acme.config;
import io.quarkus.arc.config.ConfigProperties;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import java.util.Optional;
@ConfigProperties(prefix = "greeting")
public interface GreetingConfiguration {
@ConfigProperty(name = "message") (1)
String message();
@ConfigProperty(defaultValue = "!")
String getSuffix(); (2)
Optional<String> getName(); (3)
}
1 | The @ConfigProperty annotation is needed because the name of the configuration property that the method corresponds to doesn’t follow the getter method naming conventions |
2 | In this case since name was not set, the corresponding property will be greeting.suffix . |
3 | It is unnecessary to specify the @ConfigProperty annotation because the method name follows the getter method naming conventions (greeting.name being the corresponding property) and no default value is needed. |
When using @ConfigProperties
on a class or an interface, if the value of one of its fields is not provided, the application startup will fail and a javax.enterprise.inject.spi.DeploymentException
indicating the missing value information will be thrown.
This does not apply to Optional
fields and fields with a default value.
额外注意 @ConfigProperties
When using a regular class annotated with @ConfigProperties
the class doesn’t necessarily have to declare getters and setters.
Having simple public non-final fields is valid as well.
Furthermore the configuration classes support nested object configuration. Suppose there was a need to have an extra layer
of greeting configuration named hidden
that would contain a few fields. This could be achieved like so:
@ConfigProperties(prefix = "greeting")
public class GreetingConfiguration {
public String message;
public String suffix = "!";
public Optional<String> name;
public HiddenConfig hidden; (1)
public static class HiddenConfig {
public Integer prizeAmount;
public List<String> recipients;
}
}
1 | The name of the field (not the class name) will determine the name of the properties that are bound to the object. |
Setting the properties would occur in the normal manner, for example in application.properties
one could have:
greeting.message = hello
greeting.name = quarkus
greeting.hidden.prizeAmount=10
greeting.hidden.recipients=Jane,John
Furthermore, classes annotated with @ConfigProperties
can be annotated with Bean Validation annotations similar to the following example:
@ConfigProperties(prefix = "greeting")
public class GreetingConfiguration {
@Size(min = 20)
public String message;
public String suffix = "!";
}
If the validation fails with the given configuration, the application will fail to start and indicate the corresponding validation errors in the log.
In the case of an interface being annotated with @ConfigProperties
, the interface is allowed to extend other interfaces and methods from
the entire interface hierarchy are used to bind properties.
配置 Quarkus
Quarkus itself is configured via the same mechanism as your application. Quarkus reserves the quarkus.
namespace
for its own configuration. For example to configure the HTTP server port you can set quarkus.http.port
in
application.properties
.
As mentioned above, properties prefixed with In the previous examples using |
所有配置项
All the Quarkus configuration properties are documented and searcheable.
生成程序的配置
It is also possible to generate an example application.properties
with all known configuration properties, to make
it easy to see what Quarkus configuration options are available. To do this, run:
./mvnw quarkus:generate-config
This will create a src/main/resources/application.properties.example
file that contains all the config options
exposed via the extensions you currently have installed. These options are commented out, and have their default value
when applicable. For example this HTTP port config entry will appear as:
#
# The HTTP port
#
#quarkus.http.port=8080
Rather than generating an example config file, you can also add these to you actual config file by setting the -Dfile
parameter:
./mvnw quarkus:generate-config -Dfile=application.properties
If a config option is already present (commented or not) it will not be added, so it is safe to run this after adding an additional extension to see what additional options have been added.
运行时改写配置
Quarkus does much of its configuration and bootstrap at build time. Most properties will then be read and set during the build time step. To change them, make sure to repackage your application.
./mvnw clean package
Extensions do define some properties as overridable at runtime. A canonical example is the database URL, username and password which is only known specifically in your target environment. This is a tradeoff as the more runtime properties are available, the less build time prework Quarkus can do. The list of runtime properties is therefore lean.
You can override these runtime properties with the following mechanisms (in decreasing priority):
-
using system properties:
-
for a runner jar:
java -Dquarkus.datasource.password=youshallnotpass -jar target/myapp-runner.jar
-
for a native executable:
./target/myapp-runner -Dquarkus.datasource.password=youshallnotpass
-
-
using environment variables:
-
for a runner jar:
export QUARKUS_DATASOURCE_PASSWORD=youshallnotpass ; java -jar target/myapp-runner.jar
-
for a native executable:
export QUARKUS_DATASOURCE_PASSWORD=youshallnotpass ; ./target/myapp-runner
-
-
using an environment file named
.env
placed in the current working directory containing the lineQUARKUS_DATASOURCE_PASSWORD=youshallnotpass
(for dev mode, this file can be placed in the root of the project, but it is advised to not check it in to version control) -
using a configuration file placed in
$PWD/config/application.properties
-
By placing an
application.properties
file inside a directory namedconfig
which resides in the directory where the application runs, any runtime properties defined in that file will override the default configuration. Furthermore any runtime properties added to this file that were not part of the originalapplication.properties
file will also be taken into account. -
This works in the same way for runner jar and the native executable
-
Environment variables names are following the conversion rules of Eclipse MicroProfile |
The config/application.properties features is available in development mode as well. To make use of it, config/application.properties needs to be placed inside the build tool’s output directory (target for Maven and build/classes/java/main for Gradle).
Keep in mind however that any cleaning operation from the build tool like mvn clean or gradle clean will remove the config directory as well.
|
配置 Profiles
Quarkus supports the notion of configuration profiles. These allow you to have multiple configuration in the same file and select between them via a profile name.
The syntax for this is %{profile}.config.key=value
. For example if I have the following:
quarkus.http.port=9090
%dev.quarkus.http.port=8181
The Quarkus HTTP port will be 9090, unless the dev
profile is active, in which case it will be 8181.
By default Quarkus has three profiles, although it is possible to use as many as you like. The default profiles are:
-
dev - Activated when in development mode (i.e.
quarkus:dev
) -
test - Activated when running tests
-
prod - The default profile when not running in development or test mode
There are two ways to set a custom profile, either via the quarkus.profile
system property or the QUARKUS_PROFILE
environment variable. If both are set the system property takes precedence. Note that it is not necessary to
define the names of these profiles anywhere, all that is necessary is to create a config property with the profile
name, and then set the current profile to that name. For example if I want a staging
profile with a different HTTP port
I can add the following to application.properties
:
quarkus.http.port=9090
%staging.quarkus.http.port=9999
And then set the QUARKUS_PROFILE
environment variable to staging
to activate my profile.
The proper way to check the active profile programmatically is to use the Using |
使用属性表达式
Quarkus supports the use of property expressions in the application.properties
file.
These expressions are resolved when the property is read. So if your configuration property is a build time configuration property, the property expression will be resolved at build time. If your configuration property is overridable at runtime, the property expression will be resolved at runtime.
You can use property expressions both for the Quarkus configuration or for your own configuration properties.
Property expressions are defined this way: ${my-property-expression}
.
For example, having the following property:
remote.host=quarkus.io
and another property defined as:
callable.url=https://${remote.host}/
will result in the value of the callable.url
property being set to:
callable.url=https://quarkus.io/
Another example would be defining different database servers depending on the profile used:
%dev.quarkus.datasource.url=jdbc:mysql://localhost:3306/mydatabase?useSSL=false
quarkus.datasource.url=jdbc:mysql://remotehost:3306/mydatabase?useSSL=false
can be simplified by having:
%dev.application.server=localhost
application.server=remotehost
quarkus.datasource.url=jdbc:mysql://${application.server}:3306/mydatabase?useSSL=false
It does result in one more line in this example but the value of application.server
can be reused in other properties,
diminishing the possibility of typos and providing more flexibility in property definitions.
Combining Property Expressions and Environment Variables
Quarkus also supports the combination of both property expressions and environment variables.
Let’s assume you have following property defined in application.properties
:
remote.host=quarkus.io
You can combine environment variables and property expressions by having a property defined as follows:
application.host=${HOST:${remote.host}}
This will expand the HOST
environment variable and use the value of the property remote.host
as the default value if HOST
is not set.
For the purpose of this section we used the property remote.host
we defined previously.
It has to be noted that the value could have been a fixed one such as in:
application.host=${HOST:localhost}
which will result in localhost
as the default value if HOST
is not set.
清理配置项
Run time properties which are optional, and which have had values set at build time or which have a default value, may be explicitly cleared by assigning an empty string to the property. Note that this will only affect run time properties, and will only work with properties whose values are not required.
The property may be cleared by setting the corresponding application.properties
property, setting the
corresponding system property, or setting the corresponding environment variable.
其它
The default Quarkus application runtime profile is set to the profile used to build the application. For example:
./mvnw package -Pnative -Dquarkus.profile=prod-aws`
./target/my-app-1.0-runner (1)
1 | The command will run with the prod-aws profile. This can be overridden using the quarkus.profile system property. |
Custom Configuration
Custom configuration sources
You can also introduce custom configuration sources in the standard MicroProfile Config manner. To
do this, you must provide a class which implements either org.eclipse.microprofile.config.spi.ConfigSource
or org.eclipse.microprofile.config.spi.ConfigSourceProvider
. Create a
service file for the
class and it will be detected and installed at application startup.
Custom configuration converters
You can also use your custom types as a configuration values. This can be done by implementing org.eclipse.microprofile.config.spi.Converter<T>
and adding its fully qualified class name in the META-INF/services/org.eclipse.microprofile.config.spi.Converter
file.
Let us assume you have a custom type like this one:
package org.acme.config;
public class MicroProfileCustomValue {
private final int number;
public MicroProfileCustomValue(int number) {
this.number = number;
};
public int getNumber() {
return number;
}
}
The corresponding converter will look like the one below. Please note that your custom converter class must be public
and must have
a public
no-argument constructor. It also must not be abstract
.
package org.acme.config;
import org.eclipse.microprofile.config.spi.Converter;
public class MicroProfileCustomValueConverter implements Converter<MicroProfileCustomValue> {
@Override
public MicroProfileCustomValue convert(String value) {
return new MicroProfileCustomValue(Integer.valueOf(value));
}
}
Then you need to include the fully qualified class name of the converter in a service file META-INF/services/org.eclipse.microprofile.config.spi.Converter
.
If you have more converters, simply add their class names in this file as well. Single fully qualified class name per line, for example:
org.acme.config.MicroProfileCustomValueConverter
org.acme.config.SomeOtherConverter
org.acme.config.YetAnotherConverter
Please note that SomeOtherConverter
and YetAnotherConverter
were added just for a demonstration. If you include in this file classes
which are not available at runtime, the converters loading will fail.
After this is done you can use your custom type as a configuration value:
@ConfigProperty(name = "configuration.value.name")
MicroProfileCustomValue value;
Converter priority
In some cases, you may want to use a custom converter to convert a type which is already converted
by a different converter. In such cases, you can use the javax.annotation.Priority
annotation to
change converters precedence and make your custom converter of higher priority than the other
on the list.
By default, if no @Priority
can be found on a converter, it’s registered with a priority of 100
and all Quarkus core converters are registered with a priority of 200, so depending on which
converter you would like to replace, you need to set a higher value.
To demonstrate the idea let us implement a custom converter which will take precedence over
MicroProfileCustomValueConverter
implemented in the previous example.
package org.acme.config;
import javax.annotation.Priority;
import org.eclipse.microprofile.config.spi.Converter;
@Priority(150)
public class MyCustomConverter implements Converter<MicroProfileCustomValue> {
@Override
public MicroProfileCustomValue convert(String value) {
final int secretNumber;
if (value.startsFrom("OBF:")) {
secretNumber = Integer.valueOf(SecretDecoder.decode(value));
} else {
secretNumber = Integer.valueOf(value);
}
return new MicroProfileCustomValue(secretNumber);
}
}
Since it converts the same value type (namely MicroProfileCustomValue
) and has a priority
of 150, it will be used instead of a MicroProfileCustomValueConverter
which has a default
priority of 100.
This new converter also needs to be listed in a service file, i.e. META-INF/services/org.eclipse.microprofile.config.spi.Converter .
|
YAML for Configuration
Add YAML Config Support
You might want to use YAML over properties for configuration. Since SmallRye Config brings support for YAML configuration, Quarkus supports this as well.
First you will need to add the YAML extension to your pom.xml
:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-config-yaml</artifactId>
</dependency>
Or you can alternatively run this command in the directory containing your Quarkus project:
./mvnw quarkus:add-extension -Dextensions="config-yaml"
Now Quarkus can read YAML configuration files. The config directories and priorities are the same as before.
Quarkus will choose an application.yaml over an application.properties .
YAML files are just an alternative way to configure your application.
You should decide and keep one configuration type to avoid errors.
|
Configuration Examples
# YAML supports comments
quarkus:
datasource:
url: jdbc:postgresql://localhost:5432/some-database
driver: org.postgresql.Driver
username: quarkus
password: quarkus
# REST Client configuration property
org:
acme:
restclient:
CountriesService/mp-rest/url: https://restcountries.eu/rest
# For configuration property names that use quotes, do not split the string inside the quotes.
quarkus:
log:
category:
"io.quarkus.category":
level: INFO
Quarkus also supports using |
Profile dependent configuration
Providing profile dependent configuration with YAML is done like with properties.
Just add the %profile
wrapped in quotation marks before defining the key-value pairs:
"%dev":
quarkus:
datasource:
url: jdbc:postgresql://localhost:5432/some-database
driver: org.postgresql.Driver
username: quarkus
password: quarkus
Configuration key conflicts
The MicroProfile Configuration specification defines configuration keys as an arbitrary .
-delimited string.
However, structured formats like YAML naively only support a subset of the possible configuration namespace.
For example, consider the two configuration properties quarkus.http.cors
and quarkus.http.cors.methods
.
One property is the prefix of another, so it may not be immediately evident how to specify both keys in your YAML configuration.
This is solved by using a null key (normally represented by ~
) for any YAML property which is a prefix of another one. Here’s an example:
quarkus:
http:
cors:
~: true
methods: GET,PUT,POST
In general, null YAML keys are not included in assembly of the configuration property name, allowing them to be used to any level for disambiguating configuration keys.
More info on how to configure
Quarkus relies on Eclipse MicroProfile and inherits its features.
There are converters that convert your property file content from String
to typed Java types. See the list in the specification.