(最新 9000 字 )Spring Boot 配置特性解析

愛生活,愛編碼,微信搜一搜【架構技術專欄】關注這個喜歡分享的地方。本文 架構技術專欄 已收錄,有各類JVM、多線程、源碼視頻、資料以及技術文章等你來拿java

1、概述

目前Spring Boot版本: 2.3.4.RELEASE,這更新的速度也是嗖嗖的了,隨着新版本的發佈,也一步步針對公司基礎組件進行了升級改造,其中很重要的一塊就是配置文件的更新(雖然目前已經所有使用了Apollo)。針對Spring Boot 新版本的配置文件也作了一次梳理,確實發現了之前沒有注意到的點。mysql

2、新版的外部配置

一、基礎配置加載

Spring Boot 爲咱們提供了不少的外部配置參數,咱們可使用 YAML 文件(固然你也可使用properties,但不建議)、環境變量和命令行參數,來區分不一樣的環境配置。redis

使用配置有兩種方式:spring

  • 使用註解@Value,來注入Environment 裏面包含的屬性
  • 使用@ConfigurationProperties 來定義一個屬性類,來包含咱們須要的屬性(這些屬性均可以配置在YAML中)

Spring Boot 外部配置這麼多,那若是都配置了哪一個會生效呢?sql

Spring Boot會如下面的順序來加載配置,優先級從高到低(相同配置優先級高的會覆蓋低的),從外到裏的來進行配置覆蓋加載:json

1)開發者全局配置的properties文件(當開發者工具激活時,文件在$HOME/.config/spring-boot下的spring-boot-devtools.properties)微信

2)測試中配置了@TestPropertySource("base.properties") 註解來加載的配置,好比base.properties這種多線程

3)測試中 使用了@SpringBootTest的 properties架構

4)命令行參數配置,也就是java -jar後面使用的配置app

5)可使用SPRING_APPLICATION_JSON 屬性加載的SON配置,加載方式有兩種:

  • 在系統環境變量加載 SPRING_APPLICATION_JSON='{"persion":{"name":"xxx"}}',這種加載會將這個數據加到Spring Environment中,咱們能夠得到一個persion.name 的屬性,值爲xxx
  • 使用System屬性加載 java -Dspring.application.json='{"persion":{"name":"xxx"}}' -jar app.jar,這種加載方式會將spring.application.json屬性的值當作一個String來加載,不會解析。

6)ServletConfig 初始化的配置

7)ServletContext初始化的配置

8)java:comp/env的JNDI特性

9)Java的系統屬性,就是System.getProperties() 獲取到的這些

10)操做系統配置的環境變量

11)在RandomValuePropertySource中配置的以random. 開頭的屬性

12)應用外部配置的 application-{profile}.properties或YAML ,可使用spring.profiles.active 來選擇配置的環境,不選擇默認就是application-default.properties。咱們可使用spring.config.location 來定義文件的路徑進行加載。

13)在你應用內部配置的application-{profile}.properties 或 YAML,也是用於多環境加載選擇使用,能夠用spring.profiles.active 來激活配置

14)應用外部配置的application.properties或 YAML

15)應用內部配置的application-{profile}.properties 或 YAML。

這裏1四、和15 的 SpringApplication 會從application.properties來進行配置屬性的加載。

這個配置會從四個位置按照優先級從高到低的方式覆蓋加載,高優先級覆蓋低優先級的,來看下:

  • 應用外部當前目錄裏 /config 文件夾下的 application.properties 或者application.yml
  • 應用外部當前目錄裏的 application.properties 或者application.yml
  • 應用內部classpath下的/config ,也就是resources/config 目錄下的 application.properties 或者application.yml
  • 應用內部classpath下,也就是resources 目錄下的application.properties 或者application.yml

16)@Configuration 配置類上配置了 @PropertySource註解的,但在spring 上下文刷新前這個配置是不會被加載到Environment裏面的。這種加載方式不能配置那些應用上下文刷新前就須要加載的屬性,好比logging.* 和spring.main.* 這種。

使用方式:

//這裏加載classpath:/com/myco/app.properties文件
@Configuration
 @PropertySource("classpath:/com/myco/app.properties")
 public class AppConfig {

     @Autowired
     Environment env;

     @Bean
     public TestBean testBean() {
         TestBean testBean = new TestBean();
         testBean.setName(env.getProperty("testbean.name"));
         return testBean;
     }
 }

17)SpringApplication.setDefaultProperties 設置的參數

下面來用一個Demo 說下:

import org.springframework.stereotype.*;
import org.springframework.beans.factory.annotation.*;

@Component
public class MyBean {

    @Value("${name}")
    private String name;

    // ...

}
  • 你可使用 classpath 下的application.yml來配置name= laowang
  • 可使用一個外部的application.yml 來設置一個name = laoli 覆蓋上一個配置 (當前name 獲取的話是laoli)
  • 在可使用java -jar app.jar --name="Spring" 在來覆蓋上一個配置 (當前name獲取的話是 Spring)

Spring Boot 配置文件也支持通配符的方式來加載,好比使用 spring.config.additional-location和spring.config.location來加載配置的時候就可使用通配符加載多個文件。

二、配置隨機屬性

隨機屬性的注入實際上是經過RandomValuePropertySource 來實現的,它能夠幫咱們來產生integers、 longs、 uuid、strings 的隨機值。這個對於咱們平時進行一些測試案例仍是很實用的。

//能夠配置在application.yml
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}

三、命令行屬性

咱們可使用 java -jar --server.port=9000的方式將命令行參數server.port 添加到咱們應用的Environment,可使用@Value等方式獲取。正常狀況下命令行添加的屬性優先級是我們優先級高的。

若是你不想將命令行的屬性添加到應用的Environment中,那你能夠配置SpringApplication.setAddCommandLineProperties(false)就好了。

其實使用命令行加載最多的可能就是--javaagent 這個屬性了,對於如今的公司,APM監控那是必不可少的。咱們也能夠經過命令參數的配置來臨時的加載一些屬性進行測試使用。

四、應用加載配置文件

其實上面已經說過了,這裏在從新提一下。SpringApplication 會從application.yml裏面加載屬性配置,並將他們添加到Spring 的Environment中供咱們使用。優先級以下,高優先級覆蓋低的(這裏放個原版,能夠本身嘗試理解下):

  1. A /config subdirectory of the current directory
  2. The current directory
  3. A classpath /config package
  4. The classpath root

若是你不喜歡配置文件叫作application.properties,也可使用spring.config.name來進行配置。也可使用spring.config.location 來指定配置加載路徑。

舉例說明:

//修改個人配置文件叫myproject
java -jar myproject.jar --spring.config.name=myproject
  
  
//換一個地方來加載我得配置文件
java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

由於spring.config.name 和 spring.config.location 配置是用來肯定應用須要加載哪些屬性的,因此須要儘量早的加載。通常都是使用系統環境變量、系統參數、命令行加載的方式進行使用。

默認的配置加載路徑以下,安裝優先級從高到低排序(file:./config/ 優先級最高),因此在使用加載的時候必定要注意:

  1. file:./config/
  2. file:./config/*/
  3. file:./
  4. classpath:/config/
  5. classpath:/

五、佔位符的使用

在application.properties 咱們可使用佔位符來進行屬性的動態加載

好比咱們能夠藉助maven 的profile 在打包的時候動態的對環境參數進行替換(好比替換mysql 、redis等域名)

上幾個例子:

//簡單的使用
app.name=MyApp
app.description=${app.name} is a Spring Boot application
  
//配合命令行參數使用,如參數增長 --port=9000 來代替--server.port=9000,那在配置文件中咱們就能夠配置
server.port=${port:8080}

注意一點:

若是你的POM 裏面集成了spring-boot-starter-parent ,那麼默認的maven-resources-plugins插件會使用@maven.token@來代替${maven.token}。這麼作實際上是爲了防止和Spring的佔位符產生衝突,因此若是咱們使用maven 的profile 或者其餘的來動態替換application.properties 內部的屬性,請使用 @name@.

六、YAML文件進行多環境配置

1) 配置文件使用

在application.yml中,你可使用spring.profiles 來激活你想加載的環境配置內容。

例子:

server:
    address: 192.168.1.100
---
spring:
    profiles: development
server:
    address: 127.0.0.1
---
spring:
    profiles: production & eu-central
server:
    address: 192.168.1.120

在上面的例子中,若是咱們激活development 那server.address 就是127.0.0.1。若是production & eu-central 被激活,那server.address 就是192.168.1.120。若是這三個我都沒激活,那server.address 就是192.168.1.100,環境配置直接使用---來隔離開。

注意:spring.profiles 這個屬性能夠是一個名字,也能夠是一個表達式。

2)@Profile註解使用

咱們不少時候會遇到組件動態選擇的問題,好比我有多種的權限接入方式或者數據源選擇性激活。但我又不想來來回回寫點if else,那麼@Profile就是一個神器了,他的到來使咱們的代碼更加的靈活多變。

好比咱們重寫一個屬於源配置:

//第一個
@Configuration
@Profile("development")
public class StandaloneDataConfig {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .addScript("classpath:com/bank/config/sql/test-data.sql")
            .build();
    }
}

//第二個
@Configuration
@Profile("production")
public class JndiDataConfig {

    @Bean(destroyMethod="")
    public DataSource dataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }
}

這樣,咱們就能夠根據不一樣的配置來激活不一樣的邏輯了,若是再能搭配上遠程配置中心,那就更美麗了。

七、YAML的問題

1) YAML有不少優勢,那必然也是有一丟丟的小毛病的。那就是YAML文件默認不能使用@PropertySource註解來進行配置加載。若是你不想進行多餘的改造,那就老實的建一個properties文件用吧。

2)在YAML中配置多環境配置信息有的時候會有奇奇怪怪的問題,好比下面的:

application-dev.yml

server:
  port: 8000
---
spring:
  profiles: "!test"
  security:
    user:
      password: "secret"

若是此時我啓動應用的時候加載了--spring.profiles.active=dev ,那我正常是應該獲得security.user.password = secret,但真實的狀況卻不是這樣。

由於咱們在配置文件名上使用了xxx-dev.yml,這時候當應用加載的時候就會直接找到application-dev.yml文件.而這時咱們配置文件內的多環境配置就失效了。

因此再多環境配置使用的時候,咱們要否則就選擇xxx-dev.yml、xxx-pro.yml 這種方式,要否則就選擇在一個文件內配置多環境。兩者只能選一個,以避免出現噁心人的問題。

八、對象屬性綁定

有時候咱們會有一組相同類型的屬性須要加載,若是使用@Value 那真是累死人。這裏Spring Boot爲咱們提供了一個便捷的方式,咱們可使用一個類對所須要的變量進行統一的配置加載。

舉個例子:

//屬性類
package com.example;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("acme")
public class AcmeProperties {

    private boolean enabled;

    private InetAddress remoteAddress;

    private final Security security = new Security();

    public boolean isEnabled() { ... }

    public void setEnabled(boolean enabled) { ... }

    public InetAddress getRemoteAddress() { ... }

    public void setRemoteAddress(InetAddress remoteAddress) { ... }

    public Security getSecurity() { ... }

    public static class Security {

        private String username;

        private String password;

        private List<String> roles = new ArrayList<>(Collections.singleton("USER"));

        public String getUsername() { ... }

        public void setUsername(String username) { ... }

        public String getPassword() { ... }

        public void setPassword(String password) { ... }

        public List<String> getRoles() { ... }

        public void setRoles(List<String> roles) { ... }

    }
}

這時我在application.yml中配置以下屬性,Spring Boot就會幫助咱們直接將屬性綁定到AcmeProperties類中

acme.enabled =false

acme.remote-address=127.0.0.1

acme.security.username=xxx

由於 @EnableConfigurationProperties 只是幫助咱們進行聲明,在實際使用上咱們須要配合**@Configuration**,好比下面的配置,這樣配置完後咱們就可使用@Resource 進行屬性注入了。

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}

九、寬鬆的綁定規則

上面聊了@ConfigurationProperties 能夠幫助咱們進行對象屬性綁定,其實在Spring Boot中爲咱們提供了至關寬鬆的綁定規則。

好比context-path綁定到 contextPath屬性,PORT綁定到 port屬性,下面繼續搞個Demo。

@ConfigurationProperties(prefix="acme.my-project.person")
public class OwnerProperties {

    private String firstName;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

}

以上面的代碼爲例,咱們能在application.yml中下面的四種配置形式均可以將屬性綁定到 firstName參數上,厲不厲害。

acme.my-project.person.first-name

acme.myProject.person.firstName

acme.my_project.person.first_name

ACME_MYPROJECT_PERSON_FIRSTNAME

固然,爲了統一不出問題,建議都使用小寫進行屬性聲明如 acme.my-project.person.first-name 。

十、屬性綁定校驗

在@ConfigurationProperties 聲明的屬性類上,咱們能夠增長**@Validated** 來對配置屬性進行校驗。

@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {

    @NotNull
    private InetAddress remoteAddress;

    @Valid
    private final Security security = new Security();

    // ... getters and setters

    public static class Security {

        @NotEmpty
        public String username;

        // ... getters and setters

    }

}

愛生活,愛編碼,微信搜一搜【架構技術專欄】關注這個喜歡分享的地方。本文 架構技術專欄 已收錄,有各類JVM、多線程、源碼視頻、資料以及技術文章等你來拿

在這裏插入圖片描述

相關文章
相關標籤/搜索