索引
- SpringBoot源码学习(一) 启动流程解析
- SpringBoot源码学习(二) 初始化环境,创建容器,初始化Failure Analyzers
- SpringBoot源码学习(三) BeanFactory 与 ApplicationContext
- SpringBoot源码学习(四) prepareContext
- SpringBoot源码学习(五) refreshContext 正在更新...
前言
- java社区里面spring全家桶一直是支柱一般的存在。 其中springboot甚至已经不能以火热来形容了, 甚至已经形成了行业某种意义上的”标准“ 。 所以了解并探究springboot的实现是非常有意义的。
- 本文基于springboot 2.0.4, spring5 的源代码进行分析。
正文
启动第一个springboot应用
- 下载测试工程
- 在IDEA上导入项目 并执行SpringBootDemoApplication.class中的main方法
- 结果如下
如何启动的
- 通过代码我们可以看到启动一个springboot应用,非常简单。 只需要一行代码
SpringApplication.run(SpringBootDemoApplication.class, args);
- debug进去发现其实就是首先new了一个SpringApplication实例,再执行该实例的run方法. 如图
new SpringApplication发生了什么
- 先看一下源码
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = deduceWebApplicationType();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
- 在new SpringApplication 的时候核心是做了两件事 1. 判断当前spring运行的环境 2. 加载META-INF/spring.factories 并初始化监听器
- 判断当前spring运行的环境
- 我们debug到源码里面可以看到, 其实就是根据当前的运行环境有没有某个类来判断的
private WebApplicationType deduceWebApplicationType() {
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
- 简单来说,目前springboot支持三种运行环境
public enum 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.
* 普通的WEB应用
*/
SERVLET,
/**
* The application should run as a reactive web application and should start an
* embedded reactive web server.
* 响应式的WEB应用 还比较新 提供了很多新特性 spring4刚刚支持
*/
REACTIVE
}
- 可以通过加载不同starter来切换环境
- 加载META-INF/spring.factories
- 实际上setInitializers, setListeners这两个方法就是扫描spring.factories类并把实例加载到内存中
执行.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 {
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;
}
其中第一个比较核心是 这两行 获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
- 首先springboot会首先获取SpringApplicationRunListeners 根据spring.factories的定义 实际上实例化的时候是EventPublishingRunListener这个listener
- 看一下EventPublishingRunListener的构造函数
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);
}
}
- 在这里会把所有通过spring.factories实例化的listener添加到SimpleApplicationEventMulticaster中。
- 第二步执行starting方法, 会发布一个ApplicationStartingEvent
- 我们继续跟进进去,发现了另外一个核心的方法multicastEvent, 我们查一下 有四个listener注册了ApplicationStartingEvent这个事件
- 我们随便找了一个LoggingApplicationListener 看一下核心代码
@Override
public void onApplicationEvent(ApplicationEvent event) {
//在springboot启动的时候
if (event instanceof ApplicationStartedEvent) {
onApplicationStartedEvent((ApplicationStartedEvent) event);
}
//springboot的Environment环境准备完成的时候
else if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
//在springboot容器的环境设置完成以后
else if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
//容器关闭的时候
else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event)
.getApplicationContext().getParent() == null) {
onContextClosedEvent();
}
//容器启动失败的时候
else if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
- springboot如何判断一个listener支持什么事件。 debug进去可以发现是通过GenericApplicationListener 这个接口实现的
- 因为我们的事件类型为ApplicationEvent,所以会执行onApplicationStartedEvent((ApplicationStartedEvent) event);。springBoot会在运行过程中的不同阶段,发送各种事件,来执行对应监听器的对应方法。
小结
- 根据上文,我们可以简单的做一下小结,在执行了SpringApplication.run(SpringBootDemoApplication.class, args)后发生了什么
- new了一个SpringApplication实例
- 判断当前spring运行的环境
- 加载META-INF/spring.factories 并初始化监听器
- SpringApplications实例.run
- 获取并启动监听器
- 实例化EventPublishingRunListener
- 把所有通过spring.factories实例化的listener添加到SimpleApplicationEventMulticaster中。
- 发布ApplicationStartingEvent 执行注册了这个事件的listener
- 后面还会进行
- 构造容器环境
- 创建容器
- 实例化Failure Analyzers 处理启动的报错信息
- 准备容器
- 刷新容器
- 刷新容器的后扩展接口
- 后面因为涉及到了spring的核心容器部分,会单独开几章来讲,敬请期待~