SpringBoot外化配置源碼解析
在前面章節我們講解了 Spring Boot 的運作核心原理及啟動過程中進行的一系列核心操作。
從本章開始,我們将針對在實踐過程中應用的不同知識點的源代碼進行解讀和分析,内容上可能會與之前章節有所重疊,但這些重疊的内容更有助于我們在實踐和應用中形成前後呼應,加深記憶學習效果。
本章将重點講解 Spring Boot 外化配置檔案相關内容,核心包括:外化配置檔案、指令行參數、Profile 實作機制及 整個加載處理業務邏輯。
外化配置簡介
Spring Boot 允許我們将配置進行外部化處理,以便我們使用相同的代碼在不同的環境中運作。我們可以使用屬性檔案、YAML 檔案、環境變量和指令參數來進行外化配置。這些配置中的屬性可以通過@Value 注解直接注入到對應的 Bean 中,也可以通過 Spring 的Environment 抽象通路,還可以通過@ConfigurationProperties 綁定到結構化的對象上。
Spring Boot 設計了非常特殊的加載指定屬性檔案(PropertySource) 的順序,以允許對屬性值進行合理的覆寫。屬性值會以下面的優先級進行設定。
-home 目 錄 下 的 Devtools 全 局 設 置 屬 性 (~/., 條件當devtools 激活時)。@ TestPropertySource 注解的測試用例。
. @Spring BootTest#properties 注解的測試用例。
.指令行參數。
.來自 SPRING_ _APPLICATION JSON 的屬性(内嵌在環境變量或系統屬性中的内聯JSON)。
SrvletConfig 初始化參數。
:ServletContext 初始化參數。
java:comp/env 的 JNDI 屬性。
Java 系統屬性(() 。
.作業系統環境變量。
: RandomValuePropertySource,隻包含 random. *中的屬性。
jar 包外的 Profile-specific 應用屬性(application-{profile} properties 和 YAML 變量)。
:jar 包内的 Profile-specific 應用屬性(application-{profile} properties 和 YAML 變量)。
xjar 包外的應用配置(application properties 和 YAML 變量)。
xjar 包内的應用配置( 和 YAML 變 量)。
[email protected] 類上的@ PropertySource 注解。
.預設屬性(通過 指定)。
在以上配置方式中,我們經常使用的包括:指令參數、屬性檔案、YAML 檔案等内容,以下将圍繞它們的運作及相關代碼進行講解。
ApplicationArguments 參數處理
ApplicationArguments 提供了針對參數的解析和查詢功能。在 Spring Boot 運作階段的章節中 我 們 提 到 過 , 通 過 (args) 傳 遞 的 參 數 會 被 封 裝 在ApplicationArguments 接口中。本節我們來詳細了解一下 ApplicationArguments 接口。
接口定義及初始化
首先看一下 ApplicationArguments 接口的具體方法定義及功能介紹(注釋部分)。
public interface ApplicationArguments {//傳回原始未處理的參數(通過 appl ication 傳入的)String[] getSourceArgs();//傳回所有參數名稱的集合,如參數為: - foo=bar --debug, 則傳回[ "foo", "debug"]Set getOpt ionNames();//選項參數中是否包含指定名稱的參數boolean containsOpt ion(String name);//根據選項參數的名稱擷取選項參數的值清單List getOptionValues(String name);//傳回非選項參數清單List getNonOpt ionArgs();}
通過接口定義可以看出,ApplicationArguments 主要提供 了針對參數名稱和值的查詢,以及判斷是否存在指定參數的功能。
在 Spring Boot 的初始化運作過程中,ApplicationArguments 接口的執行個體化操作預設是通過實作類 DefaultApplicationArguments 來完成的。
DefaultApplicationArguments 的 底 層 又 是 基 于 Spring 框 架 中 的 命 令 行 配 置 源SimpleCommandLinePropertySource 實 現 的 SimpleCommandLinePropertySource 是PropertySource 抽象類的派生類。
以下代碼中内部類 Source 便是 SimpleCommandLinePropertySource 的子類。
public class DefaultApplicationArguments implements ApplicationArguments {private final Source source;private final String[] args;public DefaultApplicationArguments(String[] args) {Assert. notNull(args, "Args must not be null");this.source = new Source(args);this.args = args;//在此省略 Appl icat ionArguments 的其他接口實作方法private static class Source extends SimpleCommandL inePropertySource {// ...}}
我們再來看 SimpleCommand inePropertySource 的構造方法,通過代碼會發現預設使用Spring 的 SimpleCommandLineArgsParser 對 args 參加進行解析。
public class SimpleCommandL inePropertySource extends CommandL inePropertySource {public SimpleCommandL inePropertySource(String... args) {super(new SimpleCommand ineArgsParser() . parse(args));// 重裁的構造方法public SimpleCommandL inePropertySource(String name, String[] args) {super(name, new SimpleCommandL ineArgsParser() . parse(args));}}
除了構造方法之外,SimpleCommandLinePropertySource 還提供 了不同類型參數資訊的擷取和檢查是否存在的功能,代碼如下。
public class SimpleCommandL inePropertySource extends Commandl inePropertySource {/擷取選項參數數組@0verridepublic String[] getPropertyNames()return StringUtils. toStringArray(this . source . getOpt ionNames());/擷取是否包含指定 name 的參數@Overrideprotected boolean containsOption(String name) {return this. source . containsOption(name );/擷取指定 name 的選項參數清單@[email protected] List getOptionValues(String name) {return this . source . getOpt ionValues(name);//擷取非選項參數清單protected List getNonOptionArgs() {return this. source . getNonOptionArgs();}}
ApplicationArguments 或者步說是 SimpleCommandLinePropertySource 對參數類型是有所區分的,即選項參數和非選項參數。
選項參數必須以“一”為字首,參數值可為空,該參數我們可以通過 SpringBoot 屬性處理後使用,比如在執行 jar jar 指令時,添加選項參數“-spring boot learn",在代碼中可通過@Value 屬性或其他方式擷取到該參數的值。該參數可以通過逗号分隔多個參數值,或多次使用同一個參數來包含多個參數的值。
非選項參數并不要求以“--”字首開始,可自行定義。非選項參數可以是除了傳遞的 VM 參數之外的其他參數。比如我們可以直接在 jar -jar 指令中定義參數為“non-option”的參數值。
以上所說的選項參數和非選項參數的解析是在 SimpleCommandLinePropertySource 構造方法中調用的 SimpleCommandLineArgsParser 中完成的,代碼如下。
class SimpleCommandL ineArgsParser//解析 args 參數,傳回一個 完整的 CommandL ineArgs 對象public CommandLineArgs parse(String... args) {CommandL ineArgs commandLineArgs = new CommandL ineArgs();//周遊參數for (String arg : args) {//解析選項參數,以"--"開頭if (arg. startsWith("--"))String optionText = arg. substring(2, ());String optionName;String optionValue = null;//判斷是--foo=bar 參數格式, 還是 foo 參數格式, 并分别處理擷取值if (optionText . contains("=")) {optionName = optionText . substring(0, optionText . index0f('='));optionValue = optionText . substring(optionText . indexOf('=' )+1,optionText. length());} elseoptionName = optionText;if (optionName. isEmpty()|| (optionValue != null && opt ionValue.isEmpty()))-throw new IllegalArgumentException("Invalid argument syntax: ”+arg);commandL ineArgs . addOpt ionArg( optionName, optionValue);} else {//處理非選項參數commandL ineArgs . addNon0pt ionArg(arg);return commandLineArgs;}}
通過 SimpleCommandLineArgsParser 的代碼可以看出,Spring 對參數的解析是按照指定的 參 數 格 式 分 别 解 析 字 符 串 中 的 值 來 實 現 的 。 最 終 , 解 析 的 結 果 均 封 裝 在CommandLineArgs 中。而 CommandLineArgs 類 隻是指令行參數的簡單表示形式,内部分為“選項參數和“非選項參數”。
class CommandL ineArgsprivate final Map> optionArgs = new HashMap<>();private final List nonOptionArgs = new ArrayList<>();CommandLineArgs 的核心存儲結構包括:存儲選項參數的Map> optionArgs 和存儲非選項參數的 List
nonOptionArgs。同時,針對這兩個核心存儲結構,Spring Boot 也提供了讀寫操作的方法。
SimpleCommandLineArgsParser 解 析 獲 得 的 CommandLineArgs 對 象 , 最 終 會 被SimpleCommand-LinePropertySource 的 構 造 方 法 通 過 super 調 用 , 一 層 層 地 傳 遞 到PropertySource 類的構造方法, 最終封裝到對應的屬性當中。
public abstract class PropertySource {//參數類别名稱protected final String name;//參數封裝類protected final T source;
以在 SimpleCommandLinePropertySource 中的使用為例,最終封裝在 PropertySource 中的結構為: name 為“commandLineArgs",source 為解析出 的 CommandLineArgs 對象。
而DefaultApplicationArguments的内部類Source作為SimpleCommandLinePropertySource 的子類存儲了以上解析的資料内容。同時,args 參數的原始值存儲在 DefaultApplicationArguments 的 String[ ] args 屬性中。
使用執行個體
在實踐中我們可能會遇到這樣的疑問:如何通路應用程式變量?或者,如何通路通過(args) 傳 入 的 參 數 ? 下 面 我 們 以 具 體 的 例 子 來 說 明 如 何 通 過ApplicationArguments 獲得對應的參數。
ApplicationArguments 接口的使用非常簡單,在我們使用參數值的 Bean 中直接注入ApplicationArguments 即可,然後調用其方法即可獲得對應的參數值。
注入 ApplicationArguments,并提供列印所需參數資訊的方法,代碼如下。
@Componentpublic class ArgsBean {@Resourceprivate ApplicationArguments arguments;public void printArgs() {System. out. println("#非選項參數數量:”+ arguments . getNonOptionArgs().size());System. out. println("#選項參數數量:”+ arguments . getOpt ionNames(). size());System. out. println("#非選項參數具體參數:");arguments . getNonOpt ionArgs(). forEach(System. out::println);System. out.println("#選項參數具體參數:");arguments . getOptionNames() . forEach(optionName -> {System. out . println("--"+ optionName + "=" + arguments . getOptionValue(optionName));});}
在 main 方法中獲得 ArgsBean 執行個體化對象,并調用其 printArgs 方法, 代碼如下。
public static void main(String[] args) {SpringSpringApplication app = new SpringApplication(SpringL earnApplication.clasConfigurableApplicationContext context =app. run(args);ArgsBean bean = context. getBean(ArgsBean. class);bean. printArgs();}
啟動項目,控制台列印結果,代碼如下。
非選項參數數量: 1
#選項參數數量:2
#非選項參數具體參數:
nonaoptin 休會
#選項
參數:
--jdk . support=[]
- app . name=[ springBootLearn]
以上隻是示例,在上面的介紹中也提到了,選項參數可通過@Value 直接注入 Bean 中使用。
關于ApplicationArguments 其他方法的使用以此類推即可!
本文給大家講解的内容是外化配置簡介、ApplicationArguments參數處理
下篇文章給大家講解的是指令參數的擷取和配置檔案的加載;
覺得文章不錯的朋友可以轉發此文關注小編;
感謝大家的支援!