Spring Boot附带了一个使用名为application.properties的文件进行应用程序配置的内置机制。在本文中,我将向您 展示如何在自定义方案中有效使用application.properties文件 。
我不打算讨论Spring Boot框架指定的属性。使用现有配置键非常简单。您可以 在官方文档中 轻松 找到常用配置 key-value。
这篇文章介绍了定义自定义属性,处理数据类型以及在不同的运行时环境中使用属性。如果那就是你要找的东西,继续阅读。
application.properties基础知识
application.properties文件只不过是配置属性的简单键值存储。您可以将配置文件捆绑在应用程序jar中,或将该文件放在运行时环境的文件系统中,并在 Spring Boot启动 时加载它。
简而言之,您可以使用application.properties文件:
在默认位置创建application.properties
Spring Boot会自动从项目类路径加载application.properties文件。您所要做的就是在 src / main / resources 目录下创建一个新文件。
application.properties文件只是一个常规文本文件。每行包含属性键,等号和属性值。空行也是允许的。
这是一个示例属性:
sbpg.init.welcome-message=Hi there!
您可能想知道属性键是否有任何特定语法。答案是:不,没有。但是,最好保留预定义Spring Boot属性中提出的命名约定,以提高文件的可读性。
在这种情况下,您可以将键视为完全限定的Java类名。您可以通过点符号分割几个部分来构建键。键 的最后一部分应描述该属性的用途。 您可以使用其他部件对多个属性进行逻辑分组。
使用@Value注入属性
一旦定义了第一个自定义属性,就可以在Spring bean中使用它了。您可以使用 @Value 注释简单地注入属性值。注释在bean构造函数中工作,直接在bean字段上工作。
@Value 注解接受要注入的属性的键:
通常,表达式更强大,除了属性解除引用之外,您还可以将它们用于许多其他事情。让我们暂时保持简单并使用属性占位符。以下是通过bean的构造函数注入属性值的方法:
@Service <b>class</b> InitService { <b>private</b> <b>final</b> String message; InitService(@Value(<font>"${sbpg.init.welcome-message}"</font><font>) String message) { <b>this</b>.message = message; log.info(message); } </font><font><i>// ...</i></font><font> } </font>
出于同样的原因,您可以直接在字段上使用注释。但是,它会使单元测试更加困难并且可能导致一个非常常见的问题。我稍后会描述这个问题,以便你形成自己的观点。
@Service <b>class</b> InitService { @Value(<font>"${sbpg.init.welcome-message}"</font><font>) <b>private</b> String message; </font><font><i>// ...</i></font><font> } </font>
如果Spring没有找到你想要注入的键,那么在尝试创建bean时它会抛出 IllegalArgumentException 。
默认属性值
默认情况下,缺少的属性会导致异常。但是,您可以决定制作一个可选属性。当application.properties文件中缺少键时,您可以指示Spring为属性键注入默认值。
这该怎么做?
您需要通过在属性键之后添加冒号(:)后跟默认值来修改表达式。这是一个例子:
@Value(<font>"${sbpg.init.welcome-message:Hello world}"</font><font>) </font>
为什么我的@Value为null?
这是SpringBoot新人中的常见问题。让我们讨论下面使用场注入机制的bean。
@Service <b>class</b> DontDoItService { @Value(<font>"${sbpg.init.welcome-message:Hello world}"</font><font>) <b>private</b> String message; </font><font><i>// ...</i></font><font> InitService() { log.info(message); </font><font><i>// prints: null</i></font><font> } } </font>
这段代码有什么问题?
代码的作者不理解Spring 在 创建bean 之后 将值注入bean的字段。并且使用构造函数创建bean,对吧?当你这么想的时候,这很明显。您不能将值分配给尚不存在的对象的字段。
换句话说,首先执行构造函数中的代码。注射才会是接下来发生,这就是构造函数注入更安全的原因。
application.properties文件中的注释
除了属性和空行之外,application.properties字段还可以包含注释。要注释一行,只需将哈希字符放在一行的开头即可。
#The init message logged at the startup sbpg.init.welcome-message=Hi there!
您只能注解说明整行。一行中间的哈希字符是文字处理的。从技术上讲,您可以将哈希字符用作属性键或值的一部分。
定义自定义属性
到目前为止,我们只讨论了普通的字符串属性。现在我们将研究其他数据类型。我还将向您展示一些可以在表达式中使用的有用技巧。
1.基本属性类型:字符串,整数,布尔值
由于application.properties是一个文本文件,因此所有定义的值都是字符串。但是,如果尝试将值注入非字符串变量,Spring框架足够智能,可以自动将字符串值转换为其他类型。
这是一个带数字和布尔文字的示例:
sbpg.init.number=42 sbpg.init.display-number=<b>true</b>
要注入这些值,请使用与字符串值相同的表达式。Spring检测变量类型并将属性转换为适当的基元。
InitService(@Value(<font>"${sbpg.init.number}"</font><font>) <b>int</b> number, @Value(</font><font>"${sbpg.init.display-number}"</font><font>) <b>boolean</b> displayNumber) { <b>if</b> (displayNumber) { log.info(</font><font>"Magic number: {}"</font><font>, number); } } </font>
您还可以将属性注入 原始包装类, 如 Integer , Boolean , BigDecimal ,甚至是自定义 枚举 。您无需额外的工作。
2.多行字符串属性
如果您有很长的属性值,可以考虑将其分成几行以提高可读性。使用反斜杠字符断开application.properties文件中的行。
sbpg.init.welcome-message=Hi there! This value is pretty <b>long</b> / and that is why I decided to / <b>break</b> it into multiple lines
请注意,注入的值不包含新行字符。
3. 属性为数组,列表或集合
应用程序中的某些属性可能会定义值集合。在这种情况下,为您的所需属性键指定由逗号分隔的值列表。
sbpg.init.numbers=0,1,1,2,3,5,8
Spring再次为您做转换。只需将属性注入数组变量即可。
InitService(@Value(<font>"${sbpg.init.numbers}"</font><font>) <b>int</b>[] numbers) { </font><font><i>// ...</i></font><font> } </font>
列表和集合之类的集合完全相同。如果属性的值包含重复项,则只会将一个元素添加到集合中。
InitService(@Value(<font>"${sbpg.init.numbers}"</font><font>) List<Integer> numbers) { </font><font><i>// ...</i></font><font> } </font>
4.列表属性的自定义分隔符
默认情况下,Spring使用逗号分割您的属性。没有办法逃避逗号。如果你想要一个像分号这样的分隔符,你该怎么办?
sbpg.init.numbers=0;1;1;2;3;5;8
幸运的是,您可以使用不同的分隔符自行拆分属性。你需要的只是一个简单的表达。
InitService(@Value(<font>"#{'${sbpg.init.numbers}'.split(';')}"</font><font>) List<Integer> numbers) { </font><font><i>// ...</i></font><font> } </font>
这里发生了什么?
Spring将属性注入为常规字符串。用单引号标出它。接下来,在表达式( #{...} )内部,对注入的值调用String类的 split() 方法。最后,Spring将结果放入列表中。
或者,您可以将属性作为常规字符串注入并自行拆分。您应该决定什么对您更具可读性。
5.属性为hashmap
注入map比数组和列表更棘手。让我们从您应该在application.properties文件中使用的值的格式开始。
sbpg.init.number-map={KEY1:1, KEY2:2, KEY3:3}
,map几乎看起来像JSON。唯一的区别是不需要引号。如果您愿意,可以将键和值包装为引号。Spring会为你打开它们。
最后一步是使用 @Value 注释注入属性。为此,请将属性占位符放在表达式中。如果没有表达式,Spring将抛出 IllegalStateException 。
InitService(@Value(<font>"#{${sbpg.init.number-map}}"</font><font>) Map<String, Integer> numberMap) { </font><font><i>// ...</i></font><font> } </font>
6. 自定义属性的命名约定
正如我已经提到的,属性键类似于完全限定的Java类名。它不是强制性的,但连接属性的逻辑分组提高了可读性。在项目开始时,它似乎是多余的。但是,项目增长,属性数量增加。保持您的财产有条理。
根据我的经验,为所有自定义应用程序属性使用某种前缀也是一个好主意。将它们与内置的Spring属性区分开来更容易。特别是Spring Boot新人都很欣赏这种方法。
通常,项目有一些名称的缩写。您可以将它用作自定义属性键的第一部分。为了演示这种方法,我将 sbpg 放在 本文的 所有示例中,它代表Spring Boot PlayGround。
环境中的application.properties
为清楚起见,我们不会将应用程序配置保留在单独的位置。通常,我们在几个不同的环境中运行应用程序。我们将本地机器用于开发,测试环境,最后用于生产服务器。通常,我们的应用程序的配置应该在每个环境中有所不同。
您有几种方法可以解决此问题。让我们来看看Spring提供的功能。
1. 在application.properties中使用环境变量
您可以做的最简单的事情是使用操作系统中的旧环境变量。Spring允许您将环境变量直接放在属性占位符中的application.properties文件或 @Value 注释中。
sbpg.init.java-home=This is Java path: ${JAVA_HOME}
Spring在运行时插入值,并使用操作系统中的实际值替换占位符。
更重要的是,您可以像其他占位符一样设置缺失变量的默认值:
sbpg.init.java-home=This is Java path: ${JAVA_HOME:Undefined JAVA_HOME}
2. 配置文件特定配置
另一种方法是将所有可能的配置文件捆绑在jar中,并指示应用程序在启动时应加载哪一个。实现此方法的最简单方法是使用 Spring配置文件 。
这该怎么做?
首先创建具有与主application.properties文件位于同一位置的属性的其他文件。文件名应遵循模式 application- <profile> .properties ,其中 <profile> 应替换为您选择的配置文件名称。
接下来,使用适当的配置属性填充文件。您可以将公共部分保留在主application.properties文件中。Spring Boot不会加载其他文件,除非您告诉框架读取它们。
最后一步是在所需环境中激活所选的配置文件。您可以通过设置名为 spring.profiles.active的 Spring Boot属性来完成此 操作 。你有两个选择:
哪一个更好?这取决于您如何准备部署包。
选项1.按环境分开jar包
如果您希望为每个环境单独构建程序包,则可以在构建过程中在application.properties文件中设置活动概要文件。
spring.profiles.active=dev
您的构建过程需要为其作为构建的一部分所针对的每个环境替换 spring.profiles.active 属性的值。Maven还具有配置文件的概念,通常用于分离不同环境的构建。您可以 指示Maven动态替换application.properties中的值 并设置活动的Spring配置文件。
选项2.适用于所有环境的单罐包装
如果您按照他的“持续交付”一书中的Jez Humble建议,您可以将完全相同的程序包部署到所有环境中。在这种情况下, application.properties中spring.profiles.active 属性的值将仅用作默认配置文件。
接下来,在运行时环境中启动应用程序时,应将 spring.profiles.active 属性作为常规VM选项传递。此VM选项将覆盖application.properties中的值。
java -jar app.jar -Dspring.profiles.active=dev
如果不直接运行jar文件但将应用程序部署到某个servlet容器,请查看其手册以了解如何传递VM选项。
无论您选择哪个选项,设置活动配置文件都会导致Spring Boot使用环境专用属性加载所需文件。
3. 外部化application.property文件 - 超出jar / war边界
如果您无法将环境属性放在jar文件中,该怎么办?例如,我们将密码存储在属性中。即使是处理该应用程序的开发人员,生产凭证也可能保密。
不用担心,Spring Boot为您提供了解决方案。
该框架可以直接从运行时环境的文件系统加载自定义application.property文件。您需要做的是将 spring.config.additional-location 属性设置为放置外部application.properties文件的目录。
java -jar app.jar -Dspring.config.additional-location="C:/myapp/path/to/config/"
如果您的应用程序包包含application.properties,则Spring Boot将从具有更高优先级的外部文件加载属性。
结论
总而言之,您应该已经知道如何创建自定义属性并在应用程序中使用原始和更复杂的数据类型。创建专用于单独运行时环境的application.properties文件也不应该成为您的问题。您已经知道有一些方法可以解决这个问题。