Spring Boot 二三事:application.prop
新建一个Spring Boot项目后,都会默认使用名为application.properties的文件进行应用程序的配置。在本文中,我将向您展示如何在application.properties 中自定义属性,处理数据类型以及在不同的运行时环境中使用属性。
一、关于 application.properties
application.properties文件只不过是配置属性的简单Key-Value存储文件,你可以将application.properties配置文件打包在应用程序jar中,或将该文件放在运行时环境的文件系统中,并在Spring Boot启动时加载它。
简而言之,你可以使用application.properties:
- 配置SpringBoot提供的一些参数
- 自定义你的App所需的配置参数
Springboot 默认从src/main/resources
自动加载application.properties,
![](https://img.haomeiwen.com/i16192464/2275b04593c56b4e.png)
application.properties文件只是一个特定规则的文本文件,每行固定格式为key=value。 空行在propertie文件中也是允许的。例如:
aligenie.sologan=Hello Wrold
在定义属性时,最好取有意义的名称,有利于提供配置文件的可读性。
二、使用 @Value注入属性
一旦定义了第一个自定义属性,就可以准备在Spring beans中注入使用该属性。一种简单的方法是使用@Value注解。这个注解在bean的构造方法和字段上都可以使用。
@Value可以使用两种表达式进行属性注入
- 使用属性占位符
${ property : default_value }
- 使用SpEL
#{ obj.property? : default_value }
通常,使用SpEL表达式更强大,除了属性引用之外,您还可以使用它们来执行许多其他操作。让我们先从简单的开始,使用属性占位符进行注入。下面的一个例子是在bean的构造方法种注入属性
/**
* @author leven.chen
* @project aligenie-boot
* @date 2019/2/26 18:19
* @apiNote
*/
@RestController
@RequestMapping("/api/test")
public class TestController {
/**
* logger
*/
private static final Logger log = LoggerFactory.getLogger(TestController.class);
private final String aligenieSologan;
public TestController(@Value("${aligenie.sologan}") String aligenieSologan) {
this.aligenieSologan = aligenieSologan;
log.info(this.aligenieSologan);
}
}
@Value("${aligenie.sologan} 中的property要和在application.properties文件定义保持一致。
同样的,你也可以直接在 field
中使用
@RestController
@RequestMapping("/api/test")
public class TestController {
/**
* logger
*/
private static final Logger log = LoggerFactory.getLogger(TestController.class);
@Value("${aligenie.sologan}")
private String aligenieSologan;
}
如果Spring没有找到你想要注入的键,那么在尝试创建bean时它会抛出IllegalArgumentException
。
设置默认值
默认情况下,缺少的属性会导致应用抛出异常。 但是,你可以为这个属性设置一个默认值。 当application.properties文件中缺少key时,将自动使用默认值。
@Value("${aligenie.sologan:This is default value}")
你只需要修改一下@Value表达式,在property加上一个 ```:`` 符号并设置你想要的默认值即可,是不是非常简单?
@Value获取到的属性为null?
这是一个非常常见的问题,请分析如下代码,
@Service
class DemoService {
@Value("${aligenie.sologan:Hello world}")
private String message;
InitService() {
log.info(message); // prints: null
}
}
在构造方法中打印该属性时,发现是null,这是为什么呢?其实是在初始化bean的时候,首先执行的是构造函数,其次才是执行的@Value注入,所以不能将值赋给尚未存在的对象的字段。这就是为什么构造函数注入更安全的原因。
三、自定义属性及特殊处理
到目前为止,我们只讨论纯字符串属性。现在我们将研究其他数据类型及一些可以在表达式中使用的有用技巧。
基本数据类型:string, integer, boolean
由于application.properties是文本文件,因此所有定义的值都是字符串。然而,如果将非字符串变量注入值,Spring可以自动将字符串值强制转换为其他类型。
# 这是一段注释
aligenie.enabled=true
aligenie.appId=10
要插入这些值,请使用与字符串值相同的表达式。Spring将检测变量类型并将属性强制转换为适当的类型。
![](https://img.haomeiwen.com/i16192464/d8ff45e43ed98b58.png)
多行字符串属性
如果有一个非常长的属性值,可以考虑将它分成几行以提高可读性。可以在application.properties文件中使用反斜杠字符换行。
aligenie.content=Bright moonlight in front of bed\
ground frost\
up at the bright moon\
down at home
请注意,注入的值不包含换行字符。
注入多值属性 arrays, list, set
应用中的某些属性可能会使用多个值。在这种情况下,可以指定由逗号分隔的值列表进行配置。
aligenie.numbers=1,2,3,4,5,6,1,2
只需将属性注入数组变量即可。
TestController(@Value("${aligenie.numbers}") int[] numbers) {
}
List
和Set
也是基本上相同。只不过针对set
,如果属性的值包含重复项,则只会将一个元素添加到集合中。
![](https://img.haomeiwen.com/i16192464/079782edcce47c7b.png)
自定义列表属性的分隔符
默认情况下,Spring使用逗号分割属性。如果你想要使用分号来作分隔符,该怎么办呢?
aligenie.lists=aa;bb;cc;dd
幸运的是,Spring SpEL支持可以使用不同的分隔符拆分属性。你需要的只是一个简单的修改一下你的表达式
public TestController(
@Value("#{'${aligenie.lists}'.split(';')}") List<String> list) {
this.list = list;
log.debug("list", list);
}
@Value("#{'${aligenie.lists}'.split(';')}")
干了什么?
Spring将属性注入为常规字符串。用单引号标出它。接下来,在表达式(#{...})内部,对注入的值调用String类的split()方法。最后,Spring将结果放入列表中。
当然,也可以将该属性作为常规字符串注入,然后自己写代码进行拆分也是可以的。
使用Hashmap绑定properties
在项目开发中,我们经常会遇到一些字典数据想配置到配置文件中,比如国家代码这个数据,一种比较容易想到的办法就是定义多个key。
aligenie.country.CN=+86
aligenie.country.US=+01
aligenie.country.AU=+61
这样key会非常的多,而且使用起来也不方便,如果能直接注入到一个Map结构里,然后直接get(key)
就好了。
当然在Spring 中,也是非常容易做到的。
step1:定义属性,格式非常类似json数据,唯一的区别是不需要外层的双引号
aligenie.country-map={'CN':'+86', 'US':'+01', 'AU':'+61'}
step2:使用@Value + Spring SpEL注入
public TestController(@Value("#{${aligenie.country-map}}") Map<String, String> countryMap) {
this.countryMap = countryMap;
log.debug(countryMap.toString());
}
使用@Value("#{${aligenie.country-map}}")
这样就可以将属性以map
的形式注入进去。
四、分环境配置 application.properties
通常,我们在几个不同的环境中运行应用程序。我们将本地机器用于开发,测试环境,最后用于生产服务器。而且我们的应用程序的配置应该在每个环境中有所不同。Spring Boot为我们提供了几种方法可以解决这个问题。让我们一起来看看。
在application.properties中使用环境变量
可以做的最简单的事情是使用操作系统中的环境变量。Spring允许将环境变量直接放入application.properties文件或@value注释中的属性占位符中。
aligenie.init.java-home=This is Java path: ${JAVA_HOME}
Spring在运行时插入值,并使用操作系统中的实际值替换占位符。更重要的是,可以像其他占位符一样设置默认值:
aligenie.init.java-home=This is Java path: ${JAVA_HOME:Undefined JAVA_HOME}
public TestController(@Value("${aligenie.init.java-home}") String javaHome) {
this.javaHome = javaHome;
}
使用Spring.profiles
另一种方法是在application.properties同级目录下按环境新建配置文件,并在应用程序启动时指定应加载哪一个。这就是使用Spring profiles. 其中文件名应遵循模式application- <profile> .properties,其中<profile>应替换为您选择的配置文件名称。
![](https://img.haomeiwen.com/i16192464/c41597009267b770.png)
接下来,就可以按环境进行配置。同时我们可以将公共部分保留在主application.properties文件中,最后一步是在所需环境中激活所选的配置文件。可以通过属性名为spring.profiles.active
的属性来完成此操作。你有两个选择:
- 在application.properties 中设置
spring.profiles.active
- 在启动时指定
spring.profiles.active
如果你希望为每个环境单独构建程序包,则可以在application.properties文件中设置,例如:
spring.profiles.active=dev
当然也可以在启动应用程序时,将spring.profiles.active属性作为常规VM选项传递。此VM选项将覆盖application.properties中的值。
java -jar app.jar -Dspring.profiles.active=dev
以上两种方法效果是相同的,无论选择哪种方法,Spring Boot都会使用环境专用属性加载所需文件。
使用外部的application.property文件
如果无法将配置文件放在jar文件中,例如密码信息等,该怎么办?
当然不用担心,Spring Boot为我们提供了一种简单的解决方案。
Spring Boot可以直接从运行时环境的文件系统加载自定义application.property文件。需要做的是将spring.config.additional-location属性设置为文件系统中application.properties文件所在的目录即可。
java -jar app.jar -Dspring.config.additional-location="C:/myapp/path/to/config/"
如果jar文件内包含application.properties,则Spring Boot将外部文件加载属性,因为外部配置文件的优先级更高,具体可参考SpringBoot配置
五、总结
总而言之,大家应该已经知道如何创建自定义属性并在应用程序中使用基本类似和更复杂的数据类型。以及在Springboot中按环境配置,
本文中都是使用@value
注解进行属性注入,其实SpringBoot还提供一种使用Java Bean的形式进行属性注入。使用这种方式更加简单,而且可以将一组相关
的属性都定义到一个bean中,提高程序可读性。下一篇文章中将为您分享《Spring Boot二三事 —— 使用@ConfigurationProperties》
如果您觉得这篇文章有用,请留下您的小💗💗,我是一枚Java小学生,欢迎大家吐槽留言。