使用 @ConfigurationProperties 在 Spring Boot 中加载配置

本文地址:使用 @ConfigurationProperties 在 Spring Boot 中加载配置

使用 Spring Boot 加载配置文件的配置非常便利,我们只需要使用一些注解配置一下就能很方便地加载配置项了。今天我们谈一谈 ConfigurationProperties 注解的使用,ConfigurationProperties可以把配置文件中有相同前缀的配置在一个配置类中直接省去相同前缀进行读取,甚至还可以将相同前缀的配置自动封装成实体类。

步骤

创建标准 Spring Boot 工程

首先,我们使用一个标准的 Spring Boot 的依赖设置,在 build.gradle文件的plugins以及dependencies中加入相关内容,如下:

plugins {
    id 'org.springframework.boot' version '2.1.6.RELEASE'
    id 'java'
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

然后创建 Spring application 类,用于启动我们的 Spring Boot 应用。(如果使用 IDEA 进行开发,使用 New Project -> Spring Initializa 创建 Spring Boot 应用非常方便,IDEA 会帮我们自动生成 Spring application 类。)我们顺便在Spring Application 类中加上一个 component,方便我们进行测试。(实现了CommandLineRunner的 component 会在应用启动成功后运行)

@SpringBootApplication
public class ConfigurationApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigurationApplication.class, args);
    }


    @Component
    class StartupRunner implements CommandLineRunner {

        @Override
        public void run(String... args) throws Exception {
            System.out.println("Application running");
        }
    }
}

编写配置文件

Spring Boot 中默认的配置文件是 application.properties,但是我们通常会将自定义的配置单独放在一个文件中,这里我们在 resources 目录下创建一个配置文件 configprops.properties,并加入以下配置内容:

user.name=Tryking
user.password=Security
user.age=18

可以看到,三个配置都有相同的前缀 user

编写配置读取类

接下来我们编写一个配置读取的类文件

@Configuration
@PropertySource("classpath:configprops.properties")
@ConfigurationProperties(prefix = "user")
public class ConfigProperties {
    private String name;
    private String password;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    
    @Override
    public String toString() {
        ...
    }
}

Configuration 注解表明我们需要一个配置 Bean,Spring 启动的时候会在应用 Context 中帮我们创建一个 Java Bean。这里使用 Component 也是可以的,但是为了代码的可读性,我们使用 Configuration 更合理。

PropertySource 注解用来定义我们的配置文件位置,如果没有此注解的话,Spring Boot 默认找的是 application.properties 文件。

ConfigurationProperties 注解便是我们的主角了,它用来定义我们要加载的配置的前缀,我们这里定义的是 user,因此 Spring Boot 会去寻找 user 前缀的配置。

在此类中,我们定义了三个属性,分别是 name, password, age,和我们配置文件中的属性是一一对应的。因为 Spring 使用标准的 Java Bean Setters,因此我们需要实现各个属性的 getter & setter 方法。

这样我们就完成了所有的配置,Spring 会自动将我们在配置文件中含有 user 前缀的配置绑定到 ConfigProperties 类中具有相同名字的属性上。

测试配置成功与否

然后我们在 ConfigurationApplication 启动类中定义的 StartupRunner component 中增加对此配置文件的测试,我们使用此配置的 toString() 方法进行输出。

private final ConfigProperties configProperties;

// 引入配置 bean
@Autowired
public ConfigurationApplication(ConfigProperties configProperties) {
    this.configProperties = configProperties;
}

public static void main(String[] args) {
    SpringApplication.run(ConfigurationApplication.class, args);
}

@Component
class StartupRunner implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("Application running");
        // 输出
        System.out.println(configProperties.toString());
    }
}

最后,我们启动 Application,可以看到命令行中成功输出了我们的配置信息。

Application running
ConfigProperties{name='Tryking', password='Security', age=18}

进阶:加载嵌套属性

我们还可以使用 ConfigurationProperties 来实现复杂对象的属性加载,比如 Map,List,以及实体类。

我们在配置文件中继续增加更多的配置:

# Simple properties
user.name=Tryking
user.password=Security
user.age=18

# List properties
user.friends[0]=Tom
user.friends[1]=Jack

# Map properties
user.scores.java=90
user.scores.python=95

# Object properties
=Beijing
user.job.salary=50000

然后在配置读取类中增加对这些属性的读取。

private String name;
private String password;
private int age;
private List<String> friends;
private Map<String, Integer> scores;
private Job job;

...set...

同时不要忘记 getter & setter 方法的实现,以及实体 Bean Job的定义(同样需要实现 getter & setter 方法)。

完成后,我们重新运行 Application,可以看到这些复杂配置也成功读取出来了:

Application running
ConfigProperties{name='Tryking', password='Security', age=18, friends=[Tom, Jack], scores={java=90, python=95}, job=Job{address='Beijing', salary=50000}}

进阶:在 @Bean 上使用 @ConfigurationProperties

我们还可以在具有 @Bean 注解的方法上使用 @ConfigurationProperties。

当我们想在我们引用的外部第三方 component 中绑定一些属性时,此方法特别有用。

我们先创建一个简单的 Item 供后面的类引用。

public class Item {
    private String name;
    private int size;
 
    // standard getters and setters
}

接下来,我们在 @Bean 注解上使用 @ConfigurationProperties,以绑定我们配置的属性到此 Bean 上。

@Configuration
public class ConfigProperties {
 
    @Bean
    @ConfigurationProperties(prefix = "item")
    public Item item() {
        return new Item();
    }
}

这样,Spring Context 会将所有带有 item 前缀的属性帮我们映射到 Item 实例中。

配置验证

@ConfigurationProperties 提供了属性验证的功能,使用 JSR-303 格式来进行验证。支持验证需要在类上增加@Validated注解,然后我们可以进行如下验证:

  • hostName 不允许为空

    @NotBlank
    private String hostName;
    
  • authMethod 属性的长度为 1 - 3 个字符

    @Length(max = 4, min = 1)
    private String authMethod;
    
  • port 属性的取值从 1025 到 65536

    @Min(1025)
    @Max(65536)
    private int port;
    
  • from 属性必须满足 Email 格式

    @Pattern(regexp = "^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,6}$")
    private String from;
    

通过这种方式我们就可以验证属性是否合规,而不用使用一系列的 if 、else 来进行验证了。

如果有验证无法通过,那么我们启动应用的时候 Application 将启动失败,并且抛出一个IllegalStateException异常。

相关代码

Spring Boot Configuration

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

推荐阅读更多精彩内容

  • 月亮偷偷躲在云层中 腑视万物 万家灯火忽明忽暗 悲剧,喜剧 在月朦胧下上演 新生命在啼哭声中开始 预言着坎坷的到来...
    扶疏wei阅读 374评论 0 7
  • 是善意而非残忍守护着宇宙的砥柱,处于和平中的人类将会品尝到无数比在血泊中滋养出来的更为甜美胜利的果实。 我一直以来...
    米思吳阅读 322评论 0 1
  • 《磐吉解字靖庐之~招》 三十六靖庐之二十八~招隐庐。什么是修?什么是炼?万物不明,而自以为修自成其练,致使纷繁复杂...
    惬意永不放弃阅读 440评论 0 0
  • 100/100 希希说,妈妈以后能不能不生气了?二宝也跟着说,妈妈生气找奶奶。我明白她的意思,每次我生气,她就哭着...
    韩今茹阅读 230评论 1 3
  • 我没有觉得父亲在老去,反而给我的感觉是越活越年轻,他现在表现不错更注重保养自己了。又或许是当着我的面前吧故意逞能,...
    小慕雅的牧屋阅读 142评论 0 1