这个故事还是要从启动类开始
public static void main(String[] args) {
SpringApplication.run(XXX.class, args);
}
一层一层点开
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
到这里,先看new SpringApplication(primarySources)做了什么:
1. new SpringApplication
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
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.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();
}
-
primarySources就是我们的主类了,首先检查主类不能为空
-
this.webApplicationType = WebApplicationType.deduceFromClasspath();
判断当前应用类型
我们一般都是原生的SERVLET类型 -
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
这个方法最总会调用SpringFactoriesLoader.loadFactoryNames(type, classLoader)
方法,这个方法在上一篇博客已经写过,会加载所有"META-INF/spring.factories"的文件路径并解析出来
,不清楚的点这里
,然后在根据传入的type.getName()作为key筛选出对应的信息private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = this.getClassLoader(); Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
然后赋值给initializers
-
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); 像第三步一样找出所有的listener赋值给listeners
-
this.mainApplicationClass = this.deduceMainApplicationClass();
从堆栈中有main方法的类作为主程序类private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace(); StackTraceElement[] var2 = stackTrace; int var3 = stackTrace.length; for(int var4 = 0; var4 < var3; ++var4) { StackTraceElement stackTraceElement = var2[var4]; if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException var6) { } return null; }
至此,构造方法已经梳理完毕,是时候看真正的run方法了
2 run方法详解
public ConfigurableApplicationContext run(String... args) {
// 此类通常用于监控开发过程中的性能,而不是生产应用程序的一部分。
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
// 设置java.awt.headless系统属性,默认为true
// Headless模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。
this.configureHeadlessProperty();
// 1 - 获取SpringApplicationRunListeners
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 通知监听者,开始启动
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 2 - 根据SpringApplicationRunListeners以及参数来准备环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
Banner printedBanner = this.printBanner(environment);
//3 - 创建Spring上下文
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 4 - Spring上下文前置处理
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 5 - Spring上下文刷新
this.refreshContext(context);
// 6 - Spring上下文后置处理
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 通知监听者,启动完毕
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
// 通知监听者,运行中
listeners.running(context);
return context;
} catch (Throwable var9) {
// 通知监听者,运行失败
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
这个run方法包含的内容有点多的,根据上面列举出的关键步骤逐个进行分析:
2.1 获取RunListeners
SpringApplicationRunListeners listeners = this.getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
这个方法已经不陌生了,从 META-INF/spring.factories 中读取Key为 org.springframework.boot.SpringApplicationRunListener 的Values,比如在spring-boot包中的定义的spring.factories:
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
我们来看看这个EventPublishingRunListener
是干嘛的:
/**
* {@link SpringApplicationRunListener} to publish {@link SpringApplicationEvent}s.
* <p>
* Uses an internal {@link ApplicationEventMulticaster} for the events that are fired
* before the context is actually refreshed.
*
* @author Phillip Webb
* @author Stephane Nicoll
*/
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
// ...
}
从类文档可以看出,它主要是负责发布SpringApplicationEvent事件的,它会利用一个内部的ApplicationEventMulticaster在上下文实际被刷新之前对事件进行处理
。下面是接口方法,如果上面的代码有细心看,应该不会陌生:每一个方法都会在run里面被调用过
public interface SpringApplicationRunListener {
void starting();
void environmentPrepared(ConfigurableEnvironment environment);
void contextPrepared(ConfigurableApplicationContext context);
void contextLoaded(ConfigurableApplicationContext context);
void started(ConfigurableApplicationContext context);
void running(ConfigurableApplicationContext context);
void failed(ConfigurableApplicationContext context, Throwable exception);
}
2.2 触发所有listeners的starting方法
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
以LoggingApplicationListener为例,触发其onApplicationEvent方法
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartingEvent) {
onApplicationStartingEvent((ApplicationStartingEvent) event);
}
else if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
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();
}
}
onApplicationStartingEvent方法会对日志系统做初始化
private void onApplicationStartingEvent(ApplicationStartingEvent event) {
this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
this.loggingSystem.beforeInitialize();
}
2.3 准备Environment环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
创建环境,根据webApplicationType创建不同实例
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared((ConfigurableEnvironment)environment);
this.bindToSpringApplication((ConfigurableEnvironment)environment);
// 非Web环境处理
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
配置环境的方法:
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService)conversionService);
}
this.configurePropertySources(environment, args);
this.configureProfiles(environment, args);
}
所以这里实际上包含了两个步骤:
1.配置 Property Sources
2.配置 Profiles,为应用程序环境配置哪些配置文件处于active(活动)状态。
对于Web应用而言,得到的environment变量是一个StandardServletEnvironment的实例。得到实例后,会调用前面RunListeners中的environmentPrepared
方法:
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
在这里,定义的广播器就派上用场了,它会发布一个 ApplicationEnvironmentPreparedEvent 事件
。
那么有发布就有监听,在构建 SpringApplication 实例的时候不是初始化过一些 ApplicationListeners 嘛,其中的Listener就可能会监听ApplicationEnvironmentPreparedEvent事件,然后进行相应处理。
所以这里 SpringApplicationRunListeners 的用途和目的也比较明显了,它实际上是一个事件中转器,它能够感知到Spring Boot启动过程中产生的事件,然后有选择性的将事件进行中转
。
那么既然有事件的转发,是谁在监听这些事件呢,在这个类的构造器中交待了:
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
Iterator var3 = application.getListeners().iterator();
while(var3.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var3.next();
this.initialMulticaster.addApplicationListener(listener);
}
}
前面在构建 SpringApplication 实例过程中设置的监听器在这里(SpringApplicationRunListeners listeners = this.getRunListeners(args);
)被逐个添加到了 initialMulticaster 对应的 ApplicationListener 列表中。所以当 initialMulticaster 调用 multicastEvent 方法时,这些 Listeners 中定义的相应方法就会被触发了。
2.4 创建Spring Context
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
根据项目类型创建容器,对于servlet类型创建AnnotationConfigServletWebServerApplicationContext
2.5 Spring Context前置处理
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 将环境和上下文关联起来
context.setEnvironment(environment);
// 为上下文配置Bean生成器以及资源加载器(如果它们非空)
this.postProcessApplicationContext(context);
// 调用初始化器
this.applyInitializers(context);
// 触发Spring Boot启动过程的contextPrepared事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 添加两个Spring Boot中的特殊单例Beans - springApplicationArguments以及springBootBanner
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 加载sources - 对于DemoApplication而言,这里的sources集合只包含了它一个class对象// 加载sources - 对于DemoApplication而言,这里的sources集合只包含了它一个class对象
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 加载动作 - 构造BeanDefinitionLoader并完成Bean定义的加载 // 加载动作 - 构造BeanDefinitionLoader并完成Bean定义的加载
this.load(context, sources.toArray(new Object[0]));
// 触发Spring Boot启动过程的contextLoaded事件
listeners.contextLoaded(context);
}
关键步骤:
-
配置Bean生成器以及资源加载器(如果它们非空):
protected void postProcessApplicationContext(ConfigurableApplicationContext context) { if (this.beanNameGenerator != null) { context.getBeanFactory().registerSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator", this.beanNameGenerator); } if (this.resourceLoader != null) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext)context).setResourceLoader(this.resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader)context).setClassLoader(this.resourceLoader.getClassLoader()); } } if (this.addConversionService) { context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance()); } }
-
调用初始化器
protected void applyInitializers(ConfigurableApplicationContext context) { Iterator var2 = this.getInitializers().iterator(); while(var2.hasNext()) { ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next(); Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }
这里终于用到了在创建 SpringApplication实例 时设置的初始化器了,依次对它们进行遍历,并调用initialize方法。
-
遍历所有listeners调用contextPrepared()方法
public void contextPrepared(ConfigurableApplicationContext context) { Iterator var2 = this.listeners.iterator(); while(var2.hasNext()) { SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next(); listener.contextPrepared(context); } }
-
遍历所有listeners调用contextLoaded()方法
public void contextLoaded(ConfigurableApplicationContext context) { Iterator var2 = this.listeners.iterator(); while(var2.hasNext()) { SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next(); listener.contextLoaded(context); } }
2.6 Spring Context刷新
private void refreshContext(ConfigurableApplicationContext context) {
this.refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}
}
调用父类一系列的refresh操作,涉及到了很多核心操作,因此耗时会比较长,参照spring启动流程
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext)applicationContext).refresh();
}
注册一个关闭容器时的钩子函数
public void registerShutdownHook() {
if (this.shutdownHook == null) {
this.shutdownHook = new Thread() {
public void run() {
synchronized(AbstractApplicationContext.this.startupShutdownMonitor) {
AbstractApplicationContext.this.doClose();
}
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
如果没有提供自定义的shutdownHook,那么会生成一个默认的,并添加到Runtime中。默认行为就是调用它的doClose方法,完成一些容器销毁时的清理工作。
2.7 Spring Context后置处理
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
2.8 触发所有listeners的startted方法
public void started(ConfigurableApplicationContext context) {
Iterator var2 = this.listeners.iterator();
while(var2.hasNext()) {
SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
listener.started(context);
}
}
2.9 调用callRunners方法
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
Iterator var4 = (new LinkedHashSet(runners)).iterator();
while(var4.hasNext()) {
Object runner = var4.next();
if (runner instanceof ApplicationRunner) {
this.callRunner((ApplicationRunner)runner, args);
}
if (runner instanceof CommandLineRunner) {
this.callRunner((CommandLineRunner)runner, args);
}
}
}
获取所有的ApplicationRunner和CommandLineRunner,合并所有runner并排序,遍历调用run方法
2.10 遍历调用所有listeners的running方法
public void running(ConfigurableApplicationContext context) {
Iterator var2 = this.listeners.iterator();
while(var2.hasNext()) {
SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
listener.running(context);
}
}
2.11 如果出现异常,遍历调用所有listeners的方法failed方法并关闭容器**
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception, Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
try {
try {
this.handleExitCode(context, exception);
if (listeners != null) {
listeners.failed(context, exception);
}
} finally {
this.reportFailure(exceptionReporters, exception);
if (context != null) {
context.close();
}
}
} catch (Exception var9) {
logger.warn("Unable to close ApplicationContext", var9);
}
ReflectionUtils.rethrowRuntimeException(exception);
}
至此,springBoot启动完毕
推荐阅读spring (五)容器创建总结