上篇回顾
上一篇发布启动事件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的加载顺序和属性替换