Spring Boot 二三事:application.properties的那点事

新建一个Spring Boot项目后,都会默认使用名为application.properties的文件进行应用程序的配置。在本文中,我将向您展示如何在application.properties 中自定义属性,处理数据类型以及在不同的运行时环境中使用属性。

一、关于 application.properties

     application.properties文件只不过是配置属性的简单Key-Value存储文件,你可以将application.properties配置文件打包在应用程序jar中,或将该文件放在运行时环境的文件系统中,并在Spring Boot启动时加载它。
简而言之,你可以使用application.properties:

Springboot 默认从src/main/resources 自动加载application.properties,

image.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将检测变量类型并将属性强制转换为适当的类型。


多行字符串属性

如果有一个非常长的属性值,可以考虑将它分成几行以提高可读性。可以在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) {
}

ListSet也是基本上相同。只不过针对set,如果属性的值包含重复项,则只会将一个元素添加到集合中。

自定义列表属性的分隔符

默认情况下,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>应替换为您选择的配置文件名称。

接下来,就可以按环境进行配置。同时我们可以将公共部分保留在主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小学生,欢迎大家吐槽留言。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容