这里写目录标题
- 1.run()
- 1.1 程序运行监听器 SpringApplicationRunListeners
- 1.2 应用参数 ApplicationArguments
- 启动加载顺序
1.run()
run()
方法是一个SpringBoot
程序的入口
SpringApplication.run(Application.class, args);
看看方法逻辑
/**
* 运行 Spring 应用程序, 创建并刷新一个新的 应用上下文(应用参数环境)(ApplicationContext)
* @param args 应用参数 (usually passed from a Java main method)
* @return 一个运行中的应用上下文
*/
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 {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
分步解析一个SpringBoot
应用程序是怎么创建出来的
1.1 程序运行监听器 SpringApplicationRunListeners
关注语句
SpringApplicationRunListeners listeners = getRunListeners(args);
这里调用了 getRunListeners()
方法,并且把args
参数传进去了,这个是SpringApplication
的方法,进入看看
/** 获取运行监听器 */
private SpringApplicationRunListeners getRunListeners(String[] args) {
// 创建了一个 Class 数组 [SpringApplication.class, String[].class]
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// 创建了一个 SpringApplicationRunListeners 对象返回
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
在getRunListeners()
方法中创建了一个SpringApplicationRunListeners
对象返回,创建途中调用了另外一个成员方法getSpringFactoriesInstances()
,往下探
/** 获取 Spring 工厂实例 */
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 用Spring 工厂加载器加载属于 SpringApplicationRunListener.class 这个类型的工厂的名称集合
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
属于
SpringApplicationRunListener.class
这个类型的工厂的名称集合names
:
0 = “org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer”
1 = “org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener”
2 = “org.springframework.boot.devtools.restart.RestartScopeInitializer”
3 = “org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer”
4 = “org.springframework.boot.context.ContextIdApplicationContextInitializer”
5 = “org.springframework.boot.context.config.DelegatingApplicationContextInitializer”
6 = “org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer”
7 = “org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer”
就是说属于SpringApplicationRunListener
的从属类有8个,都是一些初始化器
和监听器
1.2 应用参数 ApplicationArguments
关注语句
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ApplicationArguments
(应用参数)这个对象,看名字它应该是用来封装SpringBoot
应用的参数的,
在分析这个对象之前,先来看参数args
是什么参数。
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
在主函数中args
是一个string
数组参数,而我们的run()
方法对应的参数是可变string
,没毛病
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
但是好像看完还是不知道这些个String
类型的参数是什么参数,有什么作用
那就直接看ApplicationArguments
应用参数这个对象
/** 提供用于运行Sping应用程序(SpringApplication)的参数的访问。*/
public interface ApplicationArguments {
/** 返回传递给应用程序的原始未处理的参数。*/
String[] getSourceArgs();
/** 返回所有选项参数的名称。 如参数是 "--foo=bar --debug",返回 ["foo", "debug"]。 */
Set<String> getOptionNames();
/** 判断某个选项参数是否存在在选项参数集合里 */
boolean containsOption(String name);
/**
* 获取指定选项参数的值集合
* 如"--foo", 返回 []
* 如"--foo=bar", 返回 ["bar"]
* 如"--foo=bar --foo=baz",返回 ["bar", "baz"]
* 如果指定的选项参数不存在,返回 null
*/
List<String> getOptionValues(String name);
/** 获取非选项参数集合 */
List<String> getNonOptionArgs();
}
看到这个就很清楚了,特别是选项参数
。像jar
包的运行命令
java -jar xxx.jar
java -jar xxx.jar --server.port=8888
这些启动命令行就带有选项参数,所以说args
其实就是运行程序的命令行参数。
接下来深入了解一下ApplicationArguments
的默认实现DefaultApplicationArguments
package org.springframework.boot;
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); // 用 args 创建一个源
this.args = args;
}
/** 这个 source 是 DefaultApplicationArguments 的一个静态内部类
* 继承了 SimpleCommandLinePropertySource (简单命令行属性源)
*/
private static class Source extends SimpleCommandLinePropertySource {
Source(String[] args) { super(args); }
...
}
}
这里看到DefaultApplicationArguments
封装了一个Source
(源)对象,而Source
对象用的是它父类SimpleCommandLinePropertySource
(简单命令行属性源)的构造器,深入!
package org.springframework.core.env;
public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> {
public SimpleCommandLinePropertySource(String... args) {
super((new SimpleCommandLineArgsParser()).parse(args));
}
...
}
SimpleCommandLinePropertySource
(简单命令行属性源)用的是它的父类CommandLinePropertySource<T>
的构造器初始化的,并且创建了一个SimpleCommandLineArgsParser
对象来解析参数,先看看SimpleCommandLineArgsParser
和它的parse()
方法
package org.springframework.core.env;
class SimpleCommandLineArgsParser {
SimpleCommandLineArgsParser() {}
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs(); // CommandLineArgs 对象,用来保存参数名和参数值
String[] var3 = args;
int var4 = args.length;
for(int var5 = 0; var5 < var4; ++var5) {
String arg = var3[var5];
if (arg.startsWith("--")) { // 解析"--"开头的参数
String optionText = arg.substring(2); // 取“--”后面的字符串
String optionValue = null;
int indexOfEqualsSign = optionText.indexOf(61); // 获取“=”的位置(61对应ASCLL字符为“=”)
String optionName;
if (indexOfEqualsSign > -1) { // 有“=”字符
optionName = optionText.substring(0, indexOfEqualsSign); // 取key
optionValue = optionText.substring(indexOfEqualsSign + 1); // 取value
} else { // 无“=”字符
optionName = optionText; // 取key
}
if (optionName.isEmpty()) { // 如果没有选项参数名,报错
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
commandLineArgs.addOptionArg(optionName, optionValue); // 封装到 commandLineArgs 中
} else {
commandLineArgs.addNonOptionArg(arg); // 封装空参
}
}
return commandLineArgs; // 返回
}
}
也就是说 SimpleCommandLineArgsParser 对象对args
按一定规则做参数解析,把原本像--optionName=value
这样的参数解析成optionName
和value
,再封装到一个类似键值对包装类的 CommandLineArgs 对象返回。
接下来看 SimpleCommandLinePropertySource (简单命令行属性源)的父类 CommandLinePropertySource< T > 用这个 CommandLineArgs 对象初始化时做了啥
package org.springframework.core.env;
public abstract class CommandLinePropertySource<T> extends EnumerablePropertySource<T> {
...
public CommandLinePropertySource(T source) { // CommandLineArgs commandLineArgs → T source
super("commandLineArgs", source);
}
...
}
CommandLinePropertySource< T > 又调用了它的父类 EnumerablePropertySource (有限属性源)做初始化,继续进去。
package org.springframework.core.env;
public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
public EnumerablePropertySource(String name, T source) { // (“commandLineArgs",commandLineArgs)
super(name, source);
}
...
}
EnumerablePropertySource (有限属性源)又调用了它的父类 PropertySource< T > (属性源)做初始化,继续
package org.springframework.core.env;
public abstract class PropertySource<T> {
protected final Log logger;
protected final String name;
protected final T source;
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; // 属性名字默认为 “commandLineArgs"
this.source = source; // 保存 commandLineArgs 副本
}
...
}
可以看到 PropertySource< T > 就是尽头了,一路初始化就是为了封装选项参数的key
和value
,最后得到所有参数的
结构图:
启动加载顺序
入口
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
看 SpringApplication
的初始化过程
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader; // 这里传进来的资源加载器是 null
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath(); // Web程序类型是 SERVLET
//
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
在这里new SpringApplication(primarySources)
的时候调用了loadFactoryNames()
加载的工厂名:org.springframework.context.ApplicationContextInitializer