一个读者,也是我的好朋友投稿的一篇关于 SpringBoot 启动原理的文章,才大二就如此优秀,未来可期。
我一直想了解一下 SpirngBoot 的是如何启动的,我想就来写一篇关于 SpirngBoot 启动分析吧。第一次写那么高深的技术话题理解不到位的话也请多多包涵。
SpinrgBoot 2.0.2
众所周知 SpringBoot 的启动类是在一个 main 方法中调用 SpringApplication.run() 方法启动的,如:
@SpringBootApplication
public class DiveInSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(DiveInSpringBootApplication.class, args);
}
}
启动顺序分析如下:
初始化阶段 -> 运行阶段
进入run方法中, SpringApplication.run() 会先为其创建一个 SpringApplication 对象:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//加载应用资源(URL资源、File资源、ClassPath资源)
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// primarySources 为 run 方法传入的引导类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//推断Web应用类型
this.webApplicationType = deduceWebApplicationType();
//加载应用上下文(初始化Initializers)
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//加载应用事件监听器(初始化ApplicationListener)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//推断引导类
this.mainApplicationClass = deduceMainApplicationClass();
}
deduceWebApplicationType() 来推断我们Web类型应用 private WebApplicationType deduceWebApplicationType() {
//根据当前应用的ClassPath中是否存在相关实现类来推断Web类型
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
看看使用到的 3 个常量值:
| 常量值 | 应用类型 |
|---|---|
| REACTIVE_WEB_ENVIRONMENT_CLASS | org.springframework.web.reactive.DispatcherHandler |
| MVC_WEB_ENVIRONMENT_CLASS | org.springframework.web.servlet.DispatcherServlet |
| WEB_ENVIRONMENT_CLASSES | {"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" } |
也就是说,有如下三种情况:
表示当前应用不是一个web应用,启动时无需加载启动内嵌的 web 服务器。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 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;
}
利用Spirng的工厂加载机制,实例化ApplicationContextInitializer实现类,并排序集合。具体实现方法如下:
SpringFactoriesLoader.loadFactoryNames 来扫描 META-INF/spring.factories 下符合 ApplicationContextInitializer 类型的资源名称。 META-INF/spring.factories 下找到的资源信息 @order 注解和 Ordered 接口进行排序 # Application Context Initializers org.springframework.context.ApplicationContextInitializer=/ #Spring容器的常见的错误配置警告 org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,/ #设置Spring应用上下文ID org.springframework.boot.context.ContextIdApplicationContextInitializer,/ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,/ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 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;
}
利用 Spring 工厂加载机制,实例化 ApplicationListene r实现类,并排序对象集合,具体方法跟上面 初始化Initializers 类似,不赘述。
# Application Listeners org.springframework.context.ApplicationListener=/ #Spring应用上下文加载完成之后清除缓存 org.springframework.boot.ClearCachesApplicationListener,/ #父容器关闭时通知各个子容器关闭, org.springframework.boot.builder.ParentContextCloserApplicationListener,/ #文件编码 org.springframework.boot.context.FileEncodingApplicationListener,/ #控制台彩色输出 org.springframework.boot.context.config.AnsiOutputApplicationListener,/ #外部化配置 管理factories或者YMAL文件 org.springframework.boot.context.config.ConfigFileApplicationListener,/ #将指定事件广播给指定的监听器 org.springframework.boot.context.config.DelegatingApplicationListener,/ #将需要输出的日志打印到指定的级别 DEBUG INFO ERROR org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,/ #初始化日志系统 org.springframework.boot.context.logging.LoggingApplicationListener,/ #控制可执行Spirng文件版本 org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
//根据 Main 线程执行堆栈来判断实际的引导类。
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
整个 SpringApplication 围绕着 run 这个方法并分为两个小阶段:
SpringApplication 运行监听器,并监听 Spring Boot 事件 public ConfigurableApplicationContext run(String... args) {
//记录运行时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//Spring 应用的上下文
ConfigurableApplicationContext context = null;
//记录启动期间的错误
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//配置文件加载及优先级判断
configureHeadlessProperty();
//获取SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
//加载运行监听器
listeners.starting();
try {
//创建ApplicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//加载属性配置
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
//打印Banner
Banner printedBanner = printBanner(environment);
//创建应用上下文
context = createApplicationContext();
//实例化SpringBootExceptionReporter用于报告启动过程错误。
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//初始化应用上下文
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新应用上下文(IOC容器的准备,初始化Bean)
refreshContext(context);
//应用上下刷新完成之后
afterRefresh(context, applicationArguments);
stopWatch.stop();
//启动日志记录器
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//启动运行监听器
listeners.started(context);
//启动后需要的操作
callRunners(context, applicationArguments);
....
}
}
SpringApplication 运行监听器 private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
利用 Spirng 的工厂加载机制,实例化 SpringApplicationRunListeners 实现类,并排序集合。具体实现方法如下:
SpringFactoriesLoader.loadFactoryNames 来扫描 META-INF/spring.factories 下符合 SpringApplicationRunListeners 类型的资源名称。 META-INF/spring.factories 下找到的资源信息 # Run Listeners org.springframework.boot.SpringApplicationRunListener=/ org.springframework.boot.context.event.EventPublishingRunListenr
由此可见就只有 EventPublishingRunListenr 一个实现类
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;
//实例化SimpleApplicationEventMulticaster事件发布者
this.initialMulticaster = new SimpleApplicationEventMulticaster();
//以迭代的方法逐一进行ApplicationListener的监听
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) {
}
@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 Log logger = LogFactory.getLog(EventPublishingRunListener.class);
@Override
public void handleError(Throwable throwable) {
logger.warn("Error calling ApplicationEventListener", throwable);
}
}
}
在 EventPublishingRunListener 实例化的时候,会实例化一个 SimpleApplicationEventMulticaster 事件发布者(它的作用就是监听容器中发布的事件,只要事件发生,就触发监听器的回调,来完成事件驱动开发),于是接下来调用 listeners.starting() 方法就会通过其内部的 initialMulticaster 属性发布 ApplicationStartingEvent 事件。
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
//初始化阶段的推断Web类型
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
根据初始化阶段的推断Web应用类型来创建对应的 ConfigurableApplicationContext 实例
如果推断的为 SERVLETWeb 类型就实例化这个对象
web.servlet.context.AnnotationConfigServletWebServerApplicationContext
Environment private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//创建 ConfigurableEnvironment 对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置 ConfigurableEnvironment
configureEnvironment(environment, applicationArguments.getSourceArgs());
//发布 ApplicationEnvironmentPreparedEvent 事件
listeners.environmentPrepared(environment);
//将 ConfigurableEnvironment 绑定到 SpringApplication 中
bindToSpringApplication(environment);
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
根据初始化阶段的推断Web应用类型来创建对应的 ConfigurableEnvironment 实例。
至此整一个的 SpringBoot 过程已经分析完毕:我们来总结一下: