SpringBoot 配置的加載


SpringBoot 配置的加載

SpringBoot配置及環境變量的加載提供許多便利的方式,接下來一起來學習一下吧!

本章內容的源碼按實戰過程采用小步提交,可以按提交的節點一步一步來學習,倉庫地址:https://github.com/zhouweixin/spring-boot-configuration。

本文將要介紹

  1. SpringBoot配置的加載
  2. SrpingBoot環境變量的加載
  3. 測試時動態設置配置
  4. 啟動時動態設置配置

環境:

  • java: 1.8.0_265
  • gradle: 6.6.1

1 准備

用你喜歡的方式創建一個SpringBoot工程,並寫一個hello的接口,及相應的集成測試,進行實驗吧!

1.1 hello接口代碼

HelloController.java

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "Hello world!";
    }
}

1.2 hello集成測試代碼

HelloControllerTest.java

@SpringBootTest
@AutoConfigureMockMvc
class HelloControllerTest {
    @Autowired
    MockMvc mockMvc;

    @Test
    void hello() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("http://localhost:8080/hello"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.is("Hello world!")));
    }
}

2 注解@Value

2.1 介紹

@Value通過直接注解在屬性上為屬性設置

如下所示,為name設置值為tangseng

HelloController.java

@Value("tangseng")
private String name;

2.2 加載配置文件

當然,上面的寫法不涉及配置文件的讀取,但是從配置文件加載數據也是同樣簡單

如下所示,在${}中用json的方式設置配置文件中設置的key值

HelloController.java

@Value("${value.string}")
private String valueString;

配置文件的內容為

application.properties

value.string=sunwukong

2.3 數據類型轉換

當然,@Value的功能還遠不止於此,它可以實現數據類型的轉換

即,在配置文件中配置的所有內容是沒有數據類型的,@Value會根據屬性的類型,實現自動轉換

如下所示,基本數據類型@Value注解都是可以正確轉換的,使用起來有沒有感覺很簡單呢?

application.properties

value.int=1
value.float=1.11
value.string=sunwukong
value.bool=true

HelloController.java

@Value("${value.int}")
private int valueInt;

@Value("${value.float}")
private float valueFloat;

@Value("${value.string}")
private String valueString;

@Value("${value.bool}")
private boolean valueBool;

2.4 默認值

寫到這里,你肯定認為@Value注解的功能就結束了

然而,並沒有,@Value還可以設置默認值

即,假如配置文件中沒有配置該屬性,也可以有默認值兜底的

默認值的設置格式如下所示

HelloController.java

@Value("${value.double:2.22}")
private double valueDouble;

2.5 時間轉換

這次,你一定又一次認為@Value的學習結束了,但是想再分享@Value對時間的處理,因為實際項目中經常會配置超時時間等類似的時間,比較實用

假如配置文件里配置了timeout=60,你認為是60s呢還是60ms,或是60m,是不是有點不清楚呢?

因此,多是配置成timeout=60s, 利用@DurationUnit進行單位的轉換

還是看個例子比較直觀些

首先配置一個10分鍾

application.properties

value.time=10m

然后用去解析,看看結果是否正確,這里悄悄告訴你,結果依然是正確的,轉成了600s

HelloController.java

@Value("${value.time}")
@DurationUnit(ChronoUnit.SECONDS)
private Duration time;

2.6 集成測試

接下來,寫個接口及集成測試,測試一下結果

HelloController.java

@GetMapping("/helloValue")
public Object helloValue() {
    Map<String, Object> map = new HashMap<>();
    map.put("name", name);
    map.put("valueInt", valueInt);
    map.put("valueFloat", valueFloat);
    map.put("valueString", valueString);
    map.put("valueBool", valueBool);
    map.put("valueDouble", valueDouble);
    return map;
}

HelloControllerTest.java

@Test
void helloValue() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("http://localhost:8080/helloValue"))
            .andExpect(MockMvcResultMatchers.status().isOk())
            .andExpect(MockMvcResultMatchers.jsonPath("$.name", Matchers.is("tangseng")))
            .andExpect(MockMvcResultMatchers.jsonPath("$.valueInt", Matchers.is(1)))
            .andExpect(MockMvcResultMatchers.jsonPath("$.valueFloat", Matchers.is(1.11)))
            .andExpect(MockMvcResultMatchers.jsonPath("$.valueString", Matchers.is("sunwukong")))
            .andExpect(MockMvcResultMatchers.jsonPath("$.valueBool", Matchers.is(true)))
            .andExpect(MockMvcResultMatchers.jsonPath("$.valueDouble", Matchers.is(2.22)))
            .andExpect(MockMvcResultMatchers.jsonPath("$.time", Matchers.is("600s")));
}

當然也可以用請求查看一下結果

$ curl http://localhost:8080/helloValue
{"valueString":"sunwukong","name":"tangseng","valueDouble":2.22,"time":"600s","valueInt":1,"valueFloat":1.11,"valueBool":true}

3 注解@ConfigurationProperties

3.1 介紹

@ConfigurationProperties實現了更加豐富的功能,但是該屬性需要配置@ConfigurationPropertiesScan使用

即,首先需要將@ConfigurationPropertiesScan注解到啟動類上,即XxxApplication.java

然后便可以利用@ConfigurationProperties上

@ConfigurationProperties是用來注解類上,用來批量從配置文件中加載數據

比如,配置中有如下屬性

application.properties

student.name=xiaoming
student.email=123456@qq.com
student.age=18

便可以定義Student類,並將@ConfigurationProperties注解其上

注意:屬性名需要和配置文件里對應的名字相同,你肯定觀察到了

Student.java

@ConfigurationProperties("student")
public class Student {
    private String name;
    private String email;
    private int age;
    
    // ... 省略setter, getter方法, setter方法必須有
}

3.2 加載集合數據

@ConfigurationProperties除了可以讀單值數據,也可以讀List和Map數據

比如,配置文件里有如下配置

application.properties

# class.list
student.friends[0]=zhubajie
student.friends[1]=shaheshang

# class.map
student.parent.father=tangseng
student.parent.mother=nverguoguowang

只需要在Student類中再添加兩個屬性即可,不要忘記setter和getter方法喲

Student.java

private List<String> friends;
private Map<String, String> parent;

添加getStudent接口

HelloController.java

@GetMapping("/getStudent")
public Student getStudent() {
    return student;
}

3.3 集成測試

HelloControllerTest.java

@Test
void getStudent() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("http://localhost:8080/getStudent"))
            .andExpect(MockMvcResultMatchers.status().isOk())
            .andExpect(MockMvcResultMatchers.jsonPath("$.name", Matchers.is("xiaoming")))
            .andExpect(MockMvcResultMatchers.jsonPath("$.email", Matchers.is("123456@qq.com")))
            .andExpect(MockMvcResultMatchers.jsonPath("$.age", Matchers.is(18)))
            .andExpect(MockMvcResultMatchers.jsonPath("$.friends[0]", Matchers.is("zhubajie")))
            .andExpect(MockMvcResultMatchers.jsonPath("$.friends[1]", Matchers.is("shaheshang")))
            .andExpect(MockMvcResultMatchers.jsonPath("$.parent.father", Matchers.is("tangseng")))
            .andExpect(MockMvcResultMatchers.jsonPath("$.parent.mother", Matchers.is("nverguoguowang")));
}

直接求觀測也是可以的

$ curl http://localhost:8080/getStudent
{"name":"xiaoming","email":"123456@qq.com","age":18,"friends":["zhubajie","shaheshang"],"parent":{"father":"tangseng","mother":"nverguoguowang"}}

4 環境變量Environment

4.1 介紹

在實際項目場景中,環境變量也是一種動態配置的有效方案

比如,在本地加載local的環境變量,在dev加載dev的環境變量

SpringBoot對於環境變量的加載比較簡單,只需要Environment類即可,但是需要注意該類的包,不要導錯了

import org.springframework.core.env.Environment;

使用過程,首先注入

HelloController.java

@Autowired
private Environment env;

然后你便可以通過getProperty獲取環境變量了

result.put("JAVA_HOME", env.getProperty("JAVA_HOME"));
result.put("GRADLE_HOME", env.getProperty("GRADLE_HOME"));
result.put("NO_ENV", env.getProperty("NO_ENV", "no env variable"));

是不是特別簡單呢,其實就是這么簡單,下面就不多驗證了

5 測試動態指定配置

5.1 介紹

下面介紹測試時動態修改配置

你在項目中是不是經常碰到集成測試和運行屬性不一樣的值的,本章就是解決該問題的

接下來介紹3種方法

5.2 注解@ActiveProfiles

該注解的使用比較簡單,可以動態指定加載的配置文件

首先,復制一份application.properties,命名為application-dev.properties

修改一部分值

application-dev.properties

# simple value
value.int=2
value.float=2.22
value.string=zhubajie
value.bool=false
value.time=1s

接着,復制一份HelloControllerTest.java,命名為ActiveProfilesTest.java

在類上添加注解@ActiveProfiles("local"),就完成了該測試類動態加載配置文件的目的

ActiveProfilesTest.java

@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("local")
class ActiveProfilesTest {
    @Autowired
    MockMvc mockMvc;

    @Test
    void helloValue() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("http://localhost:8080/helloValue"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.valueInt", Matchers.is(2)))
                .andExpect(MockMvcResultMatchers.jsonPath("$.valueFloat", Matchers.is(2.22)))
                .andExpect(MockMvcResultMatchers.jsonPath("$.valueString", Matchers.is("zhubajie")))
                .andExpect(MockMvcResultMatchers.jsonPath("$.valueBool", Matchers.is(false)));
    }
}

該注解有個缺陷,指定配置文件的命名必須是固定的格式application-xxx.properties

而對於外部的配置文件或者其它格式命名的卻無法加載

5.3 注解@TestPropertySource

屬性locations

該注解恰好是用來解決@ActiveProfiles的不足的

其locations屬性可以加載任意的配置文件,只要存在

示例

TestPropertyResourceTest.java

@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(locations = {"/a.properties"})
class TestPropertyResourceTest {
    @Autowired
    MockMvc mockMvc;

    @Test
    void helloValue() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("http://localhost:8080/helloValue"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.valueInt", Matchers.is(3)))
                .andExpect(MockMvcResultMatchers.jsonPath("$.valueFloat", Matchers.is(3.33)))
                .andExpect(MockMvcResultMatchers.jsonPath("$.valueString", Matchers.is("shaheshang")))
                .andExpect(MockMvcResultMatchers.jsonPath("$.valueBool", Matchers.is(true)));
    }
}

a.properties

# simple value
value.int=3
value.float=3.33
value.string=shaheshang
value.bool=true

該屬性的使用就是這么簡單

然而,當僅需要指定很少屬性的時候,通過指定配置文件的方式顯得過於笨重

屬性properties

locations屬性對於指定部分屬性就很直接了,而且其優先級高於屬性locations

示例

TestPropertyResource2Test.java

@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(properties = {"value.int=10", "value.float=10.10"}, locations = {"/a.properties"})
class TestPropertyResource2Test {
    @Autowired
    MockMvc mockMvc;

    @Test
    void helloValue() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("http://localhost:8080/helloValue"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.valueInt", Matchers.is(10)))
                .andExpect(MockMvcResultMatchers.jsonPath("$.valueFloat", Matchers.is(10.10)))
                .andExpect(MockMvcResultMatchers.jsonPath("$.valueString", Matchers.is("shaheshang")))
                .andExpect(MockMvcResultMatchers.jsonPath("$.valueBool", Matchers.is(true)));
    }
}

6 啟動時動態指定配置

你肯定了解過spring啟動的方法

  1. 通過開發工具(idea, eclipse)啟動
  2. java命令啟動
  3. gradle命令啟動
  4. maven命令啟動
  5. 。。。

通過開發工具啟動指定配置相對比較簡單,如下圖中的紅框所示,可以方便的設置

  1. Environment variables:環境變量
  2. Active profiles:配置文件
  3. Override parameters:參數

6.1 通過idea設置

![image-20200921215130704](/Users/zhouweixin/Library/Application Support/typora-user-images/image-20200921215130704.png)

設置完后,通過瀏覽器訪問接口http://localhost:8080/helloValue的結果如下

{
    "NO_ENV": "no env variable",
    "valueString": "sunwukong",
    "name": "tangseng",
    "JAVA_HOME": "test_java_home",
    "valueDouble": 2.22,
    "time": "600s",
    "valueInt": 1111,
    "valueFloat": 1.11,
    "valueBool": true,
    "GRADLE_HOME": "/Users/zhouweixin/.sdkman/candidates/gradle/current"
}

通常開發完成后會部署到linux系統中,這時通過命令指定配置則尤其顯得重要

6.2 通過java命令設置

在學習之前先學會打包

  • 用maven工程的可以用mvn package打包

  • 用gradle工程的可以用gradle bootJar打包

筆者采用的gradle打包,生成文件:build/libs/configuration-0.0.1-SNAPSHOT.jar

$ gradle bootJar            

BUILD SUCCESSFUL in 676ms
3 actionable tasks: 1 executed, 2 up-to-date

該jar文件拷貝到任何機器上都可以運行,前提是該機器安裝了jre,啟動命令也比較簡單

java -jar build/libs/configuration-0.0.1-SNAPSHOT.jar

通過瀏覽器訪問接口http://localhost:8080/helloValue的結果如下,加載的是application.properties

{
    "NO_ENV": "no env variable",
    "valueString": "sunwukong",
    "name": "tangseng",
    "JAVA_HOME": "/Users/zhouweixin/.sdkman/candidates/java/current",
    "valueDouble": 2.22,
    "time": "600s",
    "valueInt": 1,
    "valueFloat": 1.11,
    "valueBool": true,
    "GRADLE_HOME": "/Users/zhouweixin/.sdkman/candidates/gradle/current"
}

指定application-local.properties也比較容易

java -jar build/libs/configuration-0.0.1-SNAPSHOT.jar --spring.profiles.active=local

僅設置部分屬性的方式

java -jar build/libs/configuration-0.0.1-SNAPSHOT.jar --value.int=1000 --value.string=zhangsanfeng

總結

本文粗略地記錄了常用的

  1. SpringBoot配置的加載
  2. SrpingBoot環境變量的加載
  3. 測試時動態設置配置
  4. 啟動時動態設置配置

本文僅為了拋磚引玉,好學的你一起來努力吧!

錯誤及不全面的地方多謝批評指正!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM