SpringBoot如何解析應(yīng)用參數(shù)args
前言
前文深入解析了SpringBoot啟動(dòng)的開(kāi)始階段,包括獲取和啟動(dòng)應(yīng)用啟動(dòng)監(jiān)聽(tīng)器、事件與廣播機(jī)制,以及如何通過(guò)匹配監(jiān)聽(tīng)器實(shí)現(xiàn)啟動(dòng)過(guò)程各階段的自定義邏輯。接下來(lái),我們將探討SpringBoot啟動(dòng)類(lèi)main函數(shù)中的參數(shù)args的作用及其解析過(guò)程。
SpringBoot版本2.7.18SpringApplication的run方法的執(zhí)行邏輯如下,本文將詳細(xì)介紹第3小節(jié):解析應(yīng)用參數(shù)
// SpringApplication類(lèi)方法
public ConfigurableApplicationContext run(String... args) {
// 記錄應(yīng)用啟動(dòng)的開(kāi)始時(shí)間
long startTime = System.nanoTime();
// 1.創(chuàng)建引導(dǎo)上下文,用于管理應(yīng)用啟動(dòng)時(shí)的依賴(lài)和資源
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 配置無(wú)頭模式屬性,以支持在無(wú)圖形環(huán)境下運(yùn)行
// 將系統(tǒng)屬性 java.awt.headless 設(shè)置為 true
configureHeadlessProperty();
// 2.獲取Spring應(yīng)用啟動(dòng)監(jiān)聽(tīng)器,用于在應(yīng)用啟動(dòng)的各個(gè)階段執(zhí)行自定義邏輯
SpringApplicationRunListeners listeners = getRunListeners(args);
// 啟動(dòng)開(kāi)始方法(發(fā)布開(kāi)始事件、通知應(yīng)用監(jiān)聽(tīng)器ApplicationListener)
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 3.解析應(yīng)用參數(shù)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 4.準(zhǔn)備應(yīng)用環(huán)境,包括讀取配置文件和設(shè)置環(huán)境變量
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 配置是否忽略 BeanInfo,以加快啟動(dòng)速度
configureIgnoreBeanInfo(environment);
// 5.打印啟動(dòng)Banner
Banner printedBanner = printBanner(environment);
// 6.創(chuàng)建應(yīng)用程序上下文
context = createApplicationContext();
// 設(shè)置應(yīng)用啟動(dòng)的上下文,用于監(jiān)控和管理啟動(dòng)過(guò)程
context.setApplicationStartup(this.applicationStartup);
// 7.準(zhǔn)備應(yīng)用上下文,包括加載配置、添加 Bean 等
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 8.刷新上下文,完成 Bean 的加載和依賴(lài)注入
refreshContext(context);
// 9.刷新后的一些操作,如事件發(fā)布等
afterRefresh(context, applicationArguments);
// 計(jì)算啟動(dòng)應(yīng)用程序的時(shí)間,并記錄日志
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
// 10.通知監(jiān)聽(tīng)器應(yīng)用啟動(dòng)完成
listeners.started(context, timeTakenToStartup);
// 11.調(diào)用應(yīng)用程序中的 `CommandLineRunner` 或 `ApplicationRunner`,以便執(zhí)行自定義的啟動(dòng)邏輯
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 12.處理啟動(dòng)過(guò)程中發(fā)生的異常,并通知監(jiān)聽(tīng)器
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 13.計(jì)算應(yīng)用啟動(dòng)完成至準(zhǔn)備就緒的時(shí)間,并通知監(jiān)聽(tīng)器
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
// 處理準(zhǔn)備就緒過(guò)程中發(fā)生的異常
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
// 返回已啟動(dòng)并準(zhǔn)備就緒的應(yīng)用上下文
return context;
}一、入口
將main方法的參數(shù)args封裝成一個(gè)對(duì)象DefaultApplicationArguments,以便方便地解析和訪問(wèn)啟動(dòng)參數(shù)
// 3.解析應(yīng)用參數(shù) ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
二、默認(rèn)應(yīng)用程序參數(shù)DefaultApplicationArguments
1、功能概述
DefaultApplicationArguments是SpringBoot中的一個(gè)類(lèi),用于處理啟動(dòng)時(shí)傳入的參數(shù)。它實(shí)現(xiàn)了ApplicationArguments接口,并提供了一些便捷的方法來(lái)訪問(wèn)傳入的命令行參數(shù)和選項(xiàng)參數(shù)。
// 3.解析應(yīng)用參數(shù) ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
- 解析命令行參數(shù):將
main方法中的args參數(shù)解析成選項(xiàng)參數(shù)和非選項(xiàng)參數(shù),方便應(yīng)用在啟動(dòng)時(shí)讀取外部傳入的配置 - 訪問(wèn)
選項(xiàng)參數(shù):支持以--key=value格式的選項(xiàng)參數(shù),通過(guò)方法getOptionNames()和getOptionValues(String name)獲取特定的選項(xiàng)及其值 - 訪問(wèn)
非選項(xiàng)參數(shù):對(duì)于不以--開(kāi)頭的參數(shù),可以通過(guò)getNonOptionArgs()獲取它們的列表
2、使用示例
假設(shè)我們?cè)诿钚兄羞\(yùn)行應(yīng)用,傳遞了一些參數(shù)
java -jar myapp.jar --server.port=8080 arg1 arg2
在代碼中,我們可以使用DefaultApplicationArguments來(lái)解析這些參數(shù)
public static void main(String[] args) {
DefaultApplicationArguments appArgs = new DefaultApplicationArguments(args);
// 獲取所有選項(xiàng)參數(shù)名稱(chēng)
System.out.println("選項(xiàng)參數(shù):" + appArgs.getOptionNames()); // 輸出: ["server.port"]
// 獲取指定選項(xiàng)的值(所有以 `--` 開(kāi)頭的選項(xiàng)參數(shù)名稱(chēng))
System.out.println("server.port 值:" + appArgs.getOptionValues("server.port")); // 輸出: ["8080"]
// 獲取非選項(xiàng)參數(shù)(所有不以 `--` 開(kāi)頭的參數(shù),通常用于傳遞無(wú)標(biāo)記的參數(shù)值)
System.out.println("非選項(xiàng)參數(shù):" + appArgs.getNonOptionArgs()); // 輸出: ["arg1", "arg2"]
}3、接口ApplicationArguments
ApplicationArguments是DefaultApplicationArguments類(lèi)的父接口
// 提供對(duì)用于運(yùn)行應(yīng)用的參數(shù)的訪問(wèn)。
public interface ApplicationArguments {
// 返回傳遞給應(yīng)用程序的原始未處理參數(shù)
String[] getSourceArgs();
// 返回所有選項(xiàng)參數(shù)的名稱(chēng)
Set<String> getOptionNames();
// 返回解析的選項(xiàng)參數(shù)集合中是否包含具有給定名稱(chēng)的選項(xiàng)
boolean containsOption(String name);
// 返回與給定名稱(chēng)的選項(xiàng)參數(shù)關(guān)聯(lián)的值集合
List<String> getOptionValues(String name);
// 返回解析的非選項(xiàng)參數(shù)集合
List<String> getNonOptionArgs();
}getOptionNames():返回所有選項(xiàng)參數(shù)的名稱(chēng)- 例如:參數(shù)是
"--foo=bar --debug",則返回["foo", "debug"]
- 例如:參數(shù)是
getOptionValues(String name):返回與給定名稱(chēng)的選項(xiàng)參數(shù)關(guān)聯(lián)的值集合- 如果選項(xiàng)存在但沒(méi)有值(例如:
"--foo"),返回一個(gè)空集合 - 如果選項(xiàng)存在且有單一值(例如:
"--foo=bar"),返回一個(gè)包含一個(gè)元素的集合["bar"] - 如果選項(xiàng)存在且有多個(gè)值(例如:
"--foo=bar --foo=baz"),返回包含每個(gè)值的集合["bar", "baz"] - 如果選項(xiàng)不存在,返回null
- 如果選項(xiàng)存在但沒(méi)有值(例如:
getNonOptionArgs():返回解析的非選項(xiàng)參數(shù)集合
4、實(shí)現(xiàn)類(lèi)DefaultApplicationArguments
代碼很簡(jiǎn)單,對(duì)外暴露使用DefaultApplicationArguments,內(nèi)部實(shí)現(xiàn)都在Source中
// ApplicationArguments的默認(rèn)實(shí)現(xiàn)類(lèi),用于解析應(yīng)用程序啟動(dòng)時(shí)傳入的參數(shù)。
public class DefaultApplicationArguments implements ApplicationArguments {
private final Source source; // 用于解析和存儲(chǔ)參數(shù)的內(nèi)部輔助類(lèi)
private final String[] args; // 啟動(dòng)時(shí)傳入的原始參數(shù)
// 構(gòu)造函數(shù),使用傳入的參數(shù)數(shù)組初始化對(duì)象
public DefaultApplicationArguments(String... args) {
Assert.notNull(args, "Args must not be null"); // 確保傳入?yún)?shù)不為 null
this.source = new Source(args); // 使用內(nèi)部類(lèi) Source 解析參數(shù)
this.args = args; // 保存原始參數(shù)
}
// 獲取原始未處理的參數(shù)數(shù)組
@Override
public String[] getSourceArgs() {
return this.args;
}
// 獲取所有選項(xiàng)參數(shù)的名稱(chēng)集合
@Override
public Set<String> getOptionNames() {
String[] names = this.source.getPropertyNames();
// 該集合不能被修改(即添加、刪除元素等操作會(huì)拋出 UnsupportedOperationException 異常)
return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(names)));
}
// 檢查是否包含指定名稱(chēng)的選項(xiàng)參數(shù)
@Override
public boolean containsOption(String name) {
return this.source.containsProperty(name);
}
// 獲取指定名稱(chēng)的選項(xiàng)參數(shù)的值集合
@Override
public List<String> getOptionValues(String name) {
List<String> values = this.source.getOptionValues(name);
return (values != null) ? Collections.unmodifiableList(values) : null;
}
// 獲取所有非選項(xiàng)參數(shù)(不以 "--" 開(kāi)頭的參數(shù))
@Override
public List<String> getNonOptionArgs() {
return this.source.getNonOptionArgs();
}
// 內(nèi)部類(lèi),用于處理和解析命令行參數(shù)
// 繼承自 SimpleCommandLinePropertySource,可以獲取選項(xiàng)參數(shù)和非選項(xiàng)參數(shù)
private static class Source extends SimpleCommandLinePropertySource {
// 使用參數(shù)數(shù)組初始化 Source
Source(String[] args) {
super(args);
}
// 獲取所有非選項(xiàng)參數(shù)。
@Override
public List<String> getNonOptionArgs() {
return super.getNonOptionArgs();
}
// 獲取指定名稱(chēng)的選項(xiàng)參數(shù)的值列表
@Override
public List<String> getOptionValues(String name) {
return super.getOptionValues(name);
}
}
}三、Source
Source是DefaultApplicationArguments解析參數(shù)內(nèi)部的真正實(shí)現(xiàn)類(lèi),類(lèi)圖如下,逐一分析。

1、屬性源PropertySource
PropertySource是Spring框架中的一個(gè)核心抽象類(lèi),用于表示屬性(鍵值對(duì))的來(lái)源。通過(guò)將各種配置來(lái)源(如系統(tǒng)屬性、環(huán)境變量、配置文件等)封裝為PropertySource對(duì)象,Spring可以提供統(tǒng)一的接口來(lái)讀取和管理這些配置數(shù)據(jù)。
屬性源名稱(chēng):每個(gè)PropertySource實(shí)例都具有唯一的名稱(chēng),用于區(qū)分不同的屬性源屬性源對(duì)象:PropertySource<T>是一個(gè)泛型類(lèi),其中T代表具體的屬性源類(lèi)型- getProperty(String name):用于在屬性源對(duì)象中檢索具體的屬性,name表示具體屬性的鍵,返回具體屬性的值
public abstract class PropertySource<T> {
protected final Log logger = LogFactory.getLog(getClass());
protected final String name; // 屬性源的名稱(chēng)
protected final T source; // 屬性源的數(shù)據(jù)源對(duì)象
// 使用給定的名稱(chēng)和源對(duì)象創(chuàng)建屬性源
public PropertySource(String name, T source) {
Assert.hasText(name, "Property source name must contain at least one character");
Assert.notNull(source, "Property source must not be null");
this.name = name;
this.source = source;
}
// 使用給定的名稱(chēng)和一個(gè)新的Object對(duì)象作為底層源創(chuàng)建屬性源
public PropertySource(String name) {
this(name, (T) new Object());
}
// 返回屬性源名稱(chēng)
public String getName() {
return this.name;
}
// 返回屬性源的底層源對(duì)象。
public T getSource() {
return this.source;
}
// 判斷屬性源是否包含給定名稱(chēng)的屬性(子類(lèi)可以實(shí)現(xiàn)更高效的算法)
// containsProperty和getProperty參數(shù)的name與上面定義的屬性源名稱(chēng)的name不是一回事
public boolean containsProperty(String name) {
return (getProperty(name) != null);
}
// 返回與給定名稱(chēng)關(guān)聯(lián)的屬性值,如果找不到則返回null,子類(lèi)實(shí)現(xiàn)
@Nullable
public abstract Object getProperty(String name);
...
}2、枚舉屬性源EnumerablePropertySource
EnumerablePropertySource繼承自PropertySource,主要用于定義getPropertyNames()方法,可以獲取屬性源對(duì)象中所有屬性鍵的名稱(chēng)。
public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
// 使用給定的名稱(chēng)和源對(duì)象創(chuàng)建屬性源(調(diào)用父類(lèi)PropertySource的構(gòu)造方法)
public EnumerablePropertySource(String name, T source) {
super(name, source);
}
// 也是調(diào)用父類(lèi)構(gòu)造
protected EnumerablePropertySource(String name) {
super(name);
}
// 判斷屬性源是否包含具有給定名稱(chēng)的屬性(重新了PropertySource的此方法)
@Override
public boolean containsProperty(String name) {
return ObjectUtils.containsElement(getPropertyNames(), name);
}
// 返回所有屬性的名稱(chēng)
public abstract String[] getPropertyNames();
}3、命令行屬性源CommandLinePropertySource
CommandLinePropertySource是Spring框架中用于處理命令行參數(shù)的PropertySource實(shí)現(xiàn)。它可以將應(yīng)用程序啟動(dòng)時(shí)傳入的命令行參數(shù)解析成鍵值對(duì),便于在應(yīng)用配置中使用。
- 命令行屬性源名稱(chēng)默認(rèn)為
commandLineArgs getOptionValues(String name):通過(guò)命令行屬性源(即選項(xiàng)參數(shù)鍵值對(duì))的鍵獲取對(duì)應(yīng)的值getNonOptionArgs():通過(guò)命令行屬性源(即鍵默認(rèn)為nonOptionArgs的非選項(xiàng)參數(shù)鍵值對(duì))獲取對(duì)于的值- 例:java -jar your-app.jar --server.port=8081 --spring.profiles.active=prod arg1 arg2
- 選項(xiàng)參數(shù)會(huì)有
多個(gè)鍵值對(duì),key1為server.port,key2為spring.profiles.active - 非選項(xiàng)參數(shù)
永遠(yuǎn)只有一個(gè)鍵值對(duì),所有key都是nonOptionArgs
- 選項(xiàng)參數(shù)會(huì)有
public abstract class CommandLinePropertySource<T> extends EnumerablePropertySource<T> {
// CommandLinePropertySource實(shí)例的默認(rèn)名稱(chēng)
public static final String COMMAND_LINE_PROPERTY_SOURCE_NAME = "commandLineArgs";
// 表示非選項(xiàng)參數(shù)的屬性鍵的默認(rèn)名稱(chēng)
public static final String DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME = "nonOptionArgs";
private String nonOptionArgsPropertyName = DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME;
// 創(chuàng)建一個(gè)新的命令行屬性源,使用默認(rèn)名稱(chēng)
public CommandLinePropertySource(T source) {
super(COMMAND_LINE_PROPERTY_SOURCE_NAME, source);
}
// 創(chuàng)建一個(gè)新的命令行屬性源,具有給定名稱(chēng)
public CommandLinePropertySource(String name, T source) {
super(name, source);
}
// 可以通過(guò)set方法設(shè)置非選項(xiàng)參數(shù)的鍵的名稱(chēng)
public void setNonOptionArgsPropertyName(String nonOptionArgsPropertyName) {
this.nonOptionArgsPropertyName = nonOptionArgsPropertyName;
}
// 首先檢查指定的名稱(chēng)是否是特殊的“非選項(xiàng)參數(shù)”屬性,
// 如果是,則委托給抽象方法#getNonOptionArgs()
// 否則,委托并返回抽象方法#containsOption(String)
@Override
public final boolean containsProperty(String name) {
if (this.nonOptionArgsPropertyName.equals(name)) {
return !getNonOptionArgs().isEmpty();
}
return this.containsOption(name);
}
// 首先檢查指定的名稱(chēng)是否是特殊的“非選項(xiàng)參數(shù)”屬性,
// 如果是,則委托給抽象方法#getNonOptionArgs(),返回用逗號(hào)隔開(kāi)字符串
// 否則,委托并返回抽象方法#getOptionValues(name),返回用逗號(hào)隔開(kāi)字符串
@Override
@Nullable
public final String getProperty(String name) {
if (this.nonOptionArgsPropertyName.equals(name)) {
Collection<String> nonOptionArguments = getNonOptionArgs();
if (nonOptionArguments.isEmpty()) {
return null;
}
else {
return StringUtils.collectionToCommaDelimitedString(nonOptionArguments);
}
}
Collection<String> optionValues = getOptionValues(name);
if (optionValues == null) {
return null;
}
else {
return StringUtils.collectionToCommaDelimitedString(optionValues);
}
}
// 返回從命令行解析的選項(xiàng)參數(shù)集合中是否包含具有給定名稱(chēng)的選項(xiàng)
protected abstract boolean containsOption(String name);
// 返回與給定名稱(chēng)的選項(xiàng)參數(shù)關(guān)聯(lián)的值集合
@Nullable
protected abstract List<String> getOptionValues(String name);
// 返回從命令行解析的非選項(xiàng)參數(shù)集合,永不為null
protected abstract List<String> getNonOptionArgs();
}4、簡(jiǎn)單命令行屬性源SimpleCommandLinePropertySource
SimpleCommandLinePropertySource是Spring框架中的一個(gè)類(lèi),繼承自CommandLinePropertySource,用于解析和處理命令行參數(shù)。它設(shè)計(jì)為簡(jiǎn)單易用,通過(guò)接收一個(gè)字符串?dāng)?shù)組(即命令行參數(shù) args),將參數(shù)分為"選項(xiàng)參數(shù)"和"非選項(xiàng)參數(shù)"兩類(lèi)。
- 命令行屬性源對(duì)象類(lèi)型為
CommandLineArgs,通過(guò)new SimpleCommandLineArgsParser().parse(args)獲取
public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> {
// 構(gòu)造函數(shù):創(chuàng)建一個(gè)使用默認(rèn)名稱(chēng)commandLineArgs的SimpleCommandLinePropertySource實(shí)例的命令行屬性源
public SimpleCommandLinePropertySource(String... args) {
super(new SimpleCommandLineArgsParser().parse(args));
}
// 創(chuàng)建指定名稱(chēng)命令行屬性源
public SimpleCommandLinePropertySource(String name, String[] args) {
super(name, new SimpleCommandLineArgsParser().parse(args));
}
// 獲取所有選項(xiàng)參數(shù)的名稱(chēng)
@Override
public String[] getPropertyNames() {
return StringUtils.toStringArray(this.source.getOptionNames());
}
// 檢查是否包含指定名稱(chēng)的選項(xiàng)
@Override
protected boolean containsOption(String name) {
return this.source.containsOption(name);
}
// 獲取指定選項(xiàng)名稱(chēng)的值列表
@Override
@Nullable
protected List<String> getOptionValues(String name) {
return this.source.getOptionValues(name);
}
// 獲取所有非選項(xiàng)參數(shù)的列表
@Override
protected List<String> getNonOptionArgs() {
return this.source.getNonOptionArgs();
}
}四、解析參數(shù)原理
在上一節(jié)中,我們了解了應(yīng)用程序參數(shù)args被解析后的結(jié)構(gòu)和存儲(chǔ)方式。接下來(lái),我們回到文章開(kāi)頭,詳細(xì)解析參數(shù)是如何被逐步解析出來(lái)的。
// 3.解析應(yīng)用參數(shù) ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
根據(jù)new DefaultApplicationArguments(args)尋找解析arg的位置

解析完arg調(diào)用父類(lèi)CommandLinePropertySource的構(gòu)造方法

1、解析方法
SimpleCommandLineArgsParser通過(guò)遍歷傳入的命令行參數(shù)數(shù)組,根據(jù)參數(shù)的格式,將參數(shù)解析并分為選項(xiàng)參數(shù)和非選項(xiàng)參數(shù)。
- 選項(xiàng)參數(shù)解析規(guī)則
- 選項(xiàng)參數(shù)
必須以--前綴開(kāi)頭,例如 --name=John 或 --debug - 如果包含等號(hào) =,= 左邊的部分是選項(xiàng)名稱(chēng),右邊的部分是選項(xiàng)值
- 如果沒(méi)有等號(hào),則視為不帶值的選項(xiàng)
- 如果獲取不到選項(xiàng)名稱(chēng)(例如傳入 --=value或–),拋出異常,表示參數(shù)格式無(wú)效
- 選項(xiàng)參數(shù)
- 非選項(xiàng)參數(shù)解析規(guī)則
- 所有
不以--開(kāi)頭的參數(shù)被視為非選項(xiàng)參數(shù)
- 所有
class SimpleCommandLineArgsParser {
public CommandLineArgs parse(String... args) {
// 創(chuàng)建 CommandLineArgs 實(shí)例,用于存儲(chǔ)解析結(jié)果
CommandLineArgs commandLineArgs = new CommandLineArgs();
for (String arg : args) { // 遍歷每個(gè)命令行參數(shù)
if (arg.startsWith("--")) { // 如果參數(shù)以 "--" 開(kāi)頭,則視為選項(xiàng)參數(shù)
String optionText = arg.substring(2); // 去掉 "--" 前綴
String optionName; // 選項(xiàng)名稱(chēng)
String optionValue = null; // 選項(xiàng)值,默認(rèn)為 null
int indexOfEqualsSign = optionText.indexOf('='); // 查找等號(hào)的位置
if (indexOfEqualsSign > -1) { // 如果找到了等號(hào)
optionName = optionText.substring(0, indexOfEqualsSign); // 等號(hào)前的部分為選項(xiàng)名稱(chēng)
optionValue = optionText.substring(indexOfEqualsSign + 1); // 等號(hào)后的部分為選項(xiàng)值
}
else {
optionName = optionText; // 如果沒(méi)有等號(hào),整個(gè)文本為選項(xiàng)名稱(chēng),值為 null
}
// 如果選項(xiàng)名稱(chēng)為空,拋出異常,例如,只輸入了 "--=" 或 "--"
if (optionName.isEmpty()) {
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
// 將解析出的選項(xiàng)名稱(chēng)和值添加到 CommandLineArgs 對(duì)象中
commandLineArgs.addOptionArg(optionName, optionValue);
}
else {
// 如果參數(shù)不是選項(xiàng)參數(shù),直接作為非選項(xiàng)參數(shù)添加到 CommandLineArgs 對(duì)象中
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs; // 返回解析結(jié)果
}
}屬性源對(duì)象類(lèi)型CommandLineArgs
// 命令行參數(shù)的簡(jiǎn)單表示形式,分為“帶選項(xiàng)參數(shù)”和“無(wú)選項(xiàng)參數(shù)”。
class CommandLineArgs {
// 存儲(chǔ)帶選項(xiàng)的參數(shù),每個(gè)選項(xiàng)可以有一個(gè)或多個(gè)值
private final Map<String, List<String>> optionArgs = new HashMap<>();
// 存儲(chǔ)無(wú)選項(xiàng)的參數(shù)
private final List<String> nonOptionArgs = new ArrayList<>();
// 為指定的選項(xiàng)名稱(chēng)添加一個(gè)選項(xiàng)參數(shù),并將給定的值添加到與此選項(xiàng)關(guān)聯(lián)的值列表中(可能有零個(gè)或多個(gè))
public void addOptionArg(String optionName, @Nullable String optionValue) {
if (!this.optionArgs.containsKey(optionName)) {
this.optionArgs.put(optionName, new ArrayList<>());
}
if (optionValue != null) {
this.optionArgs.get(optionName).add(optionValue);
}
}
// 返回命令行中所有帶選項(xiàng)的參數(shù)名稱(chēng)集合
public Set<String> getOptionNames() {
return Collections.unmodifiableSet(this.optionArgs.keySet());
}
// 判斷命令行中是否包含指定名稱(chēng)的選項(xiàng)。
public boolean containsOption(String optionName) {
return this.optionArgs.containsKey(optionName);
}
// 返回與給定選項(xiàng)關(guān)聯(lián)的值列表。
// 表示null表示該選項(xiàng)不存在;空列表表示該選項(xiàng)沒(méi)有關(guān)聯(lián)值。
@Nullable
public List<String> getOptionValues(String optionName) {
return this.optionArgs.get(optionName);
}
// 將給定的值添加到無(wú)選項(xiàng)參數(shù)列表中
public void addNonOptionArg(String value) {
this.nonOptionArgs.add(value);
}
// 返回命令行中指定的無(wú)選項(xiàng)參數(shù)列表
public List<String> getNonOptionArgs() {
return Collections.unmodifiableList(this.nonOptionArgs);
}
}2、解析參數(shù)的存儲(chǔ)和訪問(wèn)
解析方法很簡(jiǎn)單,所有內(nèi)容都在SimpleCommandLineArgsParser的parse方法中完成。相比之下,存儲(chǔ)和訪問(wèn)方式更為復(fù)雜。
存儲(chǔ)位置位于屬性源對(duì)象PropertySource中。從代碼可知,args表示命令行參數(shù),因此屬性源名稱(chēng)為命令行屬性源默認(rèn)名稱(chēng)commandLineArgs,屬性源對(duì)象為解析args后的鍵值對(duì)。

訪問(wèn)查詢(xún)方式的底層實(shí)現(xiàn)就是操作CommandLineArgs中的optionArgs(選項(xiàng)參數(shù))和nonOptionArgs(非選項(xiàng)參數(shù))兩個(gè)集合,但此過(guò)程經(jīng)過(guò)多次跳轉(zhuǎn),最終依次通過(guò) DefaultApplicationArguments -> DefaultApplicationArguments#Source -> SimpleCommandLinePropertySource -> CommandLineArgs獲取,其中CommandLineArgs就是是命令行屬性源對(duì)象。這種設(shè)計(jì)主要是為了提供更靈活、安全的訪問(wèn)方式,避免直接暴露內(nèi)部數(shù)據(jù)結(jié)構(gòu)帶來(lái)的潛在風(fēng)險(xiǎn)。


3、實(shí)際應(yīng)用
之前在SpringBoot基礎(chǔ)(二):配置文件詳解文章中有介紹過(guò)配置文件設(shè)置臨時(shí)屬性,這次回過(guò)頭再來(lái)看,就很清晰明了了。


總結(jié)
- 在SpringBoot啟動(dòng)時(shí),啟動(dòng)類(lèi)main函數(shù)中的
args參數(shù)被解析為兩類(lèi)選項(xiàng)參數(shù)(如 --server.port=8080)非選項(xiàng)參數(shù)(如 arg1、arg2)
- 對(duì)外暴露應(yīng)用參數(shù)對(duì)象
ApplicationArguments提供查詢(xún)方法getOptionValues(String name)方法可以獲取選項(xiàng)參數(shù)getNonOptionArgs()方法則用于獲取非選項(xiàng)參數(shù)- 這些參數(shù)在啟動(dòng)過(guò)程的后續(xù)階段可供使用
到此這篇關(guān)于SpringBoot源碼解析(之如何解析應(yīng)用參數(shù)args的文章就介紹到這了,更多相關(guān)SpringBoot解析應(yīng)用參數(shù)args內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談SpringBoot是如何實(shí)現(xiàn)日志的
這篇文章主要介紹了淺談SpringBoot是如何實(shí)現(xiàn)日志的,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
Spring和Spring?Boot的區(qū)別及說(shuō)明
這篇文章主要介紹了Spring和Spring?Boot的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04
SpringBoot 指標(biāo)監(jiān)控actuator的專(zhuān)題
未來(lái)每一個(gè)微服務(wù)在云上部署以后,我們都需要對(duì)其進(jìn)行監(jiān)控、追蹤、審計(jì)、控制等。SpringBoot就抽取了Actuator場(chǎng)景,使得我們每個(gè)微服務(wù)快速引用即可獲得生產(chǎn)級(jí)別的應(yīng)用監(jiān)控、審計(jì)等功能,通讀本篇對(duì)大家的學(xué)習(xí)或工作具有一定的價(jià)值,需要的朋友可以參考下2021-11-11
Mybatis之通用Mapper動(dòng)態(tài)表名及其原理分析
這篇文章主要介紹了Mybatis之通用Mapper動(dòng)態(tài)表名及其原理分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
Activiti如何啟動(dòng)流程并使流程前進(jìn)
這篇文章主要介紹了Activiti如何啟動(dòng)流程并使流程前進(jìn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
如何利用rabbitMq的死信隊(duì)列實(shí)現(xiàn)延時(shí)消息
這篇文章主要介紹了如何利用rabbitMq的死信隊(duì)列實(shí)現(xiàn)延時(shí)消息問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01

