springboot 获取application参数_LOOK ! SpringBoot的外部化配置最全解析

本篇要点

  • 介绍各种配置方式的优先级。
  • 介绍各种外部化配置方式。
  • 介绍yaml的格式及原理。
  • 介绍如何绑定并测试类型安全的属性配置。
  • 介绍@ConfigurationProperties与@Value的区别。

一、SpringBoot官方文档对于外部化配置的介绍及作用顺序

SpringBoot支持多种外部化配置,以便于开发者能够在不同的环境下,使用同一套应用程序代码。外部化配置的方式有多种:properties文件,yaml文件,Environment变量已经命令行参数等等。

外部化配置的属性值可以通过@Value注解自动注入,亦可以通过Spring的Environment抽象访问,也可以通过@ConfigurationProperties注解绑定到结构化对象上。

SpringBoot支持很多种的外部化配置,待会我们会介绍到。在这之前,我们必须要知道如果多种配置同时出现,一定是按照特定的顺序生效的。规则如下:

  1. devtool处于active状态时, $HOME/.config/spring-boot 目录中的Devtool全局配置。
  2. 测试中的@TestPropertySource注解。
  3. 测试中的@SpringBootTest#properties注解特性。
  4. 命令行参数。
  5. SPRING_APPLICATION_JSON 中的属性(环境变量或系统属性中的内联JSON嵌入)。
  6. ServletConfig 初始化参数。
  7. ServletContext 初始化参数。
  8. java:comp/env 里的JNDI属性
  9. JVM系统属性 System.getProperties() 。
  10. 操作系统环境变量
  11. 仅具有 random.* 属性的 RandomValuePropertySource 。
  12. 应用程序以外的application-{profile}.properties或者application-{profile}.yml文件
  13. 打包在应用程序内的application-{profile}.properties或者application-{profile}.yml文件
  14. 应用程序以外的application.properties或者appliaction.yml文件
  15. 打包在应用程序内的application.properties或者appliaction.yml文件
  16. @Configuration类上的@PropertySource注解,需要注意,在ApplicationContext刷新之前,是不会将这个类中的属性加到环境中的,像 logging.*,spring.main.* 之类的属性,在这里配置为时已晚。
  17. 默认属性(通过 SpringApplication.setDefaultProperties 指定).

这里列表按组优先级排序,也就是说, 任何在高优先级属性源里设置的属性都会覆盖低优先级的相同属性 ,列如我们上面提到的命令行属性就覆盖了application.properties的属性。

举个例子吧:

如果在application.properties中设置 name=天乔巴夏 ,此时我用命令行设置 java -jar hyh.jar --author.name=summerday ,最终的name值将会是summerday,因为命令行属性优先级更高。

二、各种外部化配置举例

1、随机值配置

配置文件中 ${random} 可以用来生成各种不同类型的随机值,从而简化了代码生成的麻烦,例如 生成 int 值、long 值或者 string 字符串。原理在于, RandomValuePropertySource 类重写了 getProperty 方法,判断以 random. 为前缀之后,进行了适当的处理。

my.secret=${random.value}my.number=${random.int}my.bignumber=${random.long}my.uuid=${random.uuid}my.lessThanTen=${random.int(10)}my.inRange=${random.int[1024,65536]}

2、命令行参数配置

默认情况下,SpringApplication将所有的命令行选项参数【以 -- 开头的参数,如 --server.port=9000 】转换为属性,并将它们加入SpringEnvironment中,命令行属性的配置始终优先于其他的属性配置。

如果你不希望将命令行属性添加到Environment中,可以使用 SpringApplication.setAddCommandLineProperties(false) 禁用它。

$ java -jar app.jar --debug=true #开启debug模式,这个在application.properties文件中定义debug=true是一样的

3、属性文件配置

属性文件配置这一部分是我们比较熟悉的了,我们在快速创建SpringBoot项目的时候,默认会在resources目录下生成一个application.properties文件。SpringApplication都会从配置文件加载配置的属性,并最终加入到Spring的Environment中。除了resources目录下,还有其他路径,SpringBoot默认是支持存放配置文件的。

/config/config

以上四个,优先级从上往下依次降低,也就是说,如果同时出现,上面配置的属性将会覆盖下面的。

关于配置文件,properties和yaml文件都能够满足配置的需求。

当然,这些配置都是灵活的,如果你不喜欢默认的配置文件命名或者默认的路径,你都可以进行配置:

$ java -jar myproject.jar --spring.config.name=myproject$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

4、指定profile属性

通常情况下,我们开发的应用程序需要部署到不同的环境下,属性的配置自然也需要不同。如果每次在发布的时候替换配置文件,过于麻烦。SpringBoot的多环境配置为此提供了便利。具体做法如下:

我们之前在介绍各种配置的优先级的时候说过, application-{profile}.properties或者application-{profile}.yml文件 的优先级高于 application.properties或application.yml 配置,这里的profile就是我们定义的环境标识:

我们在resource目录下创建三个文件:

  • application.properties:默认的配置,default。
  • application-dev.properties:开发环境,dev。
  • application-prod.properties:生产环境,prod。

我们可以通过指定 spring.profiles.active 属性来激活对应的配置环境:

spring.profiles.active=dev

或使用命令行参数的配置形式:

$ java -jar hyh.jar --spring.profiles.active=dev

如果没有profile指定的文件于profile指定的文件的配置属性同时定义,那么指定profile的配置优先。

5、使用占位符

在使用application.properties中的值的时候,他们会从Environment中获取值,那就意味着,可以引用之前定义过的值,比如引用系统属性。具体做法如下:

name=天乔巴夏description=${name} is my name

6、加密属性

Spring Boot不提供对加密属性值的任何内置支持,但是,它提供了 修改Spring环境中的值 所必需的挂钩点。我们可以通过实现EnvironmentPostProcessor接口在应用程序启动之前操纵Environment。

可以参考 howto.html ,查看具体使用方法。

7、使用YAML代替properties

YAML是JSON的超集,是一种 指定层次结构配置数据的便捷格式 ,我们以properties文件对比一下就知道了:

#propertiesspring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8spring.datasource.username=rootspring.datasource.password=123456my.servers[0]=www.hyh.commy.servers[1]=www.yhy.com
# ymlspring:  datasource:    driver-class-name: com.mysql.cj.jdbc.Driver    url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8    username: root    password: 123456my:  server:    - www.hyh.com    - www.yhy.com

只要在类路径上具有SnakeYAML库,SpringApplication类就会自动支持YAML作为属性配置的方式。SpringBoot项目中的 spring-boot-starter 已经提供了相关类库: org.yaml.snakeyaml,因此SpringBoot天然支持这种方式配置。

关于yaml文件的格式,可以参考官方文档: Using YAML Instead of Properties

8、类型安全的属性配置

上面说到通过 @Value("${property}") 注解来注入配置有时会比较麻烦,特别是当多个属性本质上具有层次结构的时候。SpringBoot提供了一种解决方案: 让强类型的bean管理和验证你的配置

直接来看具体的使用叭:

@ConfigurationPropertie定义一个绑定配置的JavaBean

  1. 使用默认构造器+getter和setter注入
@ConfigurationProperties("acme")public class AcmeProperties {    private boolean enabled; //acme.enabled  默认为false    private InetAddress remoteAddress;// acme.remote-address  可以从String转换而来的类型    private final Security security = new Security();//.. 省略getter和setter方法    public static class Security {        private String username; // acme.security.username        private String password; // acme.security.password        private List roles = new ArrayList<>(Collections.singleton("USER"));// acme.security.roles//.. 省略getter setter方法    }}

这种方式依赖于默认的空构造函数,通过getter和setter方法赋值,因此getter和setter方法是必要的,且不支持静态属性的绑定。

如果嵌套pojo属性已经被初始化值: private final Security security = new Security(); 可以不需要setter方法。如果希望绑定器使用其默认构造函数动态创建实例,则需要setter。

  1. 通过@ContructorBinding注解使用构造器绑定的方式:
@ConstructorBinding //标注使用构造器绑定@ConfigurationProperties("acme")public class AcmeProperties {    private final Security security;    private final boolean enabled;    private final InetAddress remoteAddress;    public AcmeProperties(boolean enabled, InetAddress remoteAddress, Security security) {        this.enabled = enabled;        this.remoteAddress = remoteAddress;        this.security = security;    }    //..省略getter方法    @ToString    public static class Security {        private final String username;        private final String password;        private final List roles;        public Security(String username, String password,                        @DefaultValue("USER") List roles) {            this.username = username;            this.password = password;            this.roles = roles;        }    }    //..省略getter方法}

如果没有配置Security实例属性,那么最后结果:Security=null。如果我们想让Security={username=null,password=null,roles=[USER]},可以在Security上加上@DefaultValue。 public AcmeProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security)

通过@EnableConfigurationProperties注册

已经定义好了JavaBean,并与配置属性绑定完成,接着需要注册这些bean。我们通常用的@Component或@Bean,@Import加载bean的方式在这里是不可取的,SpringBoot提供了解决方案: 使用@EnableConfigurationProperties ,我们既可以一一指定配置的类,也可以按照组件扫描的方式进行配置。

@SpringBootApplication@EnableConfigurationProperties({HyhConfigurationProperties.class, MyProperties.class,AcmeProperties.class})public class SpringBootProfileApplication {}
@SpringBootApplication@ConfigurationPropertiesScan({"com.hyh.config"})public class SpringBootProfileApplication {}

配置yaml文件

acme:  remote-address: 192.168.1.1  security:    username: admin    roles:      - USER      - ADMIN

注入properties,测试

@Configurationpublic class Application implements CommandLineRunner {    @Autowired    private AcmeProperties acmeProperties;    @Override    public void run(String... args) throws Exception {        System.out.println(acmeProperties);    }}
//输出: AcmeProperties(security=AcmeProperties.Security(username=admin, password=null, roles=[USER, ADMIN]), enabled=false, remoteAddress=/192.168.1.1)

宽松绑定

SpringBoot采用宽松的规则进行Environment和@ConfigurationProperties标注bean的匹配。如:

@ConfigurationProperties(prefix="acme.my-project.person")public class OwnerProperties {    private String firstName;    public String getFirstName() {        return this.firstName;    }    public void setFirstName(String firstName) {        this.firstName = firstName;    }}

下面表格中的属性名都可以匹配:

a8f0185f12fcb01ceeb1aae9c05c1388.png

@ConfigurationProperties注解中的prefix值必须是kebab case形式的,以 - 为分割符。

Spring官方建议,属性尽可能以lower-case kebab的形式:my.property-name=acme

Map如何绑定

绑定到Map属性时,如果key包含 小写字母数字字符或-以外的任何其他字符 ,则需要使用方括号包围key,以便保留原始值。 如果键没有被 [] 包围,则所有非字母数字或-的字符都将被删除。如下:

hyh:  username: 天乔巴夏  password: 123456  map:    "[/key1]": value1 #用引号包围[],用[]包围key    /key3: value3    key-4: value4    key/5: value5# 结果:"map":{/key1=value1,key5=value5, key-4=value4, key3=value3}

环境变量如何绑定

遵循三条原则:

  1. 把 . 换成下划线 _ 。
  2. 移除 - 。
  3. 小写转大写。

如: spring.main.log-startup-info 转为: SPRING_MAIN_LOGSTARTUPINFO , my.acme[0].other 转为 MY_ACME_0_OTHER 。

9、复杂类型

之前介绍yml文件,介绍了单纯的数组形式或值的绑定,SpringBoot还支持复杂类型的绑定。

merge:  list:    - name: 天乔巴夏      desc: 帅啊    - name: tqbx      desc: 很帅啊  map:    key1:      name: summerday      desc: handsome!    key2:      name: summer
@ToString@ConfigurationProperties(prefix = "merge")public class MergeProperties {    private final List list = new ArrayList<>();    private final Map map = new HashMap<>();    public List getList() {        return list;    }    public Map getMap() {        return map;    }}

最后输出:

MergeProperties(    list=[User(name=天乔巴夏, desc=帅啊),           User(name=tqbx, desc=很帅啊)],     map={key1=User(name=summerday, desc=handsome!),          key2=User(name=summer, desc=null)}a)

10、参数校验

对@ConfigurationProperties类使用Spring的 @Valid 注解时,Spring Boot就会尝试对其进行验证。

你可以直接在配置类上使用JSR-303 javax.validation 约束注解。这个做法的前提是,你的类路径上有兼容的JSR-303实现:

org.hibernate            hibernate-validator            6.0.18.Final

然后将约束注解加到字段上,如下:

@Data@Validated@ConfigurationProperties(prefix = "validate")public class ValidateProperties {    @NotNull    private String name;    @Valid    private final SubProperties subProperties = new SubProperties();    @Data    public static class SubProperties {        @Min(value = 10,message = "年龄最小为10")        public Integer age;    }}

配置如下:

validate:  name: hyh  sub-properties:    age: 5

结果如下:

Description:Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'validate' to com.hyh.config.ValidateProperties failed:    Property: validate.sub-properties.age    Value: 5    Origin: class path resource [application.yml]:47:10    Reason: 年龄最小为10        Action:Update your application's configuration

三、@ConfigurationProperties与@Value的区别

@Value注解是一个核心容器功能,它没有提供和type-safe配置属性相关的功能,下面这个表格总结了两者分别支持的功能:

96032258c3ee0e078d333e93e399be51.png

官方建议:

  • 如果你为自己的组件定义了一套配置,建议使用@ConfigurationProperties和POJO绑定,这样做能够提供结构化且类型安全的对象。
  • 如果硬要使用@Value,建议使用kebab-case形式,如@Value(" ${demo.item-price}")

来源:https://www.tuicool.com/articles/uQFFBzz

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/245583.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spark入门_入门必读 | Spark 论文导读

Resilient Distributed Datasets: A fault-tolerant abstraction for in-Memory cluster computing&#xff0c; 是讲述 Spark RDD 的基础论文&#xff0c;通读论文能给我们带来全景的 Spark 知识面摘要&#xff1a;RDD,全称Resilient Distributed Dataset,可伸缩性数据集。使用…

Qt在linux下无法输入中文,Ubuntu使用集成开发环境QT无法输入中文的解决方法

QT Creator是轻量级集成开发环境&#xff0c;在Ubuntu系统操作中&#xff0c;使用QT时无法输入中文&#xff0c;遇到这种情况要如何处理呢&#xff1f;下面小编就给大家介绍下Ubuntu如何解决QT无法输入中文问题。1 安装搜狗输入法&#xff0c;(如果你想用ubuntu自带的输入法也没…

altium pcb 信号高亮_在PCB设计中高效的放置元件技巧

在印刷电路板设计中&#xff0c;设置电路板轮廓后&#xff0c;将零件(占地面积)调用到工作区。然后将零件重新放置到正确的位置&#xff0c;并在完成后进行接线。组件放置是这项工作的第一步&#xff0c;对于之后的平滑布线工作是非常重要的工作。如果在接线工作期间模块不足&a…

linux升级ssh到6.6版本,CentOS6.5 openssh升级到openssh-7.6版本

CentOS6.5 openssh升级到openssh-7.6版本2018-8-3 foooy升级前保证故障后能现场处理&#xff0c;或者远程卡处理&#xff0c;否则不要这样直接升级yum install -y gcc openssl-devel pam-devel rpm-buildwget http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-7.6…

python中csv文件通过什么表示字符_python_写入csv文件时候无法进行原样写入(写入字符串中出现逗号,时候,csv文件自动分成两个单元格)...

问题描述&#xff1a; 写入csv文件时候无法进行原样写入(写入字符串中出现逗号","时候&#xff0c;csv文件自动分成两个单元格) with open("test.csv","w") as f: f.write("闲暇时&#xff0c;我会被一段&#xff0c;配乐诗朗诵,所感动,悲伤…

linux爬365租房没有数据,新手求助,LINUX下安装11G不能MOUNT数据库

各位&#xff0c;我是新手&#xff0c;请帮我看一下这样的情况该如何处理&#xff0c;谢谢啦。[rootlocalhost ~]# su - oracle[oraclelocalhost ~]$ sqlplus /nologSQL*Plus: Release 11.2.0.1.0 Production on Tue Mar 23 16:04:25 2010Copyright (c) 1982, 2009, Oracle. A…

rabbitmq怎样确认是否已经消费了消息_阿里Java研发二面:了解RabbitMQ?说说RabbitMQ可靠性投递...

上期写到高并发下RabbitMq消息中间件你应该介么玩今天给小伙伴说说&#xff01;有自己看法的也可以在评论区留言探讨&#xff0c;也可以转发关注下我以后会长期分享&#xff01;目录&#xff1a;确保消息发送到RabbitMQ服务器确保消息被正确的路由确保消息在队列正确地存储确保…

linux 装完yum不能用,【linux】yum 不能安装应用,提示There are no enabled repos Run “yum repolist all”...

回答一般来说著名的linux系统基本上分两大类&#xff1a;1 RedHat系列&#xff1a;Redhat、Centos、Fedora等2 Debian系列&#xff1a;Debian、Ubuntu等RedHat 系列&#xff1a;1 常见的安装包格式 rpm 包&#xff0c;安装rpm包的命令是 “rpm -参数”2 包管理工具 yum3 支持ta…

更新fielddata为true_线程与更新UI,细谈原理

前言 相信不少读者都阅读过相类似的文章了&#xff0c;但是我还是想完整的把这之间的关系梳理清楚&#xff0c;细节聊好&#xff0c;希望你也能从中学到一些。进入正题&#xff0c;大家应该都听过这样一句话——“UI更新要在主线程&#xff0c;子线程更新UI会崩溃”。久而久之就…

linux sublime3 插件安装插件,手动安装sublimeText3插件

就在今天下午&#xff0c;我花了一个小时的时间安装sublime3插件stylus&#xff0c;就是为了让stylus文件能够高亮显示。网上找了很多方法&#xff0c;可以通过package control安装&#xff0c;然而&#xff0c;我的sublime package control能够正常显示&#xff0c;插件列表也…

vbs打开软件光标停在第一个输入框_三维设计软件,3DMAX最全快捷键大全,赶快收藏哦...

文章后有获取软件的方式。基本快捷键A-角度捕捉开关 B-切换到底视图C-切换到摄象机视图D-封闭视窗E-切换到轨迹视图F-切换到前视图G-切换到网格视图H-显示通过名称选择对话框I-交互式平移J-选择框显示切换K-切换到背视图L-切换到左视图M-材质编辑器N-动画模式开关O-自适应退化开…

linux通过延长器改变分辨率,HDMI延长器的优势及常见问题(转载)(转载)

HDMI延长器普遍用于一切需要进行HDMI信号调配和组合的领域&#xff0c;比如&#xff1a;军工、多媒体教学、电视电话会议、大屏幕显示、会展、金融、科研,天气、超市、会议系统等领域。以达到确保信号长间隔无衰减的传输。HDMI延长器的优势1、HDMI单网线延长器&#xff0c; 直接…

mfc读取txt文件并显示_Python入门丨文件读写

文件读写文件读写&#xff0c;是Python代码调用调用电脑文件的主要功能&#xff0c;能被用于读取和写入文本记录、音频片段、Excel文档、保存邮件以及任何保存在电脑上的东西。读取文件读取文件三个步骤&#xff1a;准备工作&#xff1a;首先在桌面新建了一个test文件夹&#x…

c语言最简单程序实例,C语言第一个简单实例

在信息化、智能化的世界里&#xff0c;可能很早很早 我们就听过许多IT类的名词&#xff0c;C语言也在其中&#xff0c;我们侃侃而谈&#xff0c;到底C程序是什么样子&#xff1f;让我们先看简单的一个例子:#include /*引入头文件*/int main(void) …

c语言 多个线程对同一变量执行memcpy_手把手带你实现线程池

执行与任务分离的组件— 线程池wangbojing/threadpool​github.com多线程技术主要解决了处理器单元内多个线程执行的问题&#xff0c;它可以显著的减少处理器单元的闲置时间&#xff0c;增加处理器单元的吞吐能力。线程池是多线程编程的一个必要组件&#xff0c;并且对于很多编…

android shape 自定义,Android自定义shape的使用

MainActivity如下:package cn.testshape;import android.os.Bundle;import android.app.Activity;/*** Demo描述:* 自定义shape的使用*/public class MainActivity extends Activity {Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstan…

python 爬虫库 beautifulsoup4_Python爬虫之BeautifulSoup4

The Dormouses storyOnce upon a time there were three little sisters; and their names were , Lacie and Tillie; and they lived at the bottom of a well.... """ # 创建 Beautiful Soup 对象&#xff0c;指定lxml解析器 soup BeautifulSoup(html, "…

python手势识别_Python|使用opencv进行简单的手势检测

简单的手势识别&#xff0c;基本思路是基于皮肤检测&#xff0c;皮肤的颜色在HSV颜色空间下与周围环境的区分度更高&#xff0c;从RGB转换到HSV颜色空间下针对皮肤颜色进行二值化&#xff0c;得到mask&#xff1a; defHSVBin(img):hsvcv2.cvtColor(img,cv2.COLOR_RGB2HSV) lowe…

android程序root权限,android – 如何从源代码授予对特定应用程序的root访问权限而不是root权限?...

我正在从源代码编译Android ROM,我有一个应用程序,我希望它预先安装并让它以root权限运行.如何在不支持整个ROM的情况下授予对此特定应用程序的root访问权限&#xff1f;解决方法:希望你不需要root …典型的股票Android ROM为很少的东西提供root权限,与principle of least priv…

android 弹出网格菜单,在android中的recyclerView中显示弹出按钮的确...

as per my above comment可以使用Popup MenuAndroid Popup Menu displays the menu below the anchor text if space is available otherwise above the anchor text. It disappears if you click outside the popup menu.试试这个创建菜单文件文件&#xff1a;poupup_menu.xml…