全面解讀Spring Boot 中的Profile配置體系
Pre
配置體系是基于 Spring Boot 框架開發(fā)應用程序的基礎,而自動配置也是該框架的核心功能之一,梳理使用 Spring Boot 配置體系的系統(tǒng)方法.
接下來,我們?yōu)檫@個代碼工程添加一些支持 RESTful 風格的 HTTP 端點,在這里我們同樣創(chuàng)建一個 CustomerController 類,如下所示
@RestController @RequestMapping(value="customers") public class CustomerController { @RequestMapping(value = "/{id}", method = RequestMethod.GET) public CustomerTicket getCustomerTicketById(@PathVariable Long id) { CustomerTicket customerTicket = new CustomerTicket(); customerTicket.setId(1L); customerTicket.setAccountId(100L); customerTicket.setOrderNumber("Order00001"); customerTicket.setDescription("DemoOrder"); customerTicket.setCreateTime(new Date()); return customerTicket; } }
請注意,這里是為了演示方便,我們才使用了硬編碼完成了一個 HTTP GET 請求的響應處理。
現(xiàn)在 RESTful 端點已經(jīng)開發(fā)完成,我們需要對這個應用程序進行打包?;?Spring Boot 和 Maven,當我們使用 mvn package 命令構建整個應用程序時,將得到一個 customerservice-0.0.1-SNAPSHOT.jar 文件,而這個 jar 文件就是可以直接運行的可執(zhí)行文件,內置了 Tomcat Web 服務器。也就是說,我們可以通過如下命令直接運行這個 Spring Boot 應用程序:
java –jar customerservice-0.0.1-SNAPSHOT.jar
通過 Postman 訪問“http://localhost:8083/customers/1”端點,可以得到如下圖所示的HTTP響應結果,說明整個服務已經(jīng)啟動成功。
Spring Boot 中的配置體系
在 Spring Boot 中,其核心設計理念是對配置信息的管理采用約定優(yōu)于配置。在這一理念下,則意味著開發(fā)人員所需要設置的配置信息數(shù)量比使用傳統(tǒng) Spring 框架時還大大減少。
當然,今天我們關注的主要是如何理解并使用 Spring Boot 中的配置信息組織方式,這里就需要引出一個核心的概念,即 Profile。
配置文件與 Profile
Profile 本質上代表一種用于組織配置信息的維度,在不同場景下可以代表不同的含義。例如,如果 Profile 代表的是一種狀態(tài),我們可以使用 open、halfopen、close 等值來分別代表全開、半開和關閉等。再比如系統(tǒng)需要設置一系列的模板,每個模板中保存著一系列配置項,那么也可以針對這些模板分別創(chuàng)建 Profile。這里的狀態(tài)或模版的定義完全由開發(fā)人員自主設計,我們可以根據(jù)需要自定義各種 Profile,這就是 Profile 的基本含義。
為了達到集中化管理的目的,Spring Boot 對配置文件的命名也做了一定的約定,分別使用 label 和 profile 概念來指定配置信息的版本以及運行環(huán)境,其中 label 表示配置版本控制信息,而 profile 則用來指定該配置文件所對應的環(huán)境
在 Spring Boot 中,配置文件同時支持 .properties 和 .yml 兩種文件格式,結合 label 和 profile 概念,如下所示的配置文件命名都是常見和合法的:
/{application}.yml /{application}-{profile}.yml /{label}/{application}-{profile}.yml /{application}-{profile}.properties /{label}/{application}-{profile}.properties
Yaml 的語法和其他高級語言類似,并且可以非常直觀地表達各種列表、清單、標量等數(shù)據(jù)形態(tài),特別適合用來表達或編輯數(shù)據(jù)結構和各種配置文件。在這里,我們指定了如下所示的數(shù)據(jù)源配置,這里使用了 . yml 文件,如下所示:
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/account username: root password: root
如果采用 .propertie 配置文件,那么上述配置信息將表示為如下的形式:
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://127.0.0.1:3306/account spring.datasource.username=root spring.datasource.password=root
顯然,類似這樣的數(shù)據(jù)源通常會根據(jù)環(huán)境的不同而存在很多套配置。假設我們存在如下所示的配置文件集合:
注意,這里有一個全局的 application.yml 配置文件以及多個局部的 profile 配置文件。
主 application.properties 中指定激活的Profile
那么,如何指定當前所使用的那一套配置信息呢?
在 Spring Boot 中,我們可以在主 application.properties 中使用如下的配置方式來激活當前所使用的 Profile:
spring.profiles.active = test
上述配置項意味著系統(tǒng)當前會讀取 application-test.yml 配置文件中的配置內容。同樣,如果使用 .yml 文件,則可以使用如下所示的配置方法:
spring: profiles: active: test
事實上,我們也可以同時激活幾個 Profile,這完全取決于你對系統(tǒng)配置的需求和維度:
spring.profiles.active: prod, myprofile1, myprofile2
Profile 配置信息只保存在一個文件
當然,如果你想把所有的 Profile 配置信息只保存在一個文件中而不是分散在多個配置文件中, Spring Boot 也是支持的,需要做的事情只是對這些信息按 Profile 進行組織、分段,如下所示:
spring: profiles: test #test 環(huán)境相關配置信息 spring: profiles: prod #prod 環(huán)境相關配置信息
推薦按多個配置文件的組織方法管理各個 Profile 配置信息,這樣才不容易混淆和出錯。
java –jar 激活Profile
最后,如果我們不希望在全局配置文件中指定所需要激活的 Profile,而是想把這個過程延遲到運行這個服務時,那么我們可以直接在 java –jar 命令中添加“–spring.profiles.active”參數(shù),如下所示
java –jar customerservice-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod
這種實現(xiàn)方案在通過腳本進行自動化打包和部署的場景下非常有用。
代碼控制與Profile
在 Spring Boot 中,Profile 這一概念的應用場景還包括動態(tài)控制代碼執(zhí)行流程。為此,我們需要使用 @Profile 注解,先來看一個簡單的示例。
@Configuration public class DataSourceConfig { @Bean @Profile("dev") public DataSource devDataSource() { //創(chuàng)建 dev 環(huán)境下的 DataSource } @Bean() @Profile("prod") public DataSource prodDataSource(){ //創(chuàng)建 prod 環(huán)境下的 DataSource } }
可以看到,我們構建了一個 DataSourceConfig 配置類來專門管理各個環(huán)境所需的 DataSource。注意到這里使用 @Profile 注解來指定具體所需要執(zhí)行的 DataSource 創(chuàng)建代碼,通過這種方式,可以達到與使用配置文件相同的效果。
更進一步,能夠在代碼中控制 JavaBean 的創(chuàng)建過程為我們根據(jù)各種條件動態(tài)執(zhí)行代碼流程提供了更大的可能性。
例如,在日常開發(fā)過程中,一個常見的需求是根據(jù)不同的運行環(huán)境初始化數(shù)據(jù),常見的做法是獨立執(zhí)行一段代碼或腳本?;?@Profile 注解,我們就可以將這一過程包含在代碼中并做到自動化,如下所示:
@Profile("dev") @Configuration public class DevDataInitConfig { @Bean public CommandLineRunner dataInit() { return new CommandLineRunner() { @Override public void run(String... args) throws Exception { //執(zhí)行 Dev 環(huán)境的數(shù)據(jù)初始化 }; }
這里用到了 Spring Boot 所提供了啟動時任務接口 CommandLineRunner,實現(xiàn)了該接口的代碼會在 Spring Boot 應用程序啟動時自動進行執(zhí)行 。
@Profile 注解的應用范圍很廣,我們可以將它添加到包含 @Configuration 和 @Component 注解的類及其方法,也就是說可以延伸到繼承了 @Component 注解的 @Service、@Controller、@Repository 等各種注解中。
常見配置場景和內容
下面來看幾個常見的配置示例 , 加深對 Spring Boot 中配置體系的理解。
對于一個 Web 應用程序而言,最常見的配置可能就是指定服務暴露的端口地址,如下所示:
server: port: 8080
同時,數(shù)據(jù)庫訪問也是 Web 應用程序的基本功能,因此,關于數(shù)據(jù)源的設置也是常見的一種配置場景,上一篇博文時給出了一個基本的示例。
這里再以 JPA 為例,給出如下所示的一種配置方案:
spring: jpa: hibernate: ddl-auto: create show-sql: true
顯然,這里使用了 Hibernate 作為 JPA 規(guī)范的實現(xiàn)框架,并設置了 show-sql 等相關屬性。然后,開發(fā)人員一般也需要設置日志級別和對象,如下所示的就是一個典型的配置示例:
logging.level.root=WARN logging.level.com.springcss.customer=INFO
我們設置了系統(tǒng)的全局日志級別為 WARN,而針對自定義的 com.springcss.customer 包下的日志則將其級別調整到 INFO。
這里需要注意的是,Spring Boot 基于 application.properties 或 application.yml 全局配置文件已經(jīng)自動內置了很多默認配置。即使我們不設置上述配置內容,Spring Boot 仍然可以基于這些默認配置完成系統(tǒng)的初始化。
自動配置是 Spring Boot 中的一個核心概念,我們會在后續(xù)內容中給出詳細的實現(xiàn)原理分析。
如何在應用程序中嵌入系統(tǒng)配置信息
我們知道 Spring Boot 通過自動配置機制內置了很多默認的配置信息,而在這些配置信息中,有一部分系統(tǒng)配置信息也可以反過來作為配置項應用到我們的應用程序中。
例如,如果想要獲取當前應用程序的名稱并作為一個配置項進行管理,那么很簡單,我們直接通過 ${spring.application.name} 占位符就可以做到這一點,如下所示:
myapplication.name : ${spring.application.name}
通過 ${} 占位符
同樣可以引用配置文件中的其他配置項內容,如在下列配置項中,最終“system.description”配置項的值就是“The system springcss is used for health”。
system.name=springcss system.domain=health system.description=The system ${name} is used for ${domain}.
再來看一種場景,假設我們使用 Maven 來構建應用程序,那么可以按如下所示的配置項來動態(tài)獲取與系統(tǒng)構建過程相關的信息:
info: app: encoding: @project.build.sourceEncoding@ java: source: @java.version@ target: @java.version@
上述配置項的效果與如下所示的靜態(tài)配置是一樣的:
info: app: encoding: UTF-8 java: source: 1.8.0_31 target: 1.8.0_31
根據(jù)不同的需求,在應用程序中嵌入系統(tǒng)配置信息是很有用的,特別是在一些面向 DevOps 的應用場景中。
如何創(chuàng)建和使用自定義配置信息
在現(xiàn)實的開發(fā)過程中,面對紛繁復雜的應用場景,Spring Boot 所提供的內置配置信息并不一定能夠完全滿足開發(fā)的需求,這就需要開發(fā)人員創(chuàng)建并管理各種自定義的配置信息。
例如,對于一個電商類應用場景,為了鼓勵用戶完成下單操作,我們希望每完成一個訂單給就給到用戶一定數(shù)量的積分。從系統(tǒng)擴展性上講,這個積分應該是可以調整的,所以我們創(chuàng)建了一個自定義的配置項,如下所示:
springcss.order.point = 10
這里,我們設置了每個訂單對應的積分為 10,那么應用程序該如何獲取這個配置項的內容呢?通常有兩種方法。
使用 @Value 注解
使用 @Value 注解來注入配置項內容是一種傳統(tǒng)的實現(xiàn)方法。針對前面給出的自定義配置項,我們可以構建一個 SpringCssConfig 類,如下所示:
@Component public class SpringCssConfig { @Value("${springcss.order.point}") private int point; }
在 SpringCssConfig 類中,我們要做的就是在字段上添加 @Value 注解,并指向配置項的名稱即可。
使用 @ConfigurationProperties 注解
相較 @Value 注解,更為現(xiàn)代的一種做法是使用 @ConfigurationProperties 注解。在使用該注解時,我們通常會設置一個“prefix”屬性用來指定配置項的前綴,如下所示:
@Component @ConfigurationProperties(prefix = "springcss.order") public class SpringCsshConfig { private int point; //省略 getter/setter }
相比 @Value 注解只能用于指定具體某一個配置項,@ConfigurationProperties 可以用來批量提取配置內容。只要指定 prefix,我們就可以把該 prefix 下的所有配置項按照名稱自動注入業(yè)務代碼中。
我們考慮一種更常見也更復雜的場景:假設用戶根據(jù)下單操作獲取的積分并不是固定的,而是根據(jù)每個不同類型的訂單會有不同的積分,那么現(xiàn)在的配置項的內容,如果使用 Yaml 格式的話就應該是這樣:
springcss: points: orderType[1]: 10 orderType[2]: 20 orderType[3]: 30
如果想把這些配置項全部加載到業(yè)務代碼中,使用 @ConfigurationProperties 注解同樣也很容易實現(xiàn)。我們可以直接在配置類 SpringCssConfig 中定義一個 Map 對象,然后通過 Key-Value 對來保存這些配置數(shù)據(jù),如下所示:
@Component @ConfigurationProperties(prefix="springcss.points") public class SpringCssConfig { private Map<String, Integer> orderType = new HashMap<>(); //省略 getter/setter }
可以看到這里通過創(chuàng)建一個 HashMap 來保存這些 Key-Value 對。類似的,我們也可以實現(xiàn)常見的一些數(shù)據(jù)結構的自動嵌入。
為自定義配置項添加提示功能
如果你已經(jīng)使用過 Spring Boot 中的配置文件,并添加了一些內置的配置項,你就會發(fā)現(xiàn),當我們輸入某一個配置項的前綴時,諸如 IDEA、Eclipse 這樣的,IDE 就會自動彈出該前綴下的所有配置信息供你進行選擇,效果如下:
上圖的效果對于管理自定義的配置信息非常有用。如何實現(xiàn)這種效果呢?當我們在 application.yml 配置文件中添加一個自定義配置項時,會注意到 IDE 會出現(xiàn)一個提示,說明這個配置項無法被 IDE 所識別,如下所示:
遇到這種提示時,我們是可以忽略的,因為它不會影響到任何執(zhí)行效果。
但為了達到自動提示效果,我們就需要生成配置元數(shù)據(jù)。生成元數(shù)據(jù)的方法也很簡單,直接通過 IDE 的“Create metadata for ‘springcss.order.point'”按鈕,就可以選擇創(chuàng)建配置元數(shù)據(jù)文件,這個文件的名稱為 additional-spring-configuration-metadata.json,文件內容如下所示:
{"properties": [{ "name": "springcss.order.point", "type": "java.lang.String", "description": "A description for 'springcss.order.point'" }]}
現(xiàn)在,假如我們在 application.properties 文件中輸入“springcss”,IDE 就會自動提示完整的配置項內容,效果如下所示:
另外,假設我們需要為 springcss.order.point 配置項指定一個默認值,可以通過在元數(shù)據(jù)中添加一個"defaultValue"項來實現(xiàn),如下所示:
{"properties": [{ "name": "springcss.order.point", "type": "java.lang.String", "description": "'springcss.order.point' is userd for setting the point when dealing with an order.", "defaultValue": 10 }]}
這時候,在 IDE 中設置這個配置項時,就會提出該配置項的默認值為 10,效果如下所示:
如何組織和整合配置信息
Profile 可以認為是管理配置信息中的一種有效手段。
下面,我們繼續(xù)介紹另一種組織和整合配置信息的方法,這種方法同樣依賴于前面介紹的 @ConfigurationProperties 注解。
使用 @PropertySources 注解
在使用 @ConfigurationProperties 注解時,我們可以和 @PropertySource 注解一起進行使用,從而指定從哪個具體的配置文件中獲取配置信息。
例如,在下面這個示例中,我們通過 @PropertySource 注解指定了 @ConfigurationProperties 注解中所使用的配置信息是從當前類路徑下的 application.properties 配置文件中進行讀取。
@Component @ConfigurationProperties(prefix = "springcss.order") @PropertySource(value = "classpath:application.properties") public class SpringCssConfig { }
既然我們可以通過 @PropertySource 注解來指定一個配置文件的引用地址,那么顯然也可以引入多個配置文件,這時候用到的是 @PropertySources 注解,使用方式如下所示:
@PropertySources({ @PropertySource("classpath:application.properties "), @PropertySource("classpath:redis.properties"), @PropertySource("classpath:mq.properties") }) public class SpringCssConfig {
這里,我們通過 @PropertySources 注解組合了多個 @PropertySource 注解中所指定的配置文件路徑。SpringCssConfig 類可以同時引用所有這些配置文件中的配置項。
spring.config.location 來改變配置文件的默認加載位置
另一方面,我們也可以通過配置 spring.config.location 來改變配置文件的默認加載位置,從而實現(xiàn)對多個配置文件的同時加載。例如,如下所示的執(zhí)行腳本會在啟動 customerservice-0.0.1-SNAPSHOT.jar 時加載D盤下的 application.properties 文件,以及位于當前類路徑下 config 目錄中的所有配置文件:
java -jar customerservice-0.0.1-SNAPSHOT.jar --spring.config.location=file:///D:/application.properties, classpath:/config/
通過 spring.config.location 指定多個配置文件路徑也是組織和整合配置信息的一種有效的實現(xiàn)方式。
理解配置文件的加載順序
通過前面的示例,我們看到可以把配置文件保存在多個路徑,而這些路徑在加載配置文件時具有一定的順序。Spring Boot 在啟動時會掃描以下位置的 application.properties 或者 application.yml 文件作為全局配置文件:
–file:./config/ –file:./ –classpath:/config/ –classpath:/
以下是按照優(yōu)先級從高到低的順序,如下所示:
Spring Boot 會全部掃描上圖中的這四個位置,掃描規(guī)則是高優(yōu)先級配置內容會覆蓋低優(yōu)先級配置內容。而如果高優(yōu)先級的配置文件中存在與低優(yōu)先級配置文件不沖突的屬性,則會形成一種互補配置,也就是說會整合所有不沖突的屬性。
如何覆寫內置的配置類
關于 Spring Boot 配置體系,最后值得介紹的就是如何覆寫它所提供的配置類。我們已經(jīng)反復強調 Spring Boot 內置了大量的自動配置,如果我們不想使用這些配置,就需要對它們進行覆寫。
覆寫的方法有很多,我們可以使用配置文件、Groovy 腳本以及 Java 代碼。這里,我們就以Java代碼為例來簡單演示覆寫配置類的實現(xiàn)方法。
以Spring Security為例
在 Spring Security 體系中,設置用戶認證信息所依賴的配置類是 WebSecurityConfigurer 類。顧名思義,這是一個設置 Web 安全的配置類。
Spring Security 提供了 WebSecurityConfigurerAdapter 這個適配器類來簡化該配置類的使用方式,我們可以繼承 WebSecurityConfigurerAdapter 類并且覆寫其中的 configure() 的方法來完成自定義的用戶認證配置工作。
典型的 WebSecurityConfigurerAdapter 子類及其代碼實現(xiàn)如下所示
@Configuration public class SpringHCssWebSecurityConfigurer extends WebSecurityConfigurerAdapter { @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override @Bean public UserDetailsService userDetailsServiceBean() throws Exception { return super.userDetailsServiceBean(); } @Override protected void configure(AuthenticationManagerBuilder builder) throws Exception { builder.inMemoryAuthentication().withUser("springcss_user").password("{noop}password1").roles("USER").and() .withUser("springcss_admin").password("{noop}password2").roles("USER", "ADMIN"); } }
這里我們只需要知道,在 Spring Boot 中,提供了一些類的內置配置類,而開發(fā)人員可以通過構建諸如上述所示的 SpringCssWebSecurityConfigurer 類來對這些內置配置類進行覆寫,從而實現(xiàn)自定義的配置信息。
以上就是Spring Boot 中的配置體系Profile全面解讀的詳細內容,更多關于Spring Boot配置體系Profile的資料請關注腳本之家其它相關文章!
相關文章
在ChatGPT的API中支持多輪對話的實現(xiàn)方法
ChatGPT是由OpenAI研發(fā)的一種預訓練語言模型,只能在OpenAI平臺上進行訓練,目前并不對外開放訓練接口,這篇文章主要介紹了在ChatGPT的API中支持多輪對話的實現(xiàn)方法,需要的朋友可以參考下2023-02-02Spring Cloud Gateway使用Token驗證詳解
這篇文章主要介紹了Spring Cloud Gateway使用Token驗證詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-02-02Java String index out of range:100錯誤解決方案詳解
這篇文章主要介紹了Java String index out of range:100錯誤解決方案詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下2021-08-08SpringBoot使用@NotEmpty、@NotBlank、@NotNull注解進行參數(shù)校驗
我們經(jīng)常需要對請求參數(shù)進行校驗,本文主要介紹了SpringBoot使用@NotEmpty、@NotBlank、@NotNull注解進行參數(shù)校驗,具有一定的參考價值,感興趣的可以了解一下2024-08-08java 中Executor, ExecutorService 和 Executors 間的不同
這篇文章主要介紹了java 中Executor, ExecutorService 和 Executors 間的不同的相關資料,需要的朋友可以參考下2017-06-06Java?從json提取數(shù)組并轉換為list的操作方法
這篇文章主要介紹了Java?從json提取出數(shù)組并轉換為list,使用getJSONArray()獲取到jsonarray后,再將jsonArray轉換為字符串,最后將字符串解析為List列表,本文通過實例代碼給大家詳細講解,需要的朋友可以參考下2022-10-10