quarkus实战之七:使用配置
欢迎访问我的GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览
- 本文是《quarkus实战》系列的第七篇,前文讲述了如何在将配置信息传入quarkus应用,今天要练习的是如何使用这些配置信息
- 整篇文章由以下内容构成:
- 创建工程,作为演示使用配置项操作的代码
- 演示最基本的使用配置项操作
- 展示配置项不存时会导致什么问题
- 演示如何设置默认值,这样配置项不存在也不会出错
- 默认值是字符串,而实际的变量可以是多种类型,它们之间的关系
- Optional类型的配置注入
- 不用注解注入,也可以写代码获取配置
- 针对相同前缀的配置项,使用配置接口简化代码
- 使用配置接口嵌套,简化多级的相同前缀配置项
- 用map接受配置信息(减少配置项相关代码量)
- quarkus及其扩展组件的内置配置项
- 接下来从创建demo工程开始吧
演示代码
- 创建一个demo工程,参考下面的命令,这样的工程会自带一个web服务类HobbyResource.java
mvn "io.quarkus:quarkus-maven-plugin:create" \
-DprojectGroupId="com.bolingcavalry" \
-DprojectArtifactId="hello-quarkus" \
-DprojectVersion="1.0-SNAPSHOT" \
-DclassName="HobbyResource" \
-Dpath="actions"
最基本的配置
- 先来看看最常用最基本的配置文件使用方式
- 打开文件src/main/resources/application.properties,增加下面这行配置
greeting.message = hello from application.properties
- 打开HobbyResource.java,增加如下成员变量,使用了注解ConfigProperty
@ConfigProperty(name = "greeting.message")
String message;
- 以上就是最简单的使用配置项的方式,程序运行后,application.properties中greeting.message的值就会被quarkus框架注入到message成员变量中
配置项不存在导致的异常
- 如果ConfigProperty注解的配置项在配置文件中不存在,应用启动会报错,来看看是什么错误
- 将HobbyResource.java的代码改成下面这样,成员变量notExistsConfig的配置项是not.exists.config,这个配置项在配置文件中并不存在
@Path("/actions")
public class HobbyResource {
// 配置文件中不存在名为not.exists.config的配置项
@ConfigProperty(name = "not.exists.config")
String notExistsConfig;
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello RESTEasy, " + LocalDateTime.now() + ", [" + notExistsConfig + "]";
}
}
- 启动应用时报错如下图所示,红框中提示加载配置项失败

带默认值的配置
- 对于上面演示的配置项不存在导致启动失败问题,可以给ConfigProperty注解设置默认值,这样一旦找不到配置项,就使用默认值注入,可以避免启动失败了
- HobbyResource.java的源码如下,成员变量notExistsConfig的注解了增加属性defaultValue
@Path("/actions")
public class HobbyResource {
// 配置文件中不存在名为not.exists.config的配置项
@ConfigProperty(name = "not.exists.config", defaultValue = "112233")
String notExistsConfig;
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello RESTEasy, " + LocalDateTime.now() + ", [" + notExistsConfig + "]";
}
}
- 再次启动应用,这次不报错了,浏览器访问结果如下图,defaultValue已经生效

defaultValue属性的自动转换
- 对于ConfigProperty注解的defaultValue属性还有一点要注意,来看ConfigProperty的源码,如下图,红框显示defaultValue的类型是String

- 上图中,defaultValue的注释有说明:如果ConfigProperty注解修饰的变量并非String型,那么defaultValue的字符串就会被自动quarkus字符转换
- 例如修饰的变量是int型,那么defaultValue的String类型的值会被转为int型再赋给变量,如下所示,notExistsConfig是int型,defaultValue的字符串可以被转为int:
// 配置文件中不存在名为not.exists.config的配置项
@ConfigProperty(name = "not.exists.config", defaultValue = "123")
int notExistsConfig;
- 如果把上面代码中的defaultValue的值从123改为xxx,此时应用启动就会失败,因为“xxx”转为int的过程中抛出了异常,如下图:

- 除了上面试过的int,还有很多种类型都支持从defaultValue的字符串值被自动转换,它们是:
- 基础类型:如boolean, byte, short
- 装箱类型:如java.lang.Boolean, java.lang.Byte, java.lang.Short
- Optional类型:java.util.Optional, java.util.OptionalInt, java.util.OptionalLong, and java.util.OptionalDouble
- java枚举
- java.time.Duration
- JDK网络对象:如java.net.SocketAddress, java.net.InetAddress
- 例如,下面是字符串自动转InetAddress的例子,可以正常运行:
@ConfigProperty(name = "server.address", defaultValue = "192.168.1.1")
InetAddress serverAddress;
- 如果ConfigProperty修饰的变量是boolean型,或者Boolean型,则defaultValue值的自动转换逻辑有些特别: "true", "1", "YES", "Y" "ON"这些都会被转为true(而且不区分大小写,"on"也被转为true),其他值会被转为false
- 还有一处要注意的:defaultValue的值如果是空字符串,就相当于没有设置defaultValue,此时如果在配置文件中没有该配置项,启动应用会报错
支持Optional
- 支持Optional这个特性很赞,首先Optional类型的成员变量可直接用于函数式编程,其次配置项不存在时又能避免启动失败
- 接下来试试用ConfigProperty注解修饰Optional类型的成员变量
- HobbyResource.java的源码如下,optionalMessage是Optional类型的成员变量,配置项optional.message就算不存在,应用也能正常启动,并且optionalMessage直接用于函数式编程中(optionalMessage.ifPresent)
@Path("/actions")
public class HobbyResource {
// 配置文件中存在名为greeting.message的配置项
@ConfigProperty(name = "greeting.message")
String message;
// 配置文件中,不论是否存在名为optional.message的配置项,应用都不会抛出异常
@ConfigProperty(name = "optional.message")
Optional<String> optionalMessage;
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
List<String> list = new ArrayList<>();
list.add(message);
// 只有配置项optional.message存在的时候,才会执行list.add方法
optionalMessage.ifPresent(list::add);
return "Hello RESTEasy, " + LocalDateTime.now() + ", " + list;
}
}
- 先看配置项optional.message存在的情况,如下图红框所示,optional.message在配置文件中是个正常的配置项

- 启动应用,浏览器访问web接口,如下图,optional info是配置项optional.message的值

- 现在将optional info从文件application.properties中删除,重启应用,再次访问浏览器,如下图,应用依然正常响应,list中只有成员变量message的内容:

编码获取配置项
- 除了用ConfigProperty注解来获取配置项的值,还可以用写代码的方式获取
- 下面的代码展示了通过API获取配置项的操作,请注意代码中的注释
@Path("/actions")
public class HobbyResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
List<String> list = new ArrayList<>();
// 可以用静态方法取得Config实例
Config config = ConfigProvider.getConfig();
// getValue可取得指定配置项的指定类型值
String greet = config.getValue("greeting.message", String.class);
list.add(greet);
// getOptionalValue可以将配置项的值包状为Optional对象,如果配置项不存在,也不会报错
Optional<String> optional = config.getOptionalValue("not.exists.config", String.class);
// 函数式编程:只用optional中有对象时,才会执行list.add方法
optional.ifPresent(list::add);
return "Hello RESTEasy, " + LocalDateTime.now() + ", " + list;
}
}
- 当配置项not.exists.config不存在时,页面响应如下,只有greeting.message配置项的值:

- 配置项not.exists.config=123456时,页面响应如下,两个配置项的值都能成功获取:

- 另外,官方建议不要使用System.getProperty(String) 和 System.getEnv(String)去获取配置项了,它们并非quarkus的API,因此quarkus配置相关的功能与它们并无关系(例如感知配置变化、自动转换类型等)
配置接口
- 假设配置项如下,都是相同的前缀student
student.name=Tom
student.age=11
student.description=He is a good boy
- 针对上述配置项,可以用注解ConfigMapping将这些它们集中在一个接口类中获取,接口类StudentConfiguration.java如下
package com.bolingcavalry;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;
import io.smallrye.config.WithName;
@ConfigMapping(prefix = "student")
public interface StudentConfiguration {
/**
* 名字与配置项一致
* @return
*/
String name();
/**
* 名字与配置项一致,自动转为int型
* @return
*/
int age();
/**
* 名字与配置项不一致时,用WithName注解指定配置项
* @return
*/
@WithName("description")
String desc();
/**
* 用WithDefault注解设置默认值,如果配置项"student.favorite"不存在,则默认值生效
* @return
*/
@WithDefault("default from code")
String favorite();
}
- 从上述代码可见,一个接口即可完成所有配置项的注入,在使用这些配置项的时候,只要注入StudentConfiguration实例即可
- 首先要用ConfigMapping指明配置项的前缀,该接口中的方法都对应具有此前缀的配置项
- 一般情况下,方法名就等于配置项的名称,也可以用WithName指定配置项名称
- 用WithDefault指定默认值,如果找不到配置项就用此默认值
- 来看看如何使用这个配置接口,web服务代码如下,只要依赖注入StudentConfiguration即可,不在需要为每个配置项都用成员变量和ConfigProperty注解了
package com.bolingcavalry;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.time.LocalDateTime;
@Path("/actions")
public class HobbyResource {
@Inject
StudentConfiguration student;
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello RESTEasy, "
+ LocalDateTime.now()
+ " [" + student.name() + "], "
+ " [" + student.age() + "], "
+ " [" + student.desc() + "], "
+ " [" + student.favorite() + "]";
}
}
- 运行代码,用浏览器访问web接口试试,如下图,所有配置项都能正确获取

配置项是多个单词时,如何对应配置接口的方法?
- 回顾刚才的配置,name、age、description这些都是单个单词,现在如果有个配置项是多个单词,例如学号的英文是student number,应该如何转为StudentConfiguration接口的方法呢?
- 首先要看您的匹配项的命名风格,对多个单词是如何分隔的,一般有这三种:
- 减号分隔:student-number
- 下划线分隔:student_number
- 驼峰命名:studentNumber
- ConfigMapping注解提供了namingStrategy的属性,其值有三种,分别对应上述三种命名风格,您根据自身情况选用即可
- KEBAB_CASE(默认值):减号分隔的配置项转为驼峰命令的方法,配置项student-number对应的方法是studentNumber
- SNAKE_CASE:下划线分隔的配置项转为驼峰命令的方法,配置项student_number对应的方法是studentNumber
- VERBATIM:完全对应,不做任何转换,配置项student_number对应的方法是student_number
- 使用namingStrategy属性的示例代码如下
@ConfigMapping(prefix = "student", namingStrategy = ConfigMapping.NamingStrategy.SNAKE_CASE)
public interface StudentConfiguration {
/**
* 名字与配置项一致
* @return
*/
String name();
...
配置接口嵌套
- 再来看下面的配置,有两个配置项的前缀都是student.address,给人的感觉像是student对象里面有个成员变量是address类型的,而address有两个字段:province和city
student.name=Tom
student.age=11
student.description=He is a good boy
student.address.province=guangdong
student.address.city=shenzhen
- 针对上述配置,quarkus支持用接口嵌套来导入,具体做法分为两步,首先新增一个接口Address.java,源码如下
package com.bolingcavalry;
public interface Address {
String province();
String city();
}
- 第二步,在配置接口StudentConfiguration.java中,增加下图红框中的一行代码(接口中返回接口,形成接口嵌套)

- 最后,修改HobbyResource.java代码,增加下图红框中的两行,验证能否正常取得address前缀的配置项目

- 重启应用,如下图,配置项可以正常获取

配置项转为map
- 前面的接口嵌套,虽然将多层级的配置以对象的形式清晰的表达出来,但也引出一个问题:配置越多,接口定义或者接口方法就越多,代码随之增加
- 如果配置项的层级简单,还有种简单的方式将其映射到配置接口中:转为map
- 依然以下面的配置项为例
student.address.province=guangdong
student.address.city=shenzhen
- 对应的代码改动如下图,只要把address方法的返回值从Address改为Map<String, String>即可,这样修改后,address层级下面再增加配置项,也不用修改配置项有关的代码了:

- 使用配置的业务代码也要改,如下图,改为从map中获取

- 部署运行验证,可以正常取值

内置配置项
quarkus有很多内置的配置项,例如web服务的端口quarkus.http.port就是其中一个,如果您熟悉SpringBoot的话,对这些内置配置项应该很好理解,数据库、消息、缓存,都有对应配置项
篇幅所限就不在此讲解quarkus内置的配置项了,您可以参考这份官方提供的配置项列表,里面有详细说明:https://quarkus.io/guides/all-config
上述文档中,有很多配置项带有加锁的图标,如下图红框所示,有这个图标的配置项,其值在应用构建的时候已经固定了,在应用运行期间始终保持只读状态

这种带有加锁图标的配置项的值,在应用运行期间真的不能改变了吗?其实还是有办法的,官方文档指明,如果业务的情况特殊,一定要变,就走热部署的途径,您可以参考《quarkus实战之四:远程热部署》
官方对开发者的建议:在开发quarkus应用的时候,不要使用quarkus作为配置项的前缀,因为目前quarkus框架及其插件们的配置项的前缀都是quarkus,应用开发应该避免和框架使用相同的配置项前缀,以免冲突
至此,咱们已经学习了如何在quarkus应用中使用配置项,接下来还会一起实践更多的quarkus基础知识,锁定《quarkus实战》专辑,欣宸不会辜负您的期待
欢迎关注博客园:程序员欣宸
quarkus实战之七:使用配置的更多相关文章
- [原创].NET 业务框架开发实战之七 业务层初步构想
原文:[原创].NET 业务框架开发实战之七 业务层初步构想 .NET 业务框架开发实战之七 业务层初步构想 前言:本篇主要讲述如何把DAL和BLL衔接起来. 本篇议题如下: 1. DAL ...
- Cordova - 使用Cordova开发iOS应用实战1(配置、开发第一个应用)
Cordova - 使用Cordova开发iOS应用实战1(配置.开发第一个应用) 现在比较流行使用 html5 开发移动应用,毕竟只要写一套html页面就可以适配各种移动设备,大大节省了跨平台应用的 ...
- iView 实战系列教程(21课时)_1.iView 实战教程之配置篇
1.iView 实战教程之配置篇 点击添加插件,. 选中后安装 全部导入还是按需导入. 2.是否需要自定义主题变量 3.多语言的设置. 这里我们全部选择为默认 然后点击继续. 启动项目 入口文件导入了 ...
- mongo 3.4分片集群系列之七:配置数据库管理
这个系列大致想跟大家分享以下篇章: 1.mongo 3.4分片集群系列之一:浅谈分片集群 2.mongo 3.4分片集群系列之二:搭建分片集群--哈希分片 3.mongo 3.4分片集群系列之三:搭建 ...
- nginx入门与实战 安装 启动 配置nginx Nginx状态信息(status)配置 正向代理 反向代理 nginx语法之location详解
nginx入门与实战 网站服务 想必我们大多数人都是通过访问网站而开始接触互联网的吧.我们平时访问的网站服务 就是 Web 网络服务,一般是指允许用户通过浏览器访问到互联网中各种资源的服务. Web ...
- Spark Tachyon实战应用(配置启动环境、运行spark和运行mapreduce)
Tachyon实战应用 配置及启动环境 修改spark-env.sh 启动HDFS 启动Tachyon Tachyon上运行Spark 添加core-site.xml 启动Spark集群 读取文件并保 ...
- docker实战 (3) 常规配置
本节会持续更新,在项目实战中遇到的docker配置都会更新进来 docker常用命令: docker 介绍: what: 是什么 why: 为什么用 how: 怎么用 docker 特点: 轻量级,可 ...
- 从壹开始 [ Ids4实战 ] 之七 ║ 客户端、服务端、授权中心全线打通
1.经过元旦两天的全力整改,终于在这新的一年,完成了我的布道生涯的第一个大步走 —— 那就是客户端(VUE).服务端(ASP.NET Core API).授权中心(IdentityServer4)的大 ...
- OpenFaaS实战之七:java11模板解析
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- kubebuilder实战之七:webhook
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
随机推荐
- node服务端
一,node起服务+数据交互+中间件 什么是node express koa node是js在后端运行时的一个环境 express,koa是基于node的框架,快速构建web应用 前后端交互方式 1. ...
- vue3.0
https://www.yuque.com/gdnnth/vue-v3 http://www.liulongbin.top:8085/#/ https://www.yuque.com/woniuppp ...
- SQL Server 2022 AlwaysOn新特性之包含可用性组介绍
由于技术能力有限,文章仅能进行简要分析和说明,如有不对的地方,请指正,谢谢. SQL Server的容灾功能一直弱于Oracle和MySQL,无法自动同步元数据(用户.登录名.权限.SQL 代理作业. ...
- 基于APM模式的异步实现及跨线程操作窗体或控件方法的实现示例
最近在一家某电力外派公司开发相关于GIS的功能,在实现代码的过程中出现了一些常见的问题比如: 1.跨线程执行窗体或控件操作(直接使用委拖) 2.异步模式执行某长时间耗时方法 经过一系列摸索可算找到解决 ...
- 2021-06-18:已知数组arr,生成一个数组out,out的每个元素必须大于等于1,当arr[cur]>arr[cur-1]时,out[cur]>out[cur-1];当arr[cur]>arr
2021-06-18:已知数组arr,生成一个数组out,out的每个元素必须大于等于1,当arr[cur]>arr[cur-1]时,out[cur]>out[cur-1]:当arr[cu ...
- 2021-09-11:给你一个32位的有符号整数x,返回将x中的数字部分反转后的结果。反转后整数超过 32 位的有符号整数的范围就返回0,假设环境不允许存储 64 位整数(有符号或无符号)。
2021-09-11:给你一个32位的有符号整数x,返回将x中的数字部分反转后的结果.反转后整数超过 32 位的有符号整数的范围就返回0,假设环境不允许存储 64 位整数(有符号或无符号). 福大大 ...
- Flutter编写的数独游戏
一个使用Flutter编写的每日数独小游戏,支持Android和ios.代码已上传到github:https://github.com/huhx/flutter_sudoku Library 状态管理 ...
- CMD 常用命令总结
CMD 常用命令总结 小技巧: 输入 help,查看帮助: Tab 键,自动补全: 上/下方向键,查看历史命令: 右键窗口标题栏 -> 属性,可以修改外观样式. # 关机.重启.注销.休眠.定时 ...
- django:有关移除数据库出错问题
执行: 最终,通过执行迁移文件,我们将Django项目中创建的模型转化为MySql中的数据表. 执行迁移文件的两条命令: python manage.py makemigrations python ...
- Docker安装MS SQL Server并使用Navicat远程连接
MS SQL Server简介 Microsoft SQL Server(简称SQL Server)是由微软公司开发的关系数据库管理系统,它是一个功能强大.性能卓越的企业级数据库平台,用于存储和处理大 ...