SpringApplication类通过run方法开始进行分析
configureHeadlessProperty();
java.awt.headless默认为true,用来配置不存在显示器等系统配置的时候进行使用的配置,类似于服务器。
继续往下看看到了对应的classLoader相关的代码
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
说到classLoader,顺便讲一下classLoader
1.负责将class加载到JVM中
2.ClassLoader 通过双亲委任的方式进行加载类
解释一下双亲加载机制
一个类加载器查找class和resource时,是通过“委托模式”进行的,它首先判断这个class是不是已经加载成功,如果没有的话它并不是自己进行查找,而是先通过父加载器,然后递归下去,直到Bootstrap ClassLoader,如果Bootstrap classloader找到了,直接返回,如果没有找到,则一级一级返回,最后到达自身去查找这些对象。这种机制就叫做双亲委托。
custom <-> AppclassLoader <-> extenstionClassLoader <-> bootstrap classLoader(c++ c)
为什么采用双亲委任机制:
因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。从安全角度考虑,如果不使用这种委托模式,我们自己定义一个Map来动态替代java核心api中定义的类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为Map在启动时就被引导类加载器(Bootstrcp ClassLoader)加载,所以用户自定义的ClassLoader永远也无法加载一个自己写的Map,除非你改变JDK中ClassLoader搜索类的默认算法。
Bootstrap ClassLoader:
j
a
v
a
P
a
t
h
/
l
i
b
e
x
t
e
n
s
i
o
n
C
l
a
s
s
L
o
a
d
e
r
:
javaPath/lib extension ClassLoader :
javaPath/libextensionClassLoader:javaPath/lib/ext
appClass loader:classpath 所有的类
3.显示加载和隐式加载。
显示加载是通过this.getClass | Class.forName | this.getClass.getClassLoader
隐式加载主要是以下几种方式
1)创建类的实例
2)访问某个类/接口的静态变量,或者赋值
3)调用类的静态方法
4)初始化类的子类
5)java虚拟机启动标注启动类的类,比如启动指定 main函数的类
4.源码分析开始
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
在使用springboot的时候,我们是使用 SpringApplication.run(Application.class,args); 开始启动服务,在实例化SpringApplication的时候开始注入相关数据,其中有一项是 this.webApplicationType,打开之后会发现如下代码
/**
* The application should not run as a web application and should not start an
* embedded web server.
*/
NONE,
/**
* The application should run as a servlet-based web application and should start an
* embedded servlet web server.
*/
SERVLET,
/**
* The application should run as a reactive web application and should start an
* embedded reactive web server.deduceWebEnvironment
*/
REACTIVE;
除了我们已知的传统的SERVLET的WEB容器,还采用的新型的REACTIVE响应式编程,就是springboot2.x加入了webFlux,webFlux是一个异步非阻塞式的 Web 框架,它能够充分利用多核 CPU 的硬件资源去处理大量的并发请求。
WebFlux 内部使用的是响应式编程(Reactive Programming),以 Reactor 库为基础, 基于异步和事件驱动,可以让我们在不扩充硬件资源的前提下,提升系统的吞吐量和伸缩性。
我们开始分析springboot启动过程中所有的操作
1.所有监听开始运行
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
看到以上代码就应该清除,springboot所有监听器会在这里进行启动,关于springboot源码监听器有如下几种
name | introduction |
---|---|
ClearCachesApplicationListener | 应用上下文加载完成后对缓存做清除工作,响应事件ContextRefreshedEvent |
ParentContextCloserApplicationListener | 监听双亲应用上下文的关闭事件并往自己的孩子应用上下文中传播,相关事件ParentContextAvailableEvent/ContextClosedEvent |
ClearCachesApplicationListener | 应用上下文加载完成后对缓存做清除工作,响应事件ContextRefreshedEvent |
FileEncodingApplicationListener | 如果系统文件编码和环境变量中指定的不同则终止应用启动。具体的方法是比较系统属性file.encoding和环境变量spring.mandatory-file-encoding是否相等(大小写不敏感) |
AnsiOutputApplicationListener | 根据spring.output.ansi.enabled参数配置AnsiOutput |
ConfigFileApplicationListener | EnvironmentPostProcessor,从常见的那些约定的位置读取配置文件,比如从以下目录读取application.properties,application.yml等配置文件:classpath: file:.classpath:config file:./config/:也可以配置成从其他指定的位置读取配置文件 |
DelegatingApplicationListener | 监听到事件后转发给环境变量context.listener.classes指定的那些事件监听器 |
ClasspathLoggingApplicationListener | 一个SmartApplicationListener,对环境就绪事件ApplicationEnvironmentPreparedEvent/应用失败事件ApplicationFailedEvent做出响应,往日志DEBUG级别输出TCCL(thread context class loader)的classpath。 |
LoggingApplicationListener | 配置LoggingSystem。使用logging.config环境变量指定的配置或者缺省配置 |
LiquibaseServiceLocatorApplicationListener | 使用一个可以和Spring Boot可执行jar包配合工作的版本替换liquibase ServiceLocator |
BackgroundPreinitializer | 尽早触发一些耗时的初始化任务,使用一个后台线程 |
以上是springboot定义的一些listener。每个listener作用各不相同对应的event对应不同生命周期。
关于lisntener,可以看到一个类 EventPublishingRunListener,相关代码如下
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
@Override
public int getOrder() {
return 0;
}
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster
.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should
// use it at this point if we can
context.publishEvent(event);
}
else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}
private static class LoggingErrorHandler implements ErrorHandler {
private static final Log logger = LogFactory.getLog(EventPublishingRunListener.class);
@Override
public void handleError(Throwable throwable) {
logger.warn("Error calling ApplicationEventListener", throwable);
}
}
对应类 EventPublishingRunListener 中的方法,有不同的执行周期,周期如下表
name | introduction |
---|---|
ApplicationStartingEvent | 在Environment和ApplicationContext可用之前 & 在ApplicationListener注册之后发布。 |
ApplicationEnvironmentPreparedEvent | 在context中 Environment被使用的时候并在context被创建之前 |
ApplicationContextInitializedEvent | 在bean定义加载之前 & ApplicationContextInitializers被调用之后 & ApplicationContext开始准备之后发布 |
ApplicationPreparedEvent | 在ApplicationContext完全准备好并且没有刷新之前发布,此时bean定义即将加载,Environment已经准备好被使用。 |
ApplicationStartedEvent | 在ApplicationContext刷新之后,调用ApplicationRunner和CommandLineRunner之前发布 |
ApplicationReadyEvent | 应用已经准备好,可以接收请求 |
ApplicationFailedEvent | 在启动过程中如果发生异常则触发。 |