『壹』 SpringBoot配置文件存放位置以及讀取順序
默認情況下,我們可以將application.properties或者application.yaml(為了方便演示,本文以下均以application.properties介紹)放置在如下四處:
1.1、idea中,為了我們本地方便開發測試,我們在此處創建一個config目錄,然後把application.properties放進去,項目正常運行。
jar包會自動生成在target目錄下。
我們將生成的jar包,復制出來,到另外文件夾進行運行,比如,我現在該jar包復制到test目錄下,但是這個時候是起不來,因為沒有配置文件,雖然我們在idea裡面是有config目錄的,但是它並不沒有被打包進去。我們要把config目錄也復制過來,跟該jar包放在同一個目錄下。
在此處,我們可以使用java -jar demo-0.01-SNAPSHOT來運行項目。
正常運行。
當我們將其打成jar包時,application.properties同樣不會被打包進jar包中。需要另外復制出來和jar包放在才能正常運行。
推薦以上兩種方式來放置配置文件,如果不寫開發,測試,和生產好幾套環境配置文件的話,就可以直接打開配置文件,改成自己需要的配置即可。
以下兩種方式是將該配置文件打包在jar包裡面了,即便只改一個埠號,開發人員先改配置文件,再打包,再運行。此處也記錄下,並解開jar包,看下該配置文件被打包後,放置的位置。
打包後,如下圖,jar包再target裡面,我們尋找下application.properties文件。為了方便演示,我們將target目錄下的demo-0.0.1-SNAPSHOT.jar放到一個新目錄給它解壓開,找下該配置文件,我放置到了一個test目錄下。
解壓後:如下圖,我們進入目錄
發現config目錄被放置在classes目錄下。然後這也就讓我們明白了,什麼是classpath?classpath的路徑到底指的是哪裡,在idea中我們就把它放置在resource目錄,該目錄就是表示classpath。而被打成jar包後classes目錄就是所謂的classpath。
所有的yaml文件,同理。
『貳』 springboot中獲取apollo或者nacos里的配置文件
常規的,在springboot中一般只需要拿appolo或者nacos里配置的屬性就夠了。
但是也有一些很特殊的場景,要拿到appolo或者nacos里配置的文件,比如有個第三方jar包提供的方法中,要求把properties配置文件路徑傳進去來初始化第三方jar包里需要用到的東西,這時候一般是把properties文件配置到appolo或者nacos里,但是如何直接拿到這個properties文件而不是裡面的屬性值呢?
apollo里直接提供了把配置的相應namespace直接轉換成file的方法:
再把這個content轉換成輸入流就可以用了
如果只是想拿到裡面某個namespace的屬性,則可以:
key為屬性key名,c.getPropertyNames()方法能拿到該namespace下面的所有屬性,返回一個Set<String>集合,再遍歷這個集合就能拿到所有屬性。
nacos跟apollo的處理思路有點不一樣,找了很多資料,貌似沒有找到nacos里直接獲取整個獲取配置文件的方法,後面如果有同學找到了這個方法記得留言提醒我。
nacos在springboot啟動的時候已經把所有配置文件都注入到了spring里。
第一種:可以直接用註解 @Value("${key}")來獲取配置好的屬性值
第二種:在java里獲取:
新建SpringContextUtil實現org.springframework.context.ApplicationContextAware這個介面:
在啟動類用註解導入該類:@Import({SpringContextUtil.class})
利用org.springframework.core.env.Environment類來直接獲取屬性:
如果有這樣一個需求,有個第三方的jar包要求初始化配置好的properties文件,只給了properties文件的路徑傳參,只能用文件路徑的方式初始化這個第三方jar包,那麼我們就必須保證項目里或者其他文件夾有這個properties文件才可以,而這些配置如果經常要變的話,最好也是配置在nacos或者apollo,如此看來,apollo是可以直接把配置的相應namespace直接轉換成file,而nacos大概只能把所有屬性手工生成一個新的properties文件來保存到本地了。
這個生成文件的過程,要在springboot啟動之後立即執行:
那我們就要建一個配置類實現org.springframework.beans.factory.InitializingBean這個介面,重寫afterPropertiesSet()方法:把需要啟動後執行的邏輯放在裡面,下面是一個示例:
把這個類在啟動類里注入:
如此,在啟動的時候就可以在本地生成一個cssconfig.properties文件了。
於是乎就可以類似這樣調用第三方介面(根據第三方jar包來定):
『叄』 SpringBoot有幾種讀取配置的方式
常見的讀取配置的方式有三種:
第一、@Value註解,比較常用的一種方式。也支持與@propertySource註解何用,指定使用的配置文件
第二、@Configuration註解,讀取配置到類中,批量注入配置屬性
第三、Environment對象,獲取配置文件中所有的屬性的對象
如果你想掌握時下熱門微服務技術棧,跟上時代技術步伐,就去黑馬程序員官網視頻庫看免費視頻。
『肆』 二、springboot配置文件
1. 配置文件
Spring Boot使用一個全局的配置文件
application.properties
application.yml
配置文件的作用:修改Spring Boot自動配置的默認值,SpringBoot在底層都給我們自動
配置好。有什麼配置項,可以移步官方文檔
配置文件一般放在src/main/resources目錄或者類路徑/confifig下,當然還有很多位置可
以放,它們會有不同優先順序,後面會講到。
YAML (YAML Ain't Markup Language)
簡單介紹
<!--綁定配置文件處理器,配置文件進行綁定的時候就會有提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- 將應用打包成一個可執行Jar包,直接使用java -jar xxxx的命令來執行 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>以前的配置文件:大多是xml
.yml是YAML語言的文件,以數據為中 心,比json、xml等更適合做配置文件
全局配置文件的可以對一些默認配置值進行修改
配置實例
xml:
yml:
2. YAML語法
基本語法
K:(空格)V 標識一對鍵值對
以空格的縮進來控制層級關系
只要是左對齊的一列數據,都是同一層級的
屬性和值也是大小寫敏感
實例:
值的寫法
普通的值
k: v 字面量直接來寫,字元串默認不用添加單引號
" " 雙引號 不會轉義字元串裡面的特殊字元;
<server>
<port>8081</port>
</server>
server:
port: 8081
server:
port: 8081
path: /hello // 冒號後面的空格不要拉下' ' 單引號 會轉義字元,特殊字元最終是一個普通的字元串
對象
普通寫法:
行內寫法
frends:{ lastName: zhang,age: 18 }
Map
示例:
maps: {k1: v1,k2: v2}
數組
普通寫法:
pets: // var onj = {pets: ['cat','pig','dog']}
- cat
- pig
- dog
行內寫法
pets:[cat, pig, dog]
配置文件獲取
將配置文件中的每一個值映射到此組件中
1. Persion
name: "wang \n qian" // 輸出:wang 換行 qian
frends:
lastName: zhang
age: 20package com.wrq.boot.bean;
@Component
@ConfigurationProperties(prefix = "persion")
public class Persion {
private String name;
private int age;
private double weight;
private boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> list;
private Dog dog;
此處,這個bean的getter、setter和tostring方法已經省略,千萬不能忽略!
}
@ConfifigurationProperties 意思是:我們類裡面的屬性和配置文件中的屬性做綁定
不使用此註解,可以在bean的屬性添加@value()註解,如下:
@Component
// @ConfigurationProperties(prefix = "persion")
public class Persion {
@value("${persion.name}") // $()讀取配置文件、環境變數中的值
private String name;
@value("#{11*2}") // #{SpEL} 採用表達式
private int age;
@value("true") // 直接賦值
private boolean boos;
}
此處採用@ConfifigurationProperties的方式,@value()和@ConfifigurationProperties的
區別見下方表格。prefifix = "persion" 配置文件中那個下面的屬性來一一映射
@Component 如果想要這個註解起作用,必須放到容器裡面
2. Dog
package com.wrq.boot.bean;
public class Dog { // 用作Persion中的屬性
private String name;
private int age;
此處,這個bean的getter、setter和tostring方法已經省略,千萬不能忽略!
}
3. 配置文件
方式一: application.yml
persion:
name: 王大錘
age: 18
weight: 125
boss: false
birth: 2018/5/5
maps: {k1: v1,k2: v2}
list:
- wangli
- wang
dog:
name: xiaogou
age: 2
方式二: application.propertiespersion.name = 王大錘
persion.age = 18
persion.weight = 125
persion.boss = false
persion.birth = 2018/5/5
persion.maps.k1 = v1
persion.maps.k2 = v2
persion.dog.name = xiaogou
persion.dog.age = 15
4. 測試類:BootApplicationTests
package com.wrq.boot;
@RunWith(SpringRunner.class)
@SpringBootTest
public class BootApplicationTests {
@Autowired
Persion persion;
@Test
public void contextLoads() {
System.out.print(persion);
}
}
5. 運行 BootApplicationTests方法
控制台列印:
application.yml的結果:
Persion{name='王大錘', age=18, weight=125.0, boss=false, birth=Sat May
05 00:00:00 CST 2018, maps={k1=v1, k2=v2}, list=[wangli, wang],
dog=Dog{name='xiaogou', age=2}}
application.properties的結果:
Persion{name='��Ǭ', age=18, weight=125.0, boss=false, birth=Sat
May 05 00:00:00 CST 2018, maps={k2=v2, k1=v1}, list=[wangli, wang],
dog=Dog{name='xiaogou', age=15}}
把Bean中的屬性和配置文件綁定,通過yml文件和properties都可以做到,但是properties
文件出現亂碼。
properties中文讀取亂碼:File->Settings->File Encodings最底部選utf-8、Tranparent打
上勾
註解比較
@value和@ConfifigurationProperties獲取值比較
名詞解釋:
鬆散綁定
last-name和lastName都可以獲取導致,則代表支持鬆散綁定
JSR303@Component
@ConfigurationProperties(prefix = "persion") // 如果使用的是@value注入值
時,無法使用校驗
@Validated // 添加此註解
public class Persion {
@Email // 配置文件書寫的屬性必須是郵箱格式,不符合報錯!
private String name;
}
復雜類型封裝
如果獲取配置文件中map的值時,@value是獲取不到值的
@value("${persion.maps}") // 由於使用的是@value,無法獲取配置文件中的map
private Map<String,Object> maps;
@PropertySource
@PropertySource:載入指定配置文件
@ConfifigurationProperties()默認是從全局配置文件中獲取值,也就是
application.properties這個文件中獲取值。
如果做的配置很多,全局的配置文件就會特別大,為了方便管理。我會創建不同的配置文
件定向管理不同的配置。
如創建persion.properties文件單獨存放persion需要的配置
@PropertySource就是用來導入創建的配置文件
示例:
1. persion.properties
同時把兩個全局的配置中關於Persion的配置都注釋掉persion.name = 王弟弟
persion.age = 18
persion.weight = 125
persion.boss = false
persion.birth = 2018/5/5
persion.maps.k1 = v1
persion.maps.k2 = v2
persion.dog.name = xiaogou
persion.dog.age = 15
2. Persion
package com.wrq.boot.bean;
@Component
@PropertySource(value = {"classpath:persion.properties"})
@ConfigurationProperties(prefix = "persion")
public class Persion {
private String name;
private int age;
private double weight;
private boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> list;
private Dog dog;
此處,這個bean的getter、setter和tostring方法已經省略,千萬不能忽略!
}
這樣運行測試類,控制台就可以列印persion.properties中的數據。
通過下面的註解,把類路徑下的persion.properties載入進來。並且把persion開頭的數
據進行綁定。
@PropertySource(value = {"classpath:persion.properties"})@ConfifigurationProperties(prefifix = "persion")
@ImportResource
@ImportResource:導入Spring的配置文件,讓配置文件生效。
示例:
1. com.wrq.boot.service
package com.wrq.boot.service;
/**
* Created by wangqian on 2019/1/12.
*/
public class HelloService {
}
2. resources目錄手動建立bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloService" class="com.wrq.boot.service.HelloService">
</bean>
</beans>
3. 測試類
package com.wrq.boot;
@RunWith(SpringRunner.class)
@SpringBootTest
public class BootApplicationTests {
@Autowired
ApplicationContext ioc;@Test
public void testConfig() {
boolean b = ioc.containsBean("helloService");
System.out.print(b);
}
}
試圖通過添加一個Spring的配置文件bean.xml來把HelloService注入進去。
運行測試類結果:false
結果表明IoC容器中並不包含HelloService,即:配置文件bean.xml沒有生效
解決方式
方式一: 主程序中進行配置@ImportResouece註解
package com.wrq.boot;
@ImportResource(locations = {"classpath:bean.xml"}) // 通過此配置是
bean.xml生效
@SpringBootApplication
public class BootApplication {
public static void main(String[] args) {
//應用啟動起來
SpringApplication.run(BootApplication.class, args);
}
}
方法二:通過配置類實現,這種方式也是Spring Boot推薦的
1. com.wrq.boot.confifigpackage com.wrq.boot.config;
/**
* Created by wangqian on 2019/1/12.
*/
@Configuration
public class MyConfig {
// 將方法的返回值添加到容器之中,並且容器中這個組件的id就是方法名
@Bean
public HelloService helloService(){
System.out.print("通過@Bean給容器添加組件了..");
return new HelloService();
}
}
@Confifiguration標注這是一個配置類
通過@Bean註解,將方法的返回值添加到容器之中,並且容器中這個組件的id就是方
法名
2. 把主程序類中@ImportResource()配置注釋掉
3. 測試成功,添加了HelloService()組件
3. 配置文件佔位符
隨機數
RandomValuePropertySource:配置文件中可以使用隨機數
${random.value}
${random.int}
${random.long}
${random.uuid}
${random.int(10)}
${random.int[1024,65536]}
屬性配置佔位符可以在配置文件中引用前面配置過的屬性(優先順序前面配置過的這里都能用)
${app.name:默認值}來指定找不到屬性時的默認值
persion.name = 王弟弟${random.uuid}
persion.age = ${random.int}
persion.dog.name = ${persion.name}_dog
4. Profifile 多環境支持
Profifile是Spring對不同環境提供不同配置功能的支持,可以通過激活、 指定參數等方式
快速切換環境
1. 多Profifile的方式
格式:application-{profifile}.properties/yml
application-dev.properties
application-prod.properties
默認採用application.properties配置文件,如果使用別的,需要激活:
1. application.properties中配置:
# 激活application-dev.properties配置文件
spring.profiles.active=dev
2. application-dev.properties:
server.port=8082
3. 運行BootApplication主程序:
2019-01-12 20:46:09.345 INFO 14404 --- [main]
s.b.c.e.t. : Tomcat started on port(s):
8082 (http)
2. 多文檔塊的方式
除了上方多Profifile的方式來切換環境,也可以通過YAML多文檔塊的方式。示例:
application.yml:
server:
port: 8081
spring:
profiles:
active: dev
---
spring:
profiles: dev
server:
port: 8083
---
spring:
profiles: prod
server:
port: 8084
3. 激活指定Profifile
1. application.properties中配置:
# 激活application-dev.properties配置文件
spring.profiles.active=dev
2. application.yml中配置
server:
port: 8081
spring:
profiles:
active: dev
---
spring:
profiles: dev
server:
port: 80833. 啟動配置-參數
在IDE中,類似於配置tomcat的地方,按下方配置:
Program arguments:--spring.profiles.active=dev
4. 啟動配置-虛擬機
在IDE中,類似於配置tomcat的地方,按下方配置:
VM options:-Dspring-profiles-active=dev
5. 命令行 使用Maven的package命令打包,移動到jar的目錄。
java -jar spring-boot-project-config.jar --spring.profiles.active=dev
5. 配置文件優先順序
GitHub對應項目:boot-confifig-position
優先順序
Spring Boot 啟動會掃描以下位置的application.properties或者 application.yml文件作
為Spring boot的默認配置文件
fifile:./confifig/ (項目根目錄confifig文件夾下的配置文件)
fifile:./ (項目根目下的配置文件)
classpath:/confifig/ (resources目錄confifig文件夾下的配置文件)
classpath:/ (resources目下的配置文件)
以上是按照優先順序從高到低的順序,所有位置的文件都會被載入,高優先順序配置內容會覆
蓋低優先順序配置內容,形成互補配置。
默認配置
我們也可以通過配置spring.confifig.location來改變默認配置。
項目打包後以後,我們可以使用命令行參數的形式,啟動項目的時候來指定配置文件的新
位置;指定配置文件和默認載入的這些配置文件共同起作用,形成互補配置。
1. Maven->package對項目打包2. 把待使用的配置文件放在本地文件夾中,如:D:/application.properties
3. 命令行執行命令
java -jar boot-config-position-xxxxxx.jar --
spring.config.location=D:/application.properties
這樣即使項目上線了,我們也可以通過修改本地的配置文件,使用一行命令即可,極大方
便了運維人員。
6. 外部配置載入順序
Spring Boot 支持多種外部配置方式
可以從以下位置載入配置,優先順序從高到低,高優先順序配置覆蓋低優先順序的,所以配置形
成互補配置。
1. 命令行參數
java -jar boot-config-position-xxxxxx.jar --server.port // 多個配置用空格
隔開
2. 來自java:comp/env的JNDI屬性
3. Java系統屬性(System.getProperties())
4. 操作系統環境變數
5. RandomValuePropertySource配置的random.*屬性值
6. jar包外部的application-{profifile}.properties或application.yml(帶spring.profifile)配
置文件
7. jar包內部的application-{profifile}.properties或application.yml(帶spring.profifile)配
置文件
8. jar包外部的application.properties或application.yml(不帶spring.profifile)配置文件
9. jar包內部的application.properties或application.yml(不帶spring.profifile)配置文件
10. @Confifiguration註解類上的@PropertySource
11. 通過SpringApplication.setDefaultProperties指定的默認屬性
注意:從jar包外向jar包內尋找,優先載入profifile最後載入不帶profifile,更多參考官方文
檔
7. 自動配置原理GitHub對應項目:boot-confifig-autoconfifig
1. 配置文件寫什麼?
配置文件可配置屬性查閱
2. 什麼是註解,如何實現一個註解?
關於註解的機制和相關原理可以移步此篇博客
3. 配置原理解析
我們運行Spring Boot應用是從main方法啟動,在主程序類上有一個
@SpringBootApplication註解。
@SpringBootApplication是一個復合註解,包括@ComponentScan,和
@SpringBootConfifiguration,@EnableAutoConfifiguration。
@SpringBootConfifiguration繼承自@Confifiguration,二者功能也一致,標注當前類
是配置類,並會將當前類內聲明的一個或多個以@Bean註解標記的方法的實例納入到
srping容器中,並且實例名就是方法名。
@EnableAutoConfifiguration的作用啟動自動的配置,@EnableAutoConfifiguration注
解的意思就是SpringBoot根據你添加的jar包來配置你項目的默認配置,比如根據
spring-boot-starter-web ,來判斷你的項目是否需要添加了webmvc和tomcat,就
會自動的幫你配置web項目中所需要的默認配置
@ComponentScan,掃描當前包及其子包下被@Component,@Controller,
@Service,@Repository註解標記的類並納入到spring容器中進行管理。是以前的co
ntext:component-scan(以前使用在xml中使用的標簽,用來掃描包配置的平行支
持)。
@SpringBootApplication註解分析
配置原理視頻講解
4. 自動配置類判斷
在配置文件properties中設置:debug=true 來讓控制台列印自動配置報告,方便的得知
那些配置類生效。
=========================
AUTO-CONFIGURATION REPORT
=========================Positive matches:
-----------------
matched:
- @ConditionalOnClass found required class
'org.springframework.web.servlet.DispatcherServlet';
@ConditionalOnMissingClass did not find unwanted class
(OnClassCondition)
- @ConditionalOnWebApplication (required) found 'session' scope
(OnWebApplicationCondition)
Negative matches:
-----------------
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required classes
'javax.jms.ConnectionFactory',
'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)
『伍』 SpringBoot 如何優雅讀取配置文件10分鍾教你搞定
很多時候我們需要將一些常用的配置信息比如阿里雲 oss 配置、發送簡訊的相關信息配置等等放到配置文件中。
下面我們來看一下 Spring 為我們提供了哪些方式幫助我們從配置文件中讀取這些配置信息。
application.yml 內容如下:
wuhan2020: 2020年初武漢爆發了新型冠狀病毒,疫情嚴重,但是,我相信一切都會過去!武漢加油!中國加油!my-profile:name: Guide哥email: [email protected]:location: 湖北武漢加油中國加油books: -name: 天才基本法description: 二十二歲的林朝夕在父親確診阿爾茨海默病這天,得知自己暗戀多年的校園男神裴之即將出國深造的消息——對方考取的學校,恰是父親當年為她放棄的那所。 -name: 時間的秩序description: 為什麼我們記得過去,而非未來?時間「流逝」意味著什麼?是我們存在於時間之內,還是時間存在於我們之中?卡洛·羅韋利用詩意的文字,邀請我們思考這一亘古難題——時間的本質。 -name: 了不起的我description: 如何養成一個新習慣?如何讓心智變得更成熟?如何擁有高質量的關系? 如何走出人生的艱難時刻?
1.通過 @value 讀取比較簡單的配置信息
使用 @Value("${property}") 讀取比較簡單的配置信息:
@Value("${wuhan2020}")String wuhan2020;
需要注意的是 @value這種方式是不被推薦的,Spring 比較建議的是下面幾種讀取配置信息的方式。
2.通過@ConfigurationProperties讀取並與 bean 綁定
LibraryProperties 類上加了 @Component 註解,我們可以像使用普通 bean 一樣將其注入到類中使用。
importlombok.Getter;importlombok.Setter;importlombok.ToString;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.context.annotation.Configuration;importorg.springframework.stereotype.Component;importjava.util.List;@Component@ConfigurationProperties(prefix ="library")@Setter@Getter@{privateString location;privateList books;@Setter@Getter@ToStringstaticclassBook{ String name; String description; }}
這個時候你就可以像使用普通 bean 一樣,將其注入到類中使用:
packagecn.javaguide.readconfigproperties;importorg.springframework.beans.factory.InitializingBean;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;/** *@authorshuang.kou */@ntsInitializingBean{privatefinalLibraryProperties library;(LibraryProperties library){this.library = library; }publicstaticvoidmain(String[] args){ SpringApplication.run(.class,args); }@(){ System.out.println(library.getLocation()); System.out.println(library.getBooks()); }}
控制台輸出:
湖北武漢加油中國加油[LibraryProperties.Book(name=天才基本法, description........]
3.通過@ConfigurationProperties讀取並校驗
我們先將application.yml修改為如下內容,明顯看出這不是一個正確的 email 格式:
my-profile:name: Guide哥email: koushuangbwcx@
ProfileProperties 類沒有加 @Component 註解。我們在我們要使用ProfileProperties 的地方使用@
EnableConfigurationProperties注冊我們的配置 bean:
importlombok.Getter;importlombok.Setter;importlombok.ToString;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.stereotype.Component;importorg.springframework.validation.annotation.Validated;importjavax.validation.constraints.Email;importjavax.validation.constraints.NotEmpty;/***@authorshuang.kou*/@Getter@Setter@ToString@ConfigurationProperties("my-profile")@{@NotEmptyprivateString name;@Email@NotEmptyprivateString email;//配置文件中沒有讀取到的話就用默認值privateBooleanhandsome =Boolean.TRUE;}
具體使用:
packagecn.javaguide.readconfigproperties;importorg.springframework.beans.factory.InitializingBean;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.boot.context.properties.EnableConfigurationProperties;/** *@authorshuang.kou */@SpringBootApplication@EnableConfigurationProperties(ProfileProperties.class){privatefinalProfileProperties profileProperties;(ProfileProperties profileProperties){this.profileProperties = profileProperties; }publicstaticvoidmain(String[] args){ SpringApplication.run(.class,args); }@(){ System.out.println(profileProperties.toString()); }}
因為我們的郵箱格式不正確,所以程序運行的時候就報錯,根本運行不起來,保證了數據類型的安全性:
Binding to target org.springframework.boot.context.properties.bind.BindException:Failedtobindpropertiesunder'my-profile'to cn.javaguide.readconfigproperties.ProfileProperties failed:Property:my-profile.emailValue:koushuangbwcx@Origin:classpathresource[application.yml]:5:10Reason:mustbeawell-formedemailaddress
我們把郵箱測試改為正確的之後再運行,控制台就能成功列印出讀取到的信息:
ProfileProperties(name=Guide哥, [email protected], handsome=true)
4.@PropertySource讀取指定 properties 文件
importlombok.Getter;importlombok.Setter;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.PropertySource;importorg.springframework.stereotype.Component;@Component@PropertySource("classpath:website.properties")@Getter@SetterclassWebSite{@Value("${url}")privateString url;}
使用:
@Autowiredprivate WebSite webSite;System.out.println(webSite.getUrl());//https://javaguide.cn/
5.題外話:Spring 載入配置文件的優先順序
Spring 讀取配置文件也是有優先順序的,直接上圖:
原文鏈接:https://www.toutiao.com/a6791445278911103500/?log_from=7f5fb8f9b4b47_1640606437752
『陸』 Springboot 讀取配置文件原理
Springboot 讀取配置文件(application.yaml, application.properties)的過程發生在SpringApplication#prepareEnvironment() 階段,而prepareEnvironment又屬於整個Springboot 應用啟動的非常前置階段,因為Environment的准備是後續bean創建的基礎。讓我們來一探啟動是的詳細code。除去StopWatch這些code,可以發現prepareEnvironment 發生在SpringApplication#run 這在整個應用啟動的多步實質性操作中幾乎是第一步。
而prepareEnvironment中最重要的是通過觸發listener(EventPublishingRunListener)來通過#multicastEvent發出。
而#multicastEvent的實現其實也很簡單,找到相關的監聽的listener,然後一個個的調用他們的Listener#onApplicationEvent(event)方法,而這其中就包括了處理configuration文件的listener。
在Springboot 2.4.0 之前這個處理configuration 文件的lister是ConfigFileApplicationListener,在2.4.0之後,處理configuration 文件的lister是,並且對configuration文件的載入做了較大的改變,導致一些行為可能出現了變化,這也就是下面要詳細講的內容。
Springboot 2.4.0之後,configuration 文件的load順序按照優先順序是如下順序(序號大的會被小的覆蓋):
和之前版本比較,整體的屬性載入順序並無調整,只有Application properties(14,15)這里有順序的調整,具體調整為:
如果存在多個active的profiles,例如[Test, Dev], 那麼對於同時存在兩個profile 配置文件中的配置,後面的profile里的配置(Dev)會覆蓋前面profile(Test)里配置的值。
前面講了這么多,終於要引出Springboot 2.4之後配置文件載入的行為變化了。
考慮這樣的情況,如果我想在跑Springboot test的時候指定特定的profile,那麼可以在Test class中加入@ActiveProfile("Test")。 如果我的應用中存在的某個自定義listener中,會根據當前environment 設置profile,如env.addActiveProfile("Dev")。
當前就會有兩個active profile,由於springboot-test會在調用application#run 前利用DefaultActiveProfilesResolver把@ActiveProfile註解定義的profile(Test)先加入了active的profile,等test run的時候 env.addActiveProfile("Dev") 又會把"Dev"也作為active profile 加入,這時候當前的active profile便為["Test", "Dev"]。
據上面介紹,後面的profile(Dev)對應的configuration 會覆蓋前面的(Test)。可Springboot 2.4.0之前的版本為我們做了調整,讓Test class中@ActiveProfile內定義的profile所對應的配置文件成為最高優先順序。
剛才提到在Springboot 2.4.0 之前這個處理configuration 文件的lister是ConfigFileApplicationListener,我們
來看看ConfigFileApplicationListener的相關code。
查看initializeProfiles(),發現此時對profile的順序做了調整,將activatedViaProperty (Test) 放在最後add,於是profile的順序就變成了[Dev, Test]。
在profiles.poll()時原本profile的順序已經倒了過來,已經變為[Dev, Test], 在load()方法中由於後置的Test profile,application-Test.yaml中的值最終生效了。
可是到了Springboot2.4.0之後,ConfigFileApplicationListener被deprecated了,取而代之的是,通過調用來完成configuration載入。
.java
.java
只是老老實實的set了active profile,並沒有調換profile的順序。最後調用定義在spring.factories中的resource loader class來load 配置文件。
YamlPropertySourceLoader.java
插一句,Springboot為我們提供了很好的yaml文件parse的code,當你需要解析yaml文件時不妨直接參考Springboot的YamlPropertySourceLoader
這樣一旦應用升級到Springboot 2.4.0之後相同的test code會使用application-Dev.yaml中配置的值,造成了test結果的改變。
如果要解決這個問題,根據上面介紹的配置文件優先順序順序,可以在@SpringbootTest中設置properties 來作為最終的配置覆蓋當前profile對應的配置。
了解一個框架很不容易,一個小小的變化都有可能造成應用的行為變化,唯有刨根問底,不斷總結才是framework人解決一切問題的不變的方法論。