上篇回顧
上一篇發布啟動事件ApplicationStartingEvent, 我們分析springboot發布了啟動事件, 其執行步驟如下
- 首先調用getRunListeners()方法, 獲得一個SpringApplicationRunListeners對象,
- SpringApplicationRunListeners的成員變量listeners是通過getSpringFactoriesInstances()方法獲取的SpringApplicationRunListener子類列表
- 當前只能獲取EventPublishingRunListener,
- 調用SpringApplicationRunListeners對象的starting()方法, 發布SpringApplication啟動事件
- 內部EventPublishingRunListener#starting()方法
- 最終調用SimpleApplicationEventMulticaster#multicastEvent()方法
- 發布了ApplicationStartingEvent事件, 最后執行每個監聽器的onApplicationEvent方法
- 對ApplicationStartingEvent事件感興趣的監聽器
- LoggingApplicationListener 日志監聽器,配置日志
- BackgroundPreinitializer 后臺初始化器, 多線程加載耗時任務
- DelegatingApplicationListener 代理監聽器, 繼續發布事件
- LiquibaseServiceLocatorApplicationListener 將liquibas替換為可以和spring配合工作的版本
目錄
1. DefaultApplicationArguments
2. Source
????2.1 PropertySource
????2.2 CommandLinePropertySource
????2.3 SimpleCommandLinePropertySource
3. SimpleCommandLineArgsParser
4. 總結
1.DefaultApplicationArguments
這一步的主要作用是處理啟動類main函數的參數, 將其封裝為一個DefaultApplicationArguments對象, 為prepareEnvironment()提供參數
public class SpringApplication {
//run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//本文重點
//封裝命令行參數, 傳入參數為main函數的參數args
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//...
}
}
默認的應用參數, args保存原本的命令行參數, source成員變量保存解析后的命令行參數
//默認Application命令行參數類
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;
}
//私有靜態內部類 Source類
private static class Source extends SimpleCommandLinePropertySource {
Source(String[] args) {
//調用父類方法
//解析并封裝命令行參數
super(args);
}
@Override
public List<String> getNonOptionArgs() {
//調用父類方法
return super.getNonOptionArgs();
}
@Override
public List<String> getOptionValues(String name) {
//調用父類方法
return super.getOptionValues(name);
}
}
}
2. Source
Spring支持多種形式的配置, 并按照固定的順序加載, 實現了資源的合理覆蓋, Source類繼承了CommandLinePropertySource, 用來保存命令行參數, 類繼承關系圖:
2.1 PropertySource
所有的資源都繼承了抽象類PropertySource, PropertySource以一個鍵值對的形式來保存spring配置的屬性, 提供了獲取屬性, 屬性名, containsProperty等基本方法
public abstract class PropertySource<T> {
protected final Log logger = LogFactory.getLog(getClass());
//屬性名稱
protected final String name;
//屬性值
protected final T source;
}
2.2 CommandLinePropertySource
抽象命令行參數類, 定義了兩種類型的命令行參數key
- 以--開頭的命令行參數, 保存到key為commandLineArgs的PropertySource中
- 不以--開頭的命令行參數, 保存到key為nonOptionArgs的PropertySource中
public abstract class CommandLinePropertySource<T> extends EnumerablePropertySource<T> {
//命令行參數key
//保存所有的命令行參數
public static final String COMMAND_LINE_PROPERTY_SOURCE_NAME = "commandLineArgs";
public static final String DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME = "nonOptionArgs";
private String nonOptionArgsPropertyName = DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME;
public CommandLinePropertySource(T source) {
//調用父類SimpleCommandLinePropertySource(String name, String[] args)構造函數
//返回一個name為commandLineArgs
//值為source的PropertySource對象
super(COMMAND_LINE_PROPERTY_SOURCE_NAME, source);
}
}
2.3 SimpleCommandLinePropertySource
構造函數中, 調用了SimpleCommandLineArgsParser#parse, 用來解析啟動類main函數中傳入的參數args
public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> {
//調用父類CommandLinePropertySource(T source)構造函數
public SimpleCommandLinePropertySource(String... args) {
//SimpleCommandLineArgsParser#parse解析命令行參數
super(new SimpleCommandLineArgsParser().parse(args));
}
}
3 SimpleCommandLineArgsParser
命令行解析類, 返回一個CommandLineArgs對象, CommandLineArgs內部有兩個成員變量
- optionArgs, HashMap類型, 用來保存以--開頭的屬性
- nonOptionArgs, ArrayList類型, 用來保存沒有以--開頭的屬性
//命令行參數解析類
class SimpleCommandLineArgsParser {
//解析方法
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs();
for (String arg : args) {
if (arg.startsWith("--")) {
//如果是"--"開頭的字符串
//取出"--"之后的字符串optionText
String optionText = arg.substring(2, arg.length());
String optionName;
String optionValue = null;
if (optionText.contains("=")) {
//如果字符串optionText包含"="
//使用"="分割
//"="前面的字符串作為optionName
optionName = optionText.substring(0, optionText.indexOf('='));
//"="后面的字符串作為optionValue
optionValue = optionText.substring(optionText.indexOf('=')+1, optionText.length());
}
else {
//字符串不包含"="
//整個字符串optionText作為optionName
//此時optionValue為null
optionName = optionText;
}
if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
//最后將name和value放入commandLineArgs的optionArgs中
//name為HashMap的key, value為Hashmap的value
commandLineArgs.addOptionArg(optionName, optionValue);
}
else {
//如果不是"--"開頭的
//直接放入到commandLineArgs的nonOptionArgs ArrayList中
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs;
}
}
4. 總結
這一步的主要作用是處理啟動類main函數的參數, 將其封裝為一個DefaultApplicationArguments對象, 為接下來準備環境提供參數
項目中傳入的命令行參數為:
--spring.profiles.active=test
--server.port=9080
--user.name=yangx
--test
yanggx
處理之后返回的DefaultApplicationArguments對象
{
"args":[
"--spring.profiles.active=test",
"--server.port=9080",
"--user.name=yangx",
"--test",
"yanggx"
],
//Source extends SimpleCommandLinePropertySource
"source": {//CommandLinePropertySource最終實現PropertySource
{
"name":"commandLineArgs",//CommandLineArgs
"source":{
"optionArgs":[//Map
{"key":"test","value":null},
{"key":"server.port","value":"9080"},
{"key":"user.name","value":"yangx"},
{"key":"spring.profiles.active","value":"test"},
],
"nonOptionArgs":[ //List
"yanggx"
],
}
}
}
}
下一篇
我們將會在下一篇prepareEnvironment()準備環境, 研究DefaultApplicationArgument的使用, 以及spring各個PropertySource的加載順序和屬性替換