前言
本文是作者写关于Spring源码的第一篇文章,作者水平有限,所有的源码文章仅限用作个人学习记录。文中如有错误欢迎各位留言指正。
run
java
public ConfigurableApplicationContext run(String... args) {
// 为了计时用的,老版本和新版本不一样
long startTime = System.nanoTime();
// 初始化一个引导器的上下文,这是属于Spring Boot的上下文。后边还有一个Spring的上下文。apach好喜欢context这个东西,证明写框架这个context是真的好用。
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// 这是Spring的上下文,在这里定义,在下面进行的初始化
ConfigurableApplicationContext context = null;
// 配置一个系统属性
configureHeadlessProperty();
// 获取配置文件的监听器 重点 也是扩展点,凡是读取配置文件的地方都是扩展点,因为配置在配置文件中的initializer、listener都会在某个阶段被调用
SpringApplicationRunListeners listeners = getRunListeners(args);
// 调用监听器发送启动事件,这里可以通过自定义监听器消费这个事件,处理自己的逻辑
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 解析命令行参数 将其封装成一个ApplicationArguments,这个类的变量name被设置成commandLineArgs字符串,变量source是解析args封装的CommandLineArgs对象。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
DefaultApplicationArguments
这个类的构造方法中做了一些事情,看一下构造方法。
java
public DefaultApplicationArguments(String... args) {
// 参数判断 自己的业务中可以用这个代替一些if else
Assert.notNull(args, "Args must not be null");
// 创建一个资源对象。这个资源对象中解析了环境信息和参数信息
this.source = new Source(args);
// 将我们的args赋值给了全局变量
this.args = args;
}
重点看一下构造这个source的时候做了什么事情。
new Source(args)
这个类的继承关系。
解析args参数。封装成CommandLineArgs。
java
public SimpleCommandLinePropertySource(String... args) {
super(new SimpleCommandLineArgsParser().parse(args));
}
解析args参数封装成CommandLineArgs。通过这个方法我们知道我们的args参数应该怎么传了,是以双横线开始的,key和value之间用等号连接。
java
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs();
for (String arg : args) {
if (arg.startsWith("--")) {
String optionText = arg.substring(2);
String optionName;
String optionValue = null;
int indexOfEqualsSign = optionText.indexOf('=');
if (indexOfEqualsSign > -1) {
optionName = optionText.substring(0, indexOfEqualsSign);
optionValue = optionText.substring(indexOfEqualsSign + 1);
}
else {
optionName = optionText;
}
if (optionName.isEmpty()) {
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
commandLineArgs.addOptionArg(optionName, optionValue);
}
else {
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs;
}
addOptionArg
每解析一个就添加到CommandLineArgs中。
保存在了这个对象的optionArgs属性中。根据这个属性和这个addOptionArg方法的实现来看,如果同一个key配置了两次会同一个key保存一个集合。
java
private final Map<String, List<String>> optionArgs = new HashMap<>();
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);
}
}
OK 今天先到这里吧。
See you next time :)