不學無數——SpringBoot入門Ⅲ
SpringBoot-外部資源的配置
1.外部資源的配置優先順序
為了能夠在不同的環境執行不同的配置檔案,或者有些程式碼的一些變數是跟隨著環境的改變而改變的,這時候就需要在外部做一些配置。SpringBoot允許這麼做,並且有一套配置規則。可以通過@Value註解進行將一些變數動態的取出來。SpringBoot支援多種的外部配置的方式,其中的優先順序如下:
-
全域性的設定在根目錄中(
~/.spring-boot-devtools.properties
) -
@TestPropertySource
在Test模組中的註解 -
@SpringBootTest#properties
-
命令列
-
命令列中的
SPRING_APPLICATION_JSONJSON
字串, 例如java -Dspring.application.json='{"foo":"bar"}' -jar myapp.jar
-
ServletConfig
初始化引數,可在程式碼進行配置 -
ServletContext
初始化引數,可在程式碼進行配置 -
來自
java:comp/env
的JNDI屬性 -
Java系統屬性(
System.getProperties()
) -
作業系統的環境變數
-
RandomValuePropertySource
random.*
屬性值 -
jar外部的帶指定
profile
的application.yml
,比如application-{profile}.yml
-
jar內部的帶指定
profile
的application.yml
,比如application-{profile}.yml
-
jar外部的
application.yml
-
jar內部的
application.yml
-
在自定義的
@Configuration
類中定於的@PropertySource
-
啟動的main方法中,定義的預設配置。
SpringApplication.setDefaultProperties
下面給出具體的例項:
@SpringBootApplication
@RestController
public class FirstSpringBootApplication {
@Value("${name}")
private String name;
@RequestMapping("/")
String home() {
return name;
}
public static void main(String[] args) {
SpringApplication app = new SpringApplication(FirstSpringBootApplication.class);
app.run(args);
}
}
可以在application.yml或者application.properties配置檔案中設定name的值
application.yml
name: 不學無數
注意在name: 的冒號後面要加空格
application.properties
name=不學無數
這樣在啟動專案的時候然後訪問http:localhost:8080就可以取出了name的值
<center><img src="http://p9jfgo4wc.bkt.clouddn.com/buxuewushu.png"/></center>
2.配置隨機值
當想要一些隨機值的時候SpringBoot也提供了一些方法,隨機數具體有哪些應用呢,例如可以防止瀏覽器認為是相同的資源而去讀取快取,或者是生成驗證碼,在application.properties配置如下
my.secret=${random.value} --生成隨機字串
my.number=${random.int} --生成隨機數
my.uuid=${random.uuid} --生成uuid
my.number.less.than.ten=${random.int(10)} -- 生成10以內的隨機數
my.number.in.range=${random.int[1024,65536]} --生成1024到65536的隨機數
取出的程式碼如下:
@Value("${my.secret}")
private String secret;
@Value("${my.number}")
private String number;
@Value("${my.uuid}")
private String uuid;
@Value("${my.number.less.than.ten}")
private Integer ten;
@Value("${my.number.in.range}")
private Integer range;
@RequestMapping("/")
String home() {
StringBuffer stringBuffer=new StringBuffer();
stringBuffer.append("my.secret:"+secret+"<br/>");
stringBuffer.append("my.number:"+number+"<br/>");
stringBuffer.append("my.uuid:"+uuid+"<br/>");
stringBuffer.append("my.number.less.than.ten}:"+ten+"<br/>");
stringBuffer.append("my.number.in.range:"+range+"<br/>");
return stringBuffer.toString();
}
然後訪問http:localhost:8080
可以檢視到如下的頁面
<center><img src="http://p9jfgo4wc.bkt.clouddn.com/suijishu1.png"/></center>
3.命令列配置
SpingApplication
預設的能夠轉變任何命令列的引數然後將其配置在Spring中,例如--server.port=9000
,那麼web啟動的埠號就變為了9000。
<center><img src="http://p9jfgo4wc.bkt.clouddn.com/%E7%AB%AF%E5%8F%A3.jpeg"/></center>
然後啟動專案就會發現埠號改變了。
如果不想讓命令列的配置加入到Spring環境中的話,可以
SpringApplication.setAddCommandLineProperties(false)
進行設定
4.資原始檔配置
SpringApplication
會從application.properties
進行載入系統的配置,並且將其配置加入到Spring的環境中
你也可以使用YAML('.yml')來代替.properties
如果不想使用application.properties作為配置檔案的名字的話,那麼可以選擇其他名字的檔案作為配置檔案。可以具體設定spring.config.name環境變數進行設定。也可以使用spring.config.location後面跟檔案的全路徑名。
$ 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這兩個配置因為是要設定哪個配置檔案起作用的,所以必須在程式啟動之前設定,例如設定在系統的環境變數中,或者是設定在啟動的引數中。
5.獨特的配置(Profile-specific Properties)
不知道該怎麼翻譯,就把英文給加在後面了,以免誤人子弟。在公司進行開發的過程中,也許我們會碰到這種情況,例如在開發環境中用的一套配置,而在測試環境是另一套的配置。所以在不同的環境需要有不同的配置檔案。SpringBoot也提供了這樣一套的配置規則。application-{profile}.properties
。其中的profile
]可以進行配置化,如果沒有配置的話那麼就會以預設的application-default.properties
為配置檔案。其中profile
的屬性可以在application.properties
進行配置spring.profiles.active
的值。 application.properties
配置如下
spring.profiles.active=test --即載入application-test.properties
或者spring.profiles.active=dev --即載入application-dev.properties
application-test.properties
name=BuXueWuShu---Test
application-dev.properties
name=BuXueWuShu---Dev
這樣在application.properties中就可以載入不同的配置檔案
@Value("${name}")
private String name;
@RequestMapping("/")
String home() {
return name;
}
通過不同的環境配置就可以訪問不同值
當然你也可以通過上一節的spring.config.location這個啟動的環境變數進行設定想要啟動的時候載入具體哪個配置檔案
6.資原始檔中的佔位符
當在資原始檔中定義過一些變數的時候,如果在同樣的資原始檔中要複用,那麼也可以進行引用。
application.properties配置
my.name=BuXueWuShu
my.description=My name is ${my.name}
7.使用YAML
YAML是針對於JSON的一種擴充套件,所以做配置檔案是非常不錯的選擇。並且在SpringApplication
中如果你引入了spring-boot-starter
的包,那麼SpringApplication
會自動的支援YAML。
7.1解析YAML
Spring框架提供了兩個簡便的類能夠解析YAML文件。YamlPropertiesFactor
yBean解析YAML作為Properties。YamlMapFactoryBean
解析YAML作為Map。例如下面的YAML檔案中
environments:
dev:
url: http://dev.example.com
name: Developer Setup
prod:
url: http://another.example.com
name: My Cool App
剛才的例子這個YAML將會被解析成
environments.dev.url=http://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=http://another.example.com
environments.prod.name=My Cool App
YAML列表將會被替代成陣列的型別,根據[index]進行取值
my:
servers:
- dev.example.com
- another.example.com
上面的例子將會被解析成下面這種形式的
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
如果你想講這些資料進行取出來的話,新建一個配置類,使用SpringBoot提供的@ConfigurationProperties的註解,然後在此類中得有一個List或者Set集合,然後設定get方法就能將值取出來了,具體實現如下:
@ConfigurationProperties(prefix="my")
public class Config {
private List<String> servers = new ArrayList<String>();
public List<String> getServers() {
return this.servers;
}
}
7.2 多環境配置YAML
在講解application.properties
的時候如果想使用多環境的配置那麼就是設定不同的application-{profile}.properties
的檔案具體哪個檔案生效可以在application.properties
中設定spring.profiles.active
屬性即可。但是在YAML如何在不同的環境中生效呢?當然第一種辦法就是和application.properties
一樣設定不同的yml檔案,然後進行設定具體哪個生效即可。YAML也有另一種的方法。如下所示
server:
address: 192.168.1.100
spring:
profiles:
active:在此設定具體哪個profiles生效即可(例如此處填寫 test) 就是adress就是192.168.1.120
---
spring:
profiles: development
server:
address: 127.0.0.1
---
spring:
profiles: test
server:
address: 192.168.1.120
通過"---"設定不同的環境的配置屬性
如果你在配置檔案中沒有設定spring.profiles.active
屬性那麼配置檔案就不知道哪個生效,所以他會找一個預設的進行設定。只需要設定spring.profiles:default
即可
server:
port: 8000
---
spring:
profiles: default
security:
user:
password: weak
8.通過型別匹配屬性(Type-safe Configuration Properties)
通過@Value("${property}")
可以將配置中的屬性在類中取出來,這樣是可以的,但是如果有多個型別要取或者是要取的有著嚴格的等級。那麼取的時候會有些麻煩,SpringBoot提供了一套可以通過型別匹配在類中寫出熟悉然後載入到配置檔案中。而這也是SpringBoot的一項宗旨,儘量減少xml的配置。
在yml檔案中的配置如下
acme:
remote-address: 192.168.1.1
security:
username: admin
roles:
- USER
- ADMIN
接收這些配置資訊的類如下
package com.example.FirstSpringBoot.Configuration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ConfigurationProperties("acme")
public class AcmeProperties {
private boolean enabled;
private String remoteAddress;
private final Security security = new Security();
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled=enabled; }
public String getRemoteAddress() { return remoteAddress; }
public void setRemoteAddress(String remoteAddress) { this.remoteAddress=remoteAddress; }
public Security getSecurity() { return security; }
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
public String getUsername() { return username; }
public void setUsername(String username) { this.username=username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password=password; }
public List<String> getRoles() { return roles; }
public void setRoles(List<String> roles) { this.roles=roles; }
}
}
然後再寫一個類將上面的類進行註冊進Spring中
@Configuration
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}
然後在任何類中就可以進行配置了
@Service
public class MyService {
private final AcmeProperties acmeProperties;
@Autowired
public MyService(AcmeProperties acmeProperties){
this.acmeProperties=acmeProperties;
}
@PostConstruct
public void openConnection() {
System.out.println("remote-address:"+acmeProperties.getRemoteAddress());
System.out.println("username:"+acmeProperties.getSecurity().getUsername());
System.out.println("role:"+acmeProperties.getSecurity().getRoles());
}
}
這樣就可以將配置檔案中的屬性從bean中取出來,而不是一個一個@Value進行取了。其中@PostConstruct
註解意思是在載入Servlet的時候進行執行,並且只執行一次。
2018-07-18 17:55:51.593 INFO 17487 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2018-07-18 17:55:51.599 INFO 17487 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-07-18 17:55:51.599 INFO 17487 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-07-18 17:55:51.599 INFO 17487 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-07-18 17:55:51.599 INFO 17487 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
remote-address:192.168.1.1
username:admin
role:[USER, ADMIN]
檢視啟動資訊就可以知道,他是在載入Servlet的時候執行的。
8.1第三方的配置
如果你的配置想要作為一個jar包供第三方進行使用,那麼可以在與配置進行型別匹配的類中即上面提到的AcmeProperties類上面加上@Bean註解。這樣就可以將配置進行打包傳輸了。
@ConfigurationProperties(prefix = "acme")
@Bean
public class AcmeProperties() {
...
}
8.2對於資原始檔的校驗
SpringBoiot提供了一套對資原始檔的校驗。假如在資原始檔中某個欄位是不可或缺的,只要如下配置即可。
@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {
@NotNull
private String remoteAddress;
// ... getters and setters
}
此時如果資原始檔中沒有定義acme.remoteAddress的值的話那麼在專案啟動的時候就會報錯
Description:
Binding to target [email protected]6 failed:
Property: acme.remoteAddress
Value: null
Reason: 不能為null
當複雜的資原始檔想要校驗怎麼辦?就如下配置即可
@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {
@NotNull
private String remoteAddress;
@Valid
private final Security security = new Security();
// ... getters and setters
public static class Security {
@NotEmpty
public String username;
// ... getters and setters
}
}
當然這個@Validated
註解是SpringMvc中的註解,這裡就不詳細解釋了。關於@Validated
的參考資料: