往期文章回顧: Spring Boot 第一彈,問候一下世界=咸场S≌帷扒最!
前言
?上彈說到
如何使用Spring Boot問候一下世界
丑勤,想必大家都對Spring Boot
已經(jīng)有一定的掌握了。如果還沒看的吧趣,沒關(guān)系法竞,可以點擊上面往期回顧鏈接前去學(xué)習
耙厚。
?今天我們一起來學(xué)習Spring Boot第二彈
,玩轉(zhuǎn)Spring Boot配置文件
岔霸。
?說起Spring Boot的配置文件
薛躬,真的是愛恨交加
,相對于之前Spring大量的配置文件呆细,現(xiàn)在的Spring Boot簡直簡直簡直型宝。。絮爷。,怎一個爽字了得
趴酣。當然了,爽的同時坑夯,也迎來了不少困擾岖寞,比如:我們對于Spring Boot是如何實現(xiàn)
的只需要修改配置文件就能達到一定效果也是充滿了好奇,這個就需要我們?nèi)ラ喿xSpring Boot的源碼-自動裝配原理
了柜蜈,仗谆。這里我就不再贅述了,后面我會出專門針對源碼進行分析的文章
,敬請期待吧!!!
話不多說淑履,開搞!!!
Spring Boot配置文件格式
?Spring Boot 官方
提供了兩種常用的配置文件格式隶垮,分別是properties
、YML
格式秘噪。相比于properties
來說岁疼,YML
更加年輕,層級也是更加分明缆娃。強烈推薦使用YML
格式
Spring Boot配置文件優(yōu)先級加載機制
?Spring Boot項目
啟動會掃描以下位置的application.properties
或者application.yml
作為默認的配置文件.
file:./config/
file:./config/*/
file:./
classpath:/config/
classpath:/
?加載的優(yōu)先級順序是
從上向下加載
捷绒,并且所有的文件都會被加載
,高優(yōu)先級的內(nèi)容
會覆蓋低優(yōu)先級的內(nèi)容
贯要,形成互補配置
徒手撕源碼
?我們可以從
ConfigFileApplicationListener
這個類中找到暖侨,其中DEFAULT_SEARCH_LOCATIONS屬性設(shè)置了加載的目錄:
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/";
private static final String DEFAULT_NAMES = "application";
private static final Set<String> NO_SEARCH_NAMES = Collections.singleton((Object)null);
private static final Bindable<String[]> STRING_ARRAY = Bindable.of(String[].class);
private static final Bindable<List<String>> STRING_LIST = Bindable.listOf(String.class);
private static final Set<String> LOAD_FILTERED_PROPERTY;
public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";
...
...
...
}
?然后在
ConfigFileApplicationListener
類中的getSearchLocations
方法中去逗號解析成Set,其中內(nèi)部類Loader負責這一配置文件的加載過程
崇渗,包括加載profile指定環(huán)境的配置
字逗,以application+’-’+name格式的拼接加載。
內(nèi)部類Loader的load方法
private void load(ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory, ConfigFileApplicationListener.DocumentConsumer consumer) {
this.getSearchLocations().forEach((location) -> {
String nonOptionalLocation = ConfigDataLocation.of(location).getValue();
boolean isDirectory = location.endsWith("/");
Set<String> names = isDirectory ? this.getSearchNames() : ConfigFileApplicationListener.NO_SEARCH_NAMES;
names.forEach((name) -> {
this.load(nonOptionalLocation, name, profile, filterFactory, consumer);
});
});
}
getSearchLocations()方法
private Set<String> getSearchLocations() {
Set<String> locations = this.getSearchLocations("spring.config.additional-location");
if (this.environment.containsProperty("spring.config.location")) {
locations.addAll(this.getSearchLocations("spring.config.location"));
} else {
locations.addAll(this.asResolvedSet(ConfigFileApplicationListener.this.searchLocations, "classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/"));
}
return locations;
}
asResolvedSet()
private Set<String> asResolvedSet(String value, String fallback) {
List<String> list = Arrays.asList(StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(value != null ? this.environment.resolvePlaceholders(value) : fallback)));
Collections.reverse(list);
return new LinkedHashSet(list);
}
?其實可以看出源碼里面給出的
配置文件排列順序跟加載順序是相反的
宅广。而是使用了Collections.reverse(list)
方法
- 我們也可以通過指定配置
spring.config.location
來改變默認配置葫掉,一般在項目已經(jīng)打包后,我們可以通過以下指令來加載外部的配置
跟狱。
java -jar XXX-0.0.1-SNAPSHOT.jar --spring.config.location=F:/application.yml
- 另外也可以通過
命令行參數(shù)進行配置
???所有的配置
都可以在命令行
上進行指定
???多個配置用空格分開俭厚;--配置項=值
java -jar XXX-0.0.1-SNAPSHOT.jar
--server.port=8888 --server.context-path=/qlh
下面給出優(yōu)先級從高到低
的配置文件排列順序:
- 命令行參數(shù)
- 來自java:comp/env的JNDI屬性
- Java系統(tǒng)屬性(System.getProperties())
- 操作系統(tǒng)環(huán)境變量
- RandomValuePropertySource配置的random.*屬性值
備注:由jar包外向jar包內(nèi)進行尋找,優(yōu)先加載帶profile的驶臊,再加載不帶profile的
- jar包外部的application-{profile}.properties或application.yml(帶spring.profile)配- 置文件
- jar包內(nèi)部的application-{profile}.properties或application.yml(帶spring.profile)配置文件
- jar包外部的application.properties或application.yml(不帶spring.profile)配置文件
- jar包內(nèi)部的application.properties或application.yml(不帶spring.profile)配置文件
- @Configuration注解類上的@PropertySource
- 通過SpringApplication.setDefaultProperties指定的默認屬性
properties挪挤、YAML配置優(yōu)先級加載機制
?
Spring Boot
使用一個以application命名的配置文件作為默認的全局配置文件
叼丑。支持properties后綴結(jié)尾
的配置文件或者以yml/yaml后綴結(jié)尾
的YAML的文件配置。
以設(shè)置應(yīng)用端口為例初體驗Spring Boot配置文件
properties后綴結(jié)尾(application.properties)
server.port=80
yml/yaml后綴結(jié)尾(application.yml/application.yaml)
server:
prot: 8088
注:同一目錄下扛门,properties配置文件優(yōu)先級 > yml/yaml配置文件優(yōu)先級鸠信。因此在
jar包啟動時
如果帶上properties寫法的配置
可以覆蓋配置。
yaml/yml配置文件寫法冒號后要加空格
properties配置文件
語法結(jié)構(gòu)為:
key=value
值類型:
數(shù)字论寨,字符串星立,布爾,日期
person.name=tinygrey
person.age=18
person.status=true
person.birthday=2020/11/23
對象葬凳、Map
#Map
person.assets.phone=iphone 12
person.assets.car=捷豹
#對象
person.dog.name=奶狗
person.dog.age=3
數(shù)組
person.hobby[0]=打籃球
person.hobby[1]=睡覺
person.hobby[2]=玩游戲
person.hobby[3]=學(xué)習
yml/yaml配置文件
?以空格的
縮進程度來控制層級關(guān)系
贞铣。空格的個數(shù)并不重要沮明,只要左邊空格對齊則視為同一個層級
辕坝。注意不能用tab
代替空格。且大小寫敏感
荐健。支持字面值酱畅,對象,數(shù)組
三種數(shù)據(jù)結(jié)構(gòu)江场,也支持復(fù)合 結(jié)構(gòu)
纺酸。
?
字面值
:字符串
,布爾類型
址否,數(shù)值
餐蔬,日期
。字符串默認不加引號佑附,單引號會轉(zhuǎn)義特殊字符樊诺。日期格式支持yyyy/MM/dd HH:mm:ss
?對象
:由鍵值對
組成,形如key:(空格)value
的數(shù)據(jù)組成音同。冒號后面的空格是必須要有的词爬,每組鍵值對占用一行,且縮進的程度要一致权均,也可以使用行內(nèi)寫法
:{k1: v1, ....kn: vn}
?數(shù)組
:由形如-(空格)value
的數(shù)據(jù)組成顿膨。短橫線后面的空格是必須要有的,每組數(shù)據(jù)占用一行叽赊,且縮進的程度要一致恋沃,也可以使用行內(nèi)寫法:[1,2,...n]
復(fù)合結(jié)構(gòu)
:上面三種數(shù)據(jù)結(jié)構(gòu)任意組合
值類型:
數(shù)字,字符串必指,布爾囊咏,日期
person:
name: tinygrey
age: 18
status: true
birthday: 2002/04/02
對象、Map
person:
#Map
assets:
phone: iphone 12
car: 捷豹
#對象
dog:
name: 奶狗
age: 3
#行內(nèi)寫法
person:
#Map
assets: {phone: iphone 12,car: 捷豹}
#對象
dog: {name: 奶狗,age: 3}
數(shù)組
person:
hobby:
- 打籃球
- 睡覺
- 玩游戲
- 學(xué)習
#行內(nèi)寫法
person:
hobby: [打籃球,睡覺,玩游戲,學(xué)習]
注:
YML是一種新式的格式
,層級鮮明匆笤,強烈推薦使用
研侣。
注意如下:
:
?字符串
可以不加引號谱邪,若加雙引號則輸出特殊字符
炮捧,若不加或加單引號則轉(zhuǎn)義特殊字符
?數(shù)組類型
,短橫線后面要有空格惦银;對象類型
咆课,冒號后面要有空格
?yaml/yml
是以空格縮進的程度來控制層級關(guān)系,但不能用tab鍵代替空格
扯俱,大小寫敏感
本文所有提到的配置文件都對應(yīng)下面實體類做為參考
@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component
@PropertySource(value = "classpath:config/custom-profile.properties", encoding = "utf-8")
@ConfigurationProperties(prefix = "person")
public class Person {
@Value("${person.name}")
private String name;
@Value("${person.age}")
private int age;
private boolean status;
@Value("${person.birthday}")
private Date birthday;
private List<String> hobby;
private Map<String, Object> assets;
private Dog dog;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component
@PropertySource(value = "classpath:config/custom-profile.properties", encoding = "utf-8")
@ConfigurationProperties(prefix = "dog")
class Dog {
private String name;
private Integer age;
}
配置文件如何跟實體類綁定
Spring Boot
一切的配置都是為了取值书蚪,Spring Boot提供了一些取值的方式。我們一起來看一下迅栅。
@ConfigurationProperties(prefix = "person")詳解
注: 該注解用于從配置文件中取值殊校,支持復(fù)雜的數(shù)據(jù)類型,但是不支持
SPEL表達式
登澜。
prefix屬性:
指定獲配置的前綴眠菇,畢竟配置文件中的屬性很多烫映,也有很多重名的,必須用一個前綴來區(qū)分下敬察。
該注解可以標注在類上
也可以標注在方法上
,這里就可以看出它有兩種獲取值的方式
尔当。
標注在類上
@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component //注入到IOC容器中
@ConfigurationProperties(prefix = "person")//從配置文件中讀取文件
public class Person {
@Value("${person.name}")
private String name;
@Value("${person.age}")
private int age;
@Value("${person.birthday}")
private Date birthday;
private List<String> hobby;
private Map<String, Object> assets;
private Dog dog;
}
標注在方法上
/**
* @Bean 將返回的結(jié)果注入到IOC容器中
* @ConfigurationProperties 從配置文件中取值
* @return
*/
@ConfigurationProperties(prefix = "person")
@Bean
public Person person(){
return new Person();
}
綜上所述
?@ConfigurationProperties
注解能夠輕松的讓配置文件跟實體類綁定在一起莲祸。
具有以下優(yōu)點:
- 注入屬性支持批量,僅僅指定一個
前綴prefix
即可
- 數(shù)據(jù)類型支持復(fù)雜數(shù)據(jù)椭迎,比如
List锐帜、Map
- 屬性名匹配規(guī)則-
松散綁定
,比如a-b
畜号,a_b
抹估,aB
,A_B
都可以取值
- 支持JAVA的JSR303數(shù)據(jù)校驗
?值得關(guān)注的是:@ConfigurationProperties
這個注解僅僅是支持從Spring Boot的默認配置文件
中取值弄兜,也就是application.properties
药蜻、application.yml
、application.yaml
替饿,那我們?nèi)绾螐淖远x配置文件取值呢语泽??视卢?
?別著急踱卵,有解決辦法,那就是再加一個注解:@PropertySource(value = "classpath:custom-profile.properties")
,下面會有對@PropertySource
注解的介紹。請耐心往下面看惋砂。
@Value
?@Value這個注解我們應(yīng)該都比較熟悉了妒挎,Spring中從屬性取值的注解,支持SPEL表達式西饵,不支持復(fù)雜的數(shù)據(jù)類型酝掩,比如Map、List眷柔。使用可以參考上面實體類里面的代碼期虾。
自定義配置文件并取值
?
Spring Boot
在啟動的時候會自動加載application.xxx
,但是有的時候為了避免application.xxx配置文件過于臃腫
,就需要我們自定義配置文件
驯嘱,那么自定義配置文件的話镶苞,我們?nèi)绾螐淖远x配置文件里面取值呢?這時候就需要配合@PropertySource這個注解
使用了鞠评。
使用@PropertySource注解
?在配置類上標注@PropertySource并指定你自定義的配置文件即可茂蚓。可以參考下面代碼
@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component
@PropertySource(value = {"classpath:config/custom-profile.properties"}, encoding = "utf-8")
@ConfigurationProperties(prefix = "person")
public class Person {
@Value("${person.name}")
private String name;
@Value("${person.age}")
private int age;
@Value("${person.birthday}")
private Date birthday;
private List<String> hobby;
private Map<String, Object> assets;
private Dog dog;
}
對應(yīng)配置文件
person.name=tinygrey
person.age=18
person.birthday=2020/11/23
person.hobby[0]=打籃球
person.hobby[1]=睡覺
person.hobby[2]=玩游戲
person.hobby[3]=學(xué)習
person.assets.phone=iphone 12
person.assets.car=捷豹
person.dog.name=奶狗
person.dog.age=3
@PropertySource注解屬性
value:是一個
數(shù)組剃幌,可以指定多個配置文件
同時引入聋涨。
??value是數(shù)組那么問題就來了:如果同時加載多個配置文件
,并且不同配置文件
中對同一個屬性
設(shè)置了不同的值
锥忿,那么Spring會識別哪一個呢?
創(chuàng)建兩個配置文件custom-profile.yml牛郑、custom-profile1.yml
,如下去引入敬鬓。
@PropertySource(value = {"classpath:config/custom-profile.yml","classpath:config/custom-profile1.yml"})
public class Person {
...
}
我們可以通過控制變量法進行測試淹朋,具體過程我這里就不贅述了。
直接說結(jié)論
吧:Spring加載順序
為從左到右順序加載
钉答,后加載的會覆蓋
先加載的屬性值础芍。
另外需要注意的是:@PropertySource
默認加載xxx.properties類型
的配置文件,不能加載YML格式
的配置文件数尿。如何解決呢仑性?下面來解決這一問題
加載自定義YML格式的配置文件
?
@PropertySource
注解有一個屬性factory
,默認值是PropertySourceFactory.class
右蹦,這個就是用來加載properties格式
的配置文件诊杆,那我們自定義一個用來加載YML格式
的配置文件不就可以了嘛?上代碼
/**
* 解決@PropertySource只對properties文件可以進行加載何陆,但對于yml或者yaml不能支持晨汹。
*/
public class YmlPropertySourceFactory extends DefaultPropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
String sourceName = name != null ? name : resource.getResource().getFilename();
if (!resource.getResource().exists()) {
assert sourceName != null;
return new PropertiesPropertySource(sourceName, new Properties());
} else if (Objects.requireNonNull(sourceName).endsWith(".yml") || sourceName.endsWith(".yaml")) {
Properties propertiesFromYaml = loadYamlProperties(resource);
return new PropertiesPropertySource(sourceName, propertiesFromYaml);
} else {
return super.createPropertySource(name, resource);
}
}
private Properties loadYamlProperties(EncodedResource resource){
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
factory.afterPropertiesSet();
return factory.getObject();
}
}
?我們寫好上面的代碼之后,只需要添加
@PropertySource
注解中factory屬性指定為YmlPropertySourceFactory
即可,代碼如下:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
@Component
@PropertySource(value = {"classpath:config/custom-profile.yml"}, encoding = "utf-8", factory = YmlPropertySourceFactory.class)
@ConfigurationProperties(prefix = "person")
public class Person {
@Value("${person.name}")
private String name;
@Value("${person.age}")
private int age;
@Value("${person.birthday}")
private Date birthday;
private List<String> hobby;
private Map<String, Object> assets;
private Dog dog;
}
對應(yīng)配置文件:
person:
name: qlh
age: 22
birthday: 2012/04/02
hobby:
- 打
- 睡
- 玩
- 學(xué)
assets:
phone: iphone
car: 福特野馬
dog:
name: 狗
age: 1
?
@PropertySource
指定加載自定義的配置文件贷盲,默認只能加載properties格式
淘这,但是可以指定factory屬性來加載YML格式的配置文件
。
同時加載多個配置
測試是否成功
編寫PropertiesController
@RestController
@RequestMapping("properties")
@Slf4j
public class PropertiesController {
final
Person person;
public PropertiesController(Person person) {
this.person = person;
}
@GetMapping("getProperties")
public Dict getProperties(){
log.info(person.toString());
return Dict.create().set("person", person);
}
}
瀏覽器輸入:
http://localhost:8081/springboot-properties/properties/getProperties
驗證結(jié)果÷燎睿看到打印類信息钠怯,表示加載自定義YML格式的配置文件
成功了。
擴展功能
?
SpringBoot
還提供了@ImportResource
注解加載外部配置文件曙聂,只不過@ImportResource
通常用于加載Spring的xml配置文件
@ImportResource使用
?
Spring Boot
提出零xml的配置
晦炊,因此Spring Boot默認情況
下是不會主動識別
項目中Spring的xml配置文件
。為了能夠加載xml的配置文件
筹陵,Spring Boot提供了@ImportResource注解
刽锤,該注解可以加載Spring的xml配置文件
镊尺,通常加于啟動類
上朦佩。這里就不做贅述了,代碼參考下面。
//value:Spring的xml配置文件,支持多個庐氮。
@ImportResource(value = {"classpath:config/beans.xml"})
@SpringBootApplication
public class SpringbootPropertiesApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootPropertiesApplication.class, args);
}
}
Spring Boot多環(huán)境配置
多環(huán)境配置有什么好處呢语稠??弄砍?
?不同環(huán)境配置可以配置不同的參數(shù)
?便于部署仙畦,提高效率,減少出錯
yml多環(huán)境配置
application.yml
主配置文件
server:
port: 8081
servlet:
context-path: /springboot-properties
#配置激活選項
spring:
profiles:
active: dev
application-dev.yml
開發(fā)配置文件
#指定屬于哪個環(huán)境
spring:
profiles:
- dev
application-prod.yml
生產(chǎn)配置文件
#指定屬于哪個環(huán)境
spring:
profiles:
- prop
application-test.yml
測試配置文件
#指定屬于哪個環(huán)境
spring:
profiles:
- test
properties多環(huán)境配置
(1)主配置文件:配置激活選項
spring.profiles.active=dev
(2)其他配置文件:指定屬于哪個環(huán)境(同yml音婶,只不過表現(xiàn)形式是key=value
的,三個配置文件分別是:application-dev.properties
,application-prod.properties
,application-test.properties
)
yml多環(huán)境配置和properties多環(huán)境配置比較
Properties配置多環(huán)境
:需要添加多個配置文件
yml配置多環(huán)境
:可添加多個配置文件慨畸,可不添加,使用---
分隔(案例如下面代碼)(不建議使用該方法衣式,這樣顯得配置文件臃腫寸士,強烈建議添加多個配置文件
,也不費事碴卧。)
server:
port: 8081
servlet:
context-path: /springboot-properties
spring:
profiles:
active: dev
---
spring:
profiles:
- test
---
spring:
profiles:
- prod
---
spring:
profiles:
- dev
一般使用的配置文件
?application.yml
:是主配置文件弱卡,放一些項目通用的配置
?application-dev.yml
:放平常開發(fā)的一些配置,比如說數(shù)據(jù)庫的連接地址住册、帳號密碼等
?application-prod.yml
:放生產(chǎn)環(huán)境的一些配置婶博,比如說數(shù)據(jù)庫的連接地址、帳號密碼等
?application-test.yml
:放測試環(huán)境需要用到的參數(shù)
激活指定profile
-
使用spring.profiles.active激活
?無論是使用上述多文檔塊
的方式荧飞,還是新建application-test.yml
文件凡人,都可以在配置文件中指定 spring.profiles.active=test
激活指定的profile。
打成jar包運行時候使用命令行激活
java -jar XXXX-0.0.1-SNAPSHOT.jar --spring.profiles.active=test
使用虛擬機參數(shù)激活
-Dspring.profiles.active=test
在java代碼中激活
@SpringBootApplication
public class SpringbootPropertiesApplication {
public static void main(String[] args) {
System.setProperty("spring.profiles.active", "test");
SpringApplication.run(SpringbootPropertiesApplication.class, args);
}
}
結(jié)束語
感謝閱讀小生文章叹阔。祝大家早日富可敵國挠轴,實現(xiàn)財富自由。
寫文不易
,一定要點贊条获、評論忠荞、收藏哦
,感謝感謝感謝!!!