SpringBoot允许使用配置文件对应用程序进行配置,支持以下不同形式的配置源:

  1. 属性文件(比如application.properties)
  2. yaml文件(后缀可以是yml或者yaml)
  3. 环境变量
  4. 命令行参数

获取这些外部化属性有以下几种方式:

  1. 使用@Value注解
  2. 使用Spring的Environment抽象层进行访问
  3. 使用@ConfigurationProperties注解将一批特定属性绑定到java对象

配置源的加载顺序和优先级

由于先加载的配资源都有可能被后加载的配资源覆盖,因此认为后加载的配置源优先级更高,加载顺序如下:

  1. 默认属性(通过SpringApplication.setDefaultProperties()方法指定)
  2. 配置类(@Configuration修饰的类)上用@PropertySource注解加载的属性文件中的属性值
  3. 配置数据(如application.properties文件等)
  4. RandomValuePropertySource,只包含random.*中的属性
  5. 操作系统环境变量
  6. java系统属性(System的getProperties()方法返回的属性)
  7. 来自java:comp/env的jndi属性(只在Web服务器或应用服务器中有用)
  8. ServletContext的初始化参数(在Web.xml文件中通过<context-param.../>元素设置的初始化参数)(只在Web服务器或应用服务器中有用)
  9. ServletConfig的初始化参数(在Web.xml文件中通过<init-param.../>元素设置的初始化参数,或者通过@Servlet注解设置的初始化参数)
  10. 来自SPRING_APPLICATION_JSON的属性(嵌套在环境变量或系统属性中的JSON文本)
  11. 命令行参数
  12. 测试用例类上通过@SpringBootTest注解的properties所指定的属性(仅在单元测试生效)
  13. 测试用例类上用@TestPropertySource注解加载的属性文件中的属性值(仅在单元测试生效)
  14. 当SpringBoot的devtools工具处于激活状态时,用户Home目录中.config/spring-boot/子目录下spring-boot-devtools.properties或spring-boot-devtools.yml文件中设置的属性值(windows平台对应“C:\User\用户名”下的配置文件)

此外application.properties(包括application.yml)也有几个不同的来源,它们按如下顺序加载:

  1. jar包内的application.properties(或application.yml)
  2. jar包内的application-{profile}.properties(或application-{profile}.yml)
  3. jar包外临时指定的application.properties(或application.yml)
  4. jar包外临时指定的application-{profile}.properties(或application-{profile}.yml)

jar包外临时指定的配置文件优先级高于jar包内;特定的profile对应的配置文件优先级高于通用配置文件的优先级。

利用JSON参数配置

SpringBoot支持一个特殊的系统属性(环境变量):Spring.application.json,通过这个属性可传入一段json文本,而这段文本会被解析成配置属性来加载,加载顺序排在第10位(上一小节)。

@SpringBootApplication
public class App
{
public static void main(String[] args)
{
// 设置spring.application.json系统属性
// 属性值为{“fkjava”:{name:""SPringBoot服务器", age:20, servers:["fkjava.org", "crazyit.org"]}}
System.setProperty("spring.application.json",
"{\"fkjava\":{\"name\":\"SPringBoot服务器\", \"age\":20, " +
"\"servers\":[\"fkjava.org\", \"crazyit.org\"]}}");
SpringApplication.run(App.class, args);
}
}

application.properties文件内容:

fkjava.name=springboot软件服务器
fkjava.age=35
@RestController
public class HelloController {
// 使用@Value注解访问配置属性
@Value("${fkjava.name}")
private String name;
@Value("${fkjava.age}")
private String age;
@Value("${fkjava.servers[0]}")
private String server1;
@Value("${fkjava.servers[1]}")
private String server2;
@GetMapping
public Map<String, String> hello()
{
return Map.of("名称", name, "年龄", age,
"服务器1", server1, "服务器2", server2);
}
}

输出结果:

可见,通过spring.application.json设置的属性覆盖了application.properties文件配置的属性。

使用YAML配置文件

application.properties文件的另一种形式是application.yml,这两种文件形式不同,本质一样。

YAML是JSON格式的超集,以层次格式来存储配置属性,SpringBoot使用SnakeYAML来解析YAML文件,由于spring-boot-starter自动依赖SnakeYAML,因此只要项目添加了spring-boot-starter依赖,SpringApplication就能解析YAML文件。

application.properties文件中的点号(.)对应yaml文件中的缩进,比如spring.application,name=cruncher对应于

spring:
application:
name: "cruncher"

如果YAML配置的属性包含多个列表项,那么它会被转化成【index】形式,比如

fkjava:
name: "SpringBoot软件"
age: 20
servers:
- www.fkjava.org
- www.crazyit.org

被转换为:

fkjava.servers[0]=www.fkjava.org

fkjava.servers[1]=www.crazyit.org

如果程序要加载YAML文件配置的属性,可使用以下工具类

  1. YamlPropertiesFactoryBean,它将YAML文件加载为Properties对象
  2. YamlMapFactoryBean,它将YAML文件加载为Map对象
  3. YamlPropertySourceLoader,它将YAML文件加载为PropertySource.

@PropertySource和@TestPropertySource都只能加载属性文件,不能加载Yaml文件,这是Yaml文件的局限之一。

文件路径:/resources/application.yml(系统会自动加载)

fkjava:
server:
name: "疯狂软件服务器"
port: 9000

文件路径:/resources/fk/fk.yml(系统不会自动加载)

fkjava:
name: "疯狂软件"
age: 20
servers:
- www.fkjava.org
- www.crazyit.org
//定义一个配置环境后处理器来加载fk.yml
public class FkEnvironmentPostProcessor implements EnvironmentPostProcessor
{
// 创建YamlPropertySourceLoader,用于加载YAML文件
private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader(); @Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application)
{
// 指定自定义的配置文件
Resource path = new ClassPathResource("fk/fk.yml");
// 加载自定义配置文件
PropertySource<?> ps = null;
try
{
ps = this.loader.load("custom-resource", path).get(0);
}
catch (IOException e)
{
e.printStackTrace();
}
System.out.println("fkjava.name: " + ps.getProperty("fkjava.name"));
System.out.println("fkjava.age: " +ps.getProperty("fkjava.age"));
System.out.println("fkjava.servers[0]: " +ps.getProperty("fkjava.servers[0]"));
System.out.println("fkjava.servers[1]: " +ps.getProperty("fkjava.servers[1]"));
// 将PropertySource中的属性添加到Environment配置环境中
environment.getPropertySources().addLast(ps);
}
}

可通过/META-INF/spring.factories来注册配置环境后处理器:

org.springframework.boot.env.EnvironmentPostProcessor=org.crazyit.app.FkEnvironmentPostProcessor

这样一来application.yml和fk.yml都被加载进来,下来可以在其他任何Bean中通过@Value来访问它们

@RestController
public class HelloController
{
// 使用@Value注解访问配置属性
@Value("${fkjava.server.name}")
private String serverName;
@Value("${fkjava.server.port}")
private String serverPort;
@Value("${fkjava.age}")
private String age;
@GetMapping
public String hello()
{
return "名称: " + serverName + ", 端口: " + serverPort
+ ", 年龄: " + age;
}
}

如果仅需要加载自定义的Yaml文件,在组件中使用这些配置属性,并不需要将属性添加到配置环境中,那么只要在容器中配置一个YamlPropertiesFactoryBean工厂Bean或YamlMapFactoryBean工厂Bean,它们就会自动读取Yaml文件,将其中的配置内容加载为Properties对象或Map对象。

@Configuration
public class MyConfig
{
// 在容器中配置一个YamlPropertiesFactoryBean
@Bean
public YamlPropertiesFactoryBean fkProps()
{
var factory = new YamlPropertiesFactoryBean();
factory.setResources(new ClassPathResource("fk/fk.yml"));
return factory;
}
}

spring中的工厂Bean有一个特征:当程序通过spring容器获取工厂Bean时,实际获取到的是该工厂的产品(getObject()方法的返回值),因此当程序获取上面配置的fkProps时,实际返回的是一个Properties对象。

@RestController
public class HelloController
{
// 使用@Value注解访问配置属性
@Value("${fkjava.server.name}")
private String serverName;
@Value("${fkjava.server.port}")
private String serverPort;
// 指定将容器中fkProps Bean注入fkProps实例变量
@Resource(name = "fkProps")
private Properties fkProps;
@GetMapping
public String hello()
{
return "名称: " + serverName + ", 端口: " + serverPort
+ ", 年龄: " + fkProps.getProperty("fkjava.age");
}
}

改变配置文件的位置

默认情况下,SpringBoot或按如下顺序加载配置文件,后加载的文件会覆盖先加载的文件,因此,后加载的文件有更高的优先级

  1. 类加载路径的根路径
  2. 类加载路径下的/config子目录
  3. 当前路径
  4. 当前路径的/config子目录
  5. 当前路径的/config子目录的任意直接子目录,比如/config/abc、/config/aef等

如果想改变这种默认行为,可通过如下系统属性(或环境变量、或命令行参数)来设置

  1. spring.config.name:改变配置文件的文件名,默认是application。如果用OS环境变量来设置,则属性对应于SPRING_APPLICATION_NAME环境变量名。
  2. spring.config.location:改变配置文件的加载路径。如果用OS环境变量来设置,则属性对应于SPRING_APPLICATION_LOCATION环境变量名。
  3. spring.config.additional.location:添加配置文件的加载路径,不会覆盖原来的配置文件的加载路径。

不要使用SpringBoot配置属性来设置以上三种属性,因为这几个属性的加载实际非常早,只能通过系统环境变量、系统属性或命令行参数来设置。

通过spring.config.location或spring.config.additional.location指定的加载路径是有先后顺序的,比如spring.config.location设置为“optional:classpath:/fkjava/,optional:file:./fkit/”,那么加载顺序为:

  1. optional:classpath:/fkjava/
  2. optional:file:./fkit/

如果使用spring.config.additional.location来添加配置文件的加载路径,那么新增路径总是排在默认的加载路径之后。比如将spring.config.additional.location设置为“optional:classpath:/fkjava/,optional:file:./fkit/”,那么SpringBoot加载顺序为:

  1. optional:classpath:/(类加载路径的根路径)
  2. optional:classpath:/config/(类加载路径下的/config子目录)
  3. optional:file:./(当前路径)
  4. optional:file:./config/(当前路径的/config子目录)
  5. optional:file:./config/*/(当前路径的/config子目录的任意直接子目录,比如/config/abc、/config/aef等)
  6. optional:classpath:/fkjava/
  7. optional:file:./fkit/

前5项是SpringBoot配置文件的默认加载路径

通配符(*)只能在外部路径中使用,例如file前缀就表示使用文件系统的当前路径;classpath前缀的路径不能使用通配符

每个加载路径只能在最后面使用一个通配符

@SpringBootApplication
public class App
{
static {
// 设置配置文件的文件名
// 通过系统属性设置SpringBoot配置文件的主文件名可以是application和fk
// 这样SpringBoot就会自动加载application.yml和fk.yml两个配置文件
System.setProperty("spring.config.name", "application, fk");
// 设置配置文件的加载路径
// optional:表示如果classpath下没有指定的配置文件,则不报错,如果不加这个前缀,若该路径下不存在配置文件,就会抛出异常
System.setProperty("spring.config.location",
"classpath:/, optional:classpath:/fk/");
// 设置额外的加载路径
// System.setProperty("spring.config.additional-location",
// "optional:classpath:/fk/");
}
public static void main(String[] args)
{
// 创建Spring容器、运行Spring Boot应用
SpringApplication.run(App.class, args);
}
}

导入额外的配置文件

在spring容器刷新之后,SpringBoot还可使用如下方式导入额外的配置文件

  1. 使用@PropertySource导入额外的属性文件
  2. 使用spring.config.import导入额外的配置文件(包括属性文件和Yaml文件)

如果只是为应用中的Bean组件导入一些配置属性,完全可通过上面两种方式来导入。以下示例:

resources\fk\fk.yml文件内容:(只能通过spring.config.import导入)

fkjava:
name: "疯狂软件"
age: 25
servers:
- www.fkjava.org
- www.crazyit.org

resources\fk\crazyit.properties文件内容:(spring.config.import和@PropertySource都能导入)

crazyit.book.name=Spring Boot
crazyit.book.price=128

resources\applocation.yml文件内容:

fkjava:
server:
name: "疯狂软件服务器"
port: 9000
spring:
config:
# 指定导入类加载路径下fk/fk.yml文件
import: optional:classpath:/fk/fk.yml

通过@PropertySource导入crazyit.properties文件示例:

@SpringBootApplication
// 导入类加载路径下fk/crazyit.properties文件
@PropertySource("classpath:/fk/crazyit.properties")
public class App
{
public static void main(String[] args)
{
// 创建Spring容器、运行Spring Boot应用
SpringApplication.run(App.class, args);
}
}

使用占位符

配置文件中可使用占位符(${})来引用已定义的属性,或者引用其他配置源(系统属性、环境变量、命令参数等)配置的属性。示例如下:

resources\applocation.yml文件内容:

app:
name: "占位符"
# 引用配置文件中的配置属性
description: "${app.name}应用演示了如何在配置文件中使用占位符"
book:
# 引用外部的配置属性
description: ${book.name}是一本非常优秀的图书
server:
# 配置服务端口
port: ${port}

添加命令行参数:--book.name="哈利波特" --port=9090,然后启动应用就会生效。

读取构建文件的属性

SpringBoot允许配置文件读取构建文件(pom.xml或build.gradle)的属性.

只要在pom.xml文件中定义如下元素,就能在配置文件中通过“@属性名@”来引用pom.xml文件中的属性

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
</parent>

resources\applocation.yml文件内容:

app:
java:
# 引用pom.xml文件中的属性
version: @java.version@
sourceEncoding: @project.build.sourceEncoding@
name: @name@
version: @version@

对于Gradle项目,首先要在build.gradle中对java插件的processResources进行如下配置,然后将这段配置加到build.gradle文件的最后。

// 配置Java插件的processResources Task
processResources {
expand(project.properties)
}

接下来即可在配置文件中通过“${属性名}”(这里不再是占位符的表示)的形式引用build.gradle中属性。

resources\applocation.yml文件内容:

app:
java:
version: "${sourceCompatibility}"
name: "${rootProject.name}"
version: "${version}"

配置随机值

如果需要为应用配置各种随机值(如随机整数、uuid等),可通过在配置文件中使用${random.xxx}的形式来生成随机值。示例如下:

resources\applocation.yml文件内容:

fkjava:
secret: "${random.value}"
number: "${random.int}"
bignumber: "${random.long}"
uuid: "${random.uuid}"
number-less-than-ten: "${random.int(10)}"
number-in-range: "${random.int(20,100)}"

运行结果如下:

如果刷新页面,这些随机值不会发生改变,因为这些随机值是在配置文件中定义的,因此刷新页面并不会改变这些值,只有当应用重启,SpringBoot重新加载配置文件时,才会重新生成这些随机值。

SpringBoot--如何给项目添加配置属性及读取属性的更多相关文章

  1. 如何为你的 Vue 项目添加配置 Stylelint

    如何为你的 Vue 项目添加配置 Stylelint 现在已经是 9102 年了,网上许多教程和分享帖都已经过期,照着他们的步骤来会踩一些坑,如 stylelint-processor-html 已经 ...

  2. javascript 理解对象--- 定义多个属性和读取属性的特性

    一 定义多个属性 ECMAScript5 定义了一个Object.defineProperties()方法,用于定义多个属性.此方法接受两个对象参数: 第一个对象:要添加或修改其属性的对象 第二个对象 ...

  3. Spring配置从配置文件读取属性值

    spring将properties文件读取后在配置文件中直接将对象的配置信息填充到bean中的变量里. 原本使用PropertyPlaceholderConfigurer类进行文件信息配置.Prope ...

  4. SpringBoot 入门:项目属性配置

    开发一个SpringBoot 项目,首当其冲,必然是配置项目 一.项目属性配置 1. SpringBoot自带了Tomcat服务器,通过使用项目配置文件来修改项目的配置,如图配置了部署在80端口,目录 ...

  5. springboot项目接入配置中心,实现@ConfigurationProperties的bean属性刷新方案

    前言 配置中心,通过key=value的形式存储环境变量.配置中心的属性做了修改,项目中可以通过配置中心的依赖(sdk)立即感知到.需要做的就是如何在属性发生变化时,改变带有@Configuratio ...

  6. JAVAEE——spring01:介绍、搭建、概念、配置详解、属性注入和应用到项目

    一.spring介绍 1.三层架构中spring位置 2.spring一站式框架 正是因为spring框架性质是属于容器性质的. 容器中装什么对象就有什么功能.所以可以一站式. 不仅不排斥其他框架,还 ...

  7. 使用IDEA构建Spring-boot多模块项目配置流程

    使用IDEA构建Spring-boot多模块项目配置流程 1.创建项目 点击Create New Project 在左侧选中Spring Initializer,保持默认配置,点击下一步. 在Grou ...

  8. Spring-Boot项目中配置redis注解缓存

    Spring-Boot项目中配置redis注解缓存 在pom中添加redis缓存支持依赖 <dependency> <groupId>org.springframework.b ...

  9. Idea开发环境中,开发springboot类型的项目,如果只引入parent节点,不添加依赖节点,maven是不会加载springboot的任何依赖的

    在SpringBoot类型的项目中,我本来是要使用pringBoot,创建一个Console项目,我原本在pom.xml中添加paren节点了,天真的认为不需要再添加其他任何依赖了,可是接下来的1个小 ...

  10. SpringBoot 项目中配置多个 Jackson 的 ObjectMapper ,以及配置遇到的坑

    目录 问题说明 原因排查分析 结论总结 Jackson 自动装配分析 问题说明 我们都知道,SpringBoot 项目中,如果引入了 Jackson 的包,哪怕不配置,SpringBoot 也会帮我们 ...

随机推荐

  1. springboot 集成Swagger2报错 Failed to start bean ‘documentationPluginsBootstrapper’; nested exception is

    springboot 集成Swagger2报错 Failed to start bean 'documentationPluginsBootstrapper'; nested exception is ...

  2. 🔥Gitlab 删除仓库文件夹

    1.进入文件夹 -> 右键 -> Git Bash Here -> 打开命令窗口 2.拉取代码到本地 (本地无项目情况) git clone git地址 3.拉取最新代码(本地已有项 ...

  3. vscode配置xdebug断点调试thinkphp

    vscode配置xdebug断点调试thinkphp其实和配置其他php框架的断点调试一样,步骤如下: 下载xdebug,重命名为php_xdebug.dll并移动到php.ini目录: (不知道下载 ...

  4. Python实验3 函数与代码复用

    目的:理解函数封装与递归思想 实验任务: 基础 :编写函数cal_factorial(n)计算阶乘(循环实现). 源码: def cal_factorial(n): result = 1 for i ...

  5. 海康摄像头SDK在Linux、windows下的兼容问题(二)已解决

    上一篇提出的问题,在前几天解决了. 海康的技术人员给出了指导,在Linux库加载失败的时候,需要在代码中手动指定配置文件. 库文件加载说明] // 1. lib文件夹里面所有库文件libhcnetsd ...

  6. base的含义及使用及与this的区别

    C#中base关键字的几种用法 - bobob - 博客园 (cnblogs.com) C#构造函数里的base和this的区别 - 傲世狂枫 - 博客园 (cnblogs.com) 我的理解 1.在 ...

  7. 【经验】Word 2021|Word文档间复制粘贴保留源格式后,行间距却发生变化(文档网格)

    版本:Word 2021 这个问题通常是因为文档中设置了网格,段落中设置了按照网格对齐. 下面记录网格的相关设置. 打开设置界面 布局-页边距-自定义页边距. 影响网格布局的所有因素 如果想将两篇文档 ...

  8. 10个让你成为CSS画家的技巧,不容错过

    @charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...

  9. C# INotifyPropertyChanged Small Demo

    public class PChangeTest:INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyC ...

  10. B1036 跟奥巴马一起编程

    美国总统奥巴马不仅呼吁所有人都学习编程,甚至以身作则编写代码,成为美国历史上首位编写计算机代码的总统.2014年底,为庆祝"计算机科学教育周"正式启动,奥巴马编写了很简单的计算机代 ...