SpringBoot启动流程
一、SpringApplication 构造
run方法启动
public class SpringApplication { 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); } public SpringApplication(Class<?>... primarySources) { this(null, primarySources); } }
构造函数做了什么
public class SpringApplication { public SpringApplication(Class<?>... primarySources) { this(null, primarySources); } public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { // 资源加载器, 实际是null this.resourceLoader = resourceLoader; // 配置类不能位null, 一般只设置一个主配置类 Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 判断应用类型, 暂时只用管 Servlet 应用即可 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 加载 META-INF/spring.factories 文件, 注意 META-INF/spring.factories 的加载是一次性加载的 // 全部加载完毕后, 取出所有键为 BootstrapRegistryInitializer 的类 this.bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); // META-INF/spring.factories 所有键为 ApplicationContextInitializer 的类, 赋值字段 initializers setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // META-INF/spring.factories 所有键为 ApplicationListener 的类, 赋值字段 listeners setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // main 函数所在类 this.mainApplicationClass = deduceMainApplicationClass(); } }
三种 Listener
- BootstrapRegistryInitializer:只会被调用一次,后面可以看到,最先执行,在 BootstrapContext 创建后立即执行,这时我们平时应用中使用的 ApplicationContext 都还没有创建呢,【帮助 BootstrapContext 初始化】
- ApplicationContextInitializer:在 ApplicationContext 被创建,设置了 Environment 后,未 refresh 前调用,基本上可认为在 ApplicationContext 还为初始化前调用,【帮助 ApplicationContext 初始化】
- ApplicationListener:响应各种事件,会被 EventPublishingRunListener(SpringApplicationRunListeners) 调用
- SpringApplicationRunListener:【暂略】
二、run流程
1. BootstrapContext 创建
public class SpringApplication { private DefaultBootstrapContext createBootstrapContext() { /// 默认启动器 DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); // 调用所有 NETA-INF/spring.factories 下找到的 BootstrapRegistryInitializer 对象的 initialize 初始化方法 // 传入 BootstrapRegistry 作为参数, 可以看下提高了哪些API this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext)); return bootstrapContext; } }
- 创建 BootstrapRegistry 实例 DefaultBootstrapContext
- 调用 BootstrapRegistryInitializer#initialize,参数为刚刚创建的 BootstrapRegistry
2. 查找 SpringApplicationRunListener
public class SpringApplication { private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; // SpringApplicationRunListeners 封装了所有 SpringApplicationRunListener 实例 return new SpringApplicationRunListeners(logger, // META-INF/spring.factories 内的 SpringApplicationRunListener 并创建实例 getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), // SpringApplication 的, 默认是 DefaultApplicationStartup(基本啥也没干), 应该是监控埋点, 监控启动时的各种性能指标的 // 说白了第三方可以进行扩展, 暂时略 this.applicationStartup); } }
下面看一下 SpringApplicationRunListener 的各个方法
public interface SpringApplicationRunListener { /** * 差不多当调用run方法后会立即调用,可以用于非常早期的初始化' * BootstrapRegistryInitializer#initialize -> starting */ default void starting(ConfigurableBootstrapContext bootstrapContext) { } /** * Environment 环境准备好之后调用, 此时 ApplicationContext 还未创建 */ default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { } // -- 中间 PrintBanner, 打印了 Banner /** * ApplicationContext 创建后 -> setEnvironment -> ApplicationContextInitializer#initialize -> contextPrepared * 这个执行完毕, BootstrapContext 就会 close 了 */ default void contextPrepared(ConfigurableApplicationContext context) { } /** * #contextPrepared -> BootstrapContext#close -> ApplicationContext#register(我们传入的主配置类) -> contextLoaded * 注意此时还未 refresh */ default void contextLoaded(ConfigurableApplicationContext context) { } // -- 中间 ApplicationContext#refresh, 解析创建Bean实例了 /** * ApplicationContext#refresh 已被调用, CommandLineRunner、ApplicationRunner 未被调用 */ default void started(ConfigurableApplicationContext context, Duration timeTaken) { started(context); } // 已废弃, 略 @Deprecated default void started(ConfigurableApplicationContext context) { } /** * CommandLineRunner、ApplicationRunner 已被调用, run 方法马上结束 */ default void ready(ConfigurableApplicationContext context, Duration timeTaken) { running(context); } // 已废弃, 略 @Deprecated default void running(ConfigurableApplicationContext context) { } /** * 在启动过程发生失败时调用, run 中发生异常(Throwable)被 catch 时调用, 表明启动失败 */ default void failed(ConfigurableApplicationContext context, Throwable exception) { } }
3. 命令行参数解析
public class DefaultApplicationArguments { public DefaultApplicationArguments(String... args) { Assert.notNull(args, "Args must not be null"); this.source = new Source(args); this.args = args; } } private static class Source extends SimpleCommandLinePropertySource { Source(String[] args) { super(args); } } public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> { public SimpleCommandLinePropertySource(String... args) { super(new SimpleCommandLineArgsParser().parse(args)); } } public abstract class CommandLinePropertySource<T> extends EnumerablePropertySource<T> { public static final String COMMAND_LINE_PROPERTY_SOURCE_NAME = "commandLineArgs"; public static final String DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME = "nonOptionArgs"; private String nonOptionArgsPropertyName = DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME; public CommandLinePropertySource(T source) { super(COMMAND_LINE_PROPERTY_SOURCE_NAME, source); } } public abstract class EnumerablePropertySource<T> extends PropertySource<T> { public EnumerablePropertySource(String name, T source) { super(name, source); } } public abstract class PropertySource<T> { public PropertySource(String name, T source) { this.name = name; this.source = source; } } /** * 解析 String数组, 这个数组一般是命令函参数 * 解析 --开头的 类型的为 【option arguments】, 大致理解为键值对形式的 * 其他一般的非 --开头的键值对形式的存储在另一个List中 * 注意: IDEA 里面 VM options 里配置的是 -Dproperty=val 的被 System.getProperty 获取的属性, Program Arguments 才是命令行的 * * 【注意等号两侧不能有空格, 否则作为两个参数解析(连续的是一个)】 * 【注意下面的引号表示这是一个字符串】 * --foo ---> 键foo, 值null * --foo ---> 键foo, 值 ""(空串) * --foo="" ---> 键foo, 值 ""(空串) * --foo=bar ---> 键foo, 值bar * --foo="bar" ---> 键foo, 值bar * --foo="bar then baz" ---> 键foo, 值"bar then baz" * --foo=bar,then,baz ---> 键foo, 值"bar then baz" * --foo=bar --foo=baz --foo=biz ---> 键foo, 值有三个, bar、baz、biz, List存储值的(非Set) * ---> * ---> * ---> */ class SimpleCommandLineArgsParser { public CommandLineArgs parse(String... args) { CommandLineArgs commandLineArgs = new CommandLineArgs(); for (String arg : args) { if (arg.startsWith("--")) { String optionText = arg.substring(2); String optionName; String optionValue = null; int indexOfEqualsSign = optionText.indexOf('='); if (indexOfEqualsSign > -1) { optionName = optionText.substring(0, indexOfEqualsSign); optionValue = optionText.substring(indexOfEqualsSign + 1); } else { optionName = optionText; } if (optionName.isEmpty()) { throw new IllegalArgumentException("Invalid argument syntax: " + arg); } // 键值对象形式存储在 Map<String, List<String>> optionArgs commandLineArgs.addOptionArg(optionName, optionValue); } else { // 非键值对形式的存储在 List<String> nonOptionArgs commandLineArgs.addNonOptionArg(arg); } } return commandLineArgs; } }
- 命令行参数形式与解析,略
- 应该是每一种 Property 都应该有其名称,命令行的就是 commandLineArgs
DefaultApplicationArguments的继承关系比较简单,继承ApplicationArguments,就是最顶层的接口了,下面列出API,就不解释了
public interface ApplicationArguments { // 原始未解析的数组形式的 String[] getSourceArgs(); Set<String> getOptionNames(); boolean containsOption(String name); List<String> getOptionValues(String name); List<String> getNonOptionArgs(); }
但是Source的继承关系比较复杂,但还好都是单继承形式的 Source -> SimpleCommandLinePropertySource -> CommandLinePropertySource -> EnumerablePropertySource -> PropertySource
暂时略,使用到再回来
4. Environment 准备
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // Create and configure the environment // Servlet 应用创建了 ApplicationServletEnvironment 对象, 内部已经读取了 System.properties 和 System.env // 内部层层向上的初始化很复杂 ConfigurableEnvironment environment = getOrCreateEnvironment(); // getSourceArgs 得到的是未处理过的命令行参数, String[] 类型, 基本上就是main函数的 args // 解析命令行参数并将其作为 PropertySource 添加进入 Environment configureEnvironment(environment, applicationArguments.getSourceArgs()); // 将所有PropertySource封装为一个 ConfigurationPropertySourcesPropertySource, 优先级最高, 键为 configurationProperties ConfigurationPropertySources.attach(environment); // 调用所有 SpringApplicationRunListener 的 environmentPrepared, 表示启动环境准备好了 // SpringApplicationRunListeners 持有所有 SpringApplicationRunListener // 有一个 SpringApplicationRunListener 为 EventPublishingRunListener, 它(持有SpringApplication, 而 SpringApplication 在创建时又初始化并持有所有 ApplicationListener)分发各种事件给 ApplicationListener // 可以看一下 ApplicationListener 的API, 很简单, 只响应事件 // 有一个 ApplicationListener 为 EnvironmentPostProcessorApplicationListener, 它只响应环境相关事件, 持有 META-INF/spring.factories 下的所有 EnvironmentPostProcessor // 有一个 EnvironmentPostProcessor 为 SystemEnvironmentPropertySourceEnvironmentPostProcessor, 它在 environmentPrepared 时 替换 systemEnvironment 为 OriginAwareSystemEnvironmentPropertySource, 名称不变 // 有一个 EnvironmentPostProcessor 为 ConfigDataEnvironmentPostProcessor, 读取我们的配置文件的 listeners.environmentPrepared(bootstrapContext, environment); // 移动名为 defaultProperties 的 PropertySource, 默认是没有这个 PropertySource 的 DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties."); // 绑定环境中spring.main开头属性绑定到SpringApplication对象中, 实际就是设置一些 SpringApplication 的属性, 比如 spring.main.xxx=log bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()) // 这里实际没转 .convertEnvironmentIfNecessary(environment, // 根据运行类型, 如Servlet的WEB deduceEnvironmentClass()); } // 再来一次, 保证名为 configurationProperties 的优先级最高, 且含所有 PropertySource ConfigurationPropertySources.attach(environment); return environment; }
getOrCreateEnvironment 先略
configureEnvironment 解析命令行参数加入 Environment,名称为 commandLineArgs 的命令行参数 PropertySource
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { // 默认 true if (this.addConversionService) { // 转换器?干什么的 environment.setConversionService(new ApplicationConversionService()); } // 读取命令行参数(--开头的) configurePropertySources(environment, args); // 配置profile, 内部实现为空 configureProfiles(environment, args); }
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { // 得到 MutablePropertySources 对象 // 四个 servletConfigInitParams 、 servletContextInitParams 、 systemProperties 、systemEnvironment MutablePropertySources sources = environment.getPropertySources(); // false, defaultProperties 为null, 还未初始化 if (!CollectionUtils.isEmpty(this.defaultProperties)) { DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources); } // addCommandLineProperties 添加命令行参数, 默认为true, 且有命令行参数才添加 if (this.addCommandLineProperties && args.length > 0) { // commandLineArgs, 每个 PropertySource 都是有其名称的 String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; // 没有 if (sources.contains(name)) { PropertySource<?> source = sources.get(name); CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource( new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args)); composite.addPropertySource(source); sources.replace(name, composite); } else { // addFirst, 那么命令行参数的优先级显然更高, new SimpleCommandLinePropertySource 进行解析了 // add的类型只要求是 PropertySource sources.addFirst(new SimpleCommandLinePropertySource(args)); } } }
ConfigurationPropertySources.attach,PropertySource 封装
将所有 PropertySource 封装在 名为 configurationProperties 的 PropertySource 中,实际有 PropertySource 的变化必然会体现在 sources 中,而它持有 sources 引用作为 source
public static void attach(Environment environment) { Assert.isInstanceOf(ConfigurableEnvironment.class, environment); // 五个 命令行 > servletConfigInitParams >servletContextInitParams >systemProperties >systemEnvironment MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources(); // getAttached 是判断是否已经有名为 configurationProperties 这个 PropertySource, 有则获取 PropertySource<?> attached = getAttached(sources); // ? 看下面创建 PropertySource 时将 sources 作为 source 了, 二次进入如果没有意外 getSource 必然和 sources 相等 if (attached != null && attached.getSource() != sources) { sources.remove(ATTACHED_PROPERTY_SOURCE_NAME); attached = null; } if (attached == null) { // 封装为 ConfigurationPropertySourcesPropertySource, SpringConfigurationPropertySources 持有 sources // 内部暂时没有做什么解析 sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME, new SpringConfigurationPropertySources(sources))); } }
listeners.environmentPrepared(bootstrapContext, environment)
环境准备,调用 SpringApplicationRunListeners,而 SpringApplicationRunListeners 持有所有 SpringApplicationRunListener,也就是调用 所有 SpringApplicationRunListener#environmenPrepared
有一个 SpringApplicationRunListener 为 EventPublishingRunListener,它持有 SpringApplication 实例,通过这个实例间接持有所有 ApplicationListener 实例,EventPublishingRunListener 分发事件,ApplicationListener 监听事件
class SpringApplicationRunListeners { // SpringApplicationRunListener 的其他方法都是这样调用的, 不过是下面硬编码的字符串不同, 还有调用 listener. 的方法不同 void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { doWithListeners("spring.boot.application.environment-prepared", (listener) -> listener.environmentPrepared(bootstrapContext, environment)); } private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) { doWithListeners(stepName, listenerAction, null); } private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) { // SpringApplication 创建时传过来的, 对启动过程进行监控, 默认的几乎没有实现 StartupStep step = this.applicationStartup.start(stepName); // SpringApplicationRunListener 监听器执行 this.listeners.forEach(listenerAction); if (stepAction != null) { stepAction.accept(step); } step.end(); } }
EventPublishingRunListener -> SpringApplicationRunListener
先略过它的实现,它持有 SpringApplication 实例,构造时获取了 SpringApplication 构造函数时获取的所有 ApplicationListener
内部持有一个事件分发/广播器 SimpleApplicationEventMulticaster,由它持有 SpringApplication ,并广播一个事件
环境 Environment 初始化相关
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { // SimpleApplicationEventMulticaster this.initialMulticaster.multicastEvent( // 事件对象 new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment)); } }
EnvironmentPostProcessorApplicationListener -> ApplicationListener
根据名称就知道是 Environment 相关的
public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered { // 支持的事件 public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) { // 环境准备 -> 准备好了 -> run 失败 return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType) || ApplicationPreparedEvent.class.isAssignableFrom(eventType) || ApplicationFailedEvent.class.isAssignableFrom(eventType); } private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); SpringApplication application = event.getSpringApplication(); // getEnvironmentPostProcessors 略具体逻辑, 大致是获取 META-INF/spring.factories 的 所有 EnvironmentPostProcessor for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(), event.getBootstrapContext())) { postProcessor.postProcessEnvironment(environment, application); } } }
ConfigDataEnvironmentPostProcessor --> EnvironmentPostProcessor
加载 ConfigData 配置数据到 Environment,我们配置的东西就会被加载进去
比较复杂,略
RandomValuePropertySourceEnvironmentPostProcessor -> EnvironmentPostProcessor
随机数的
5. 打印 Banner
直接略
private Banner printBanner(ConfigurableEnvironment environment) { // banner模式,可以是console、log、off, 默认 CONSOLE, 前一步 prepareEnvironment 有读取配置 if (this.bannerMode == Banner.Mode.OFF) { return null; } ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader : new DefaultResourceLoader(null); // this.banner 一般默认为 null, 好像是 fallbackBanner, 就是没有其他选择才使用这个 ? SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner); // spring.banner.image.location /(若没配置则) 类路径下 banner.gif、jpg、png // 若上面没有则 spring.banner.location / (若没配置则) 类路径下 banner.txt if (this.bannerMode == Mode.LOG) { return bannerPrinter.print(environment, this.mainApplicationClass, logger); } return bannerPrinter.print(environment, this.mainApplicationClass, System.out); }
6. 创建 SpringContext 容器
public class SpringApplication { protected ConfigurableApplicationContext createApplicationContext() { // AnnotationConfigServletWebServerApplicationContext return this.applicationContextFactory.create(this.webApplicationType); } } public interface ApplicationContextFactory { ApplicationContextFactory DEFAULT = (webApplicationType) -> { try { switch (webApplicationType) { case SERVLET: // Servlet 的这个 !!! return new AnnotationConfigServletWebServerApplicationContext(); case REACTIVE: return new AnnotationConfigReactiveWebServerApplicationContext(); default: // 诶, Spring常用的这个 return new AnnotationConfigApplicationContext(); } } }; } // 这里内部的初始化基本就是 Spring 中 AnnotationConfigApplicationContext 初始化的那套了, 这里就不说了 public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { public AnnotationConfigServletWebServerApplicationContext() { // 内部注册的重要的 ConfigurationClassPostProcessor this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } }
context.setApplicationStartup(this.applicationStartup);
7. SpringContext 容器准备
待续 .....
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 环境设置 context.setEnvironment(environment); // 预处理, 实际基本啥也没做, 添加了一个 ConversionService, 暂略 postProcessApplicationContext(context); // ApplicationContextInitializer#initialize 调用 !!! applyInitializers(context); // 发布上下文准备完成事件到所有监听器 listeners.contextPrepared(context); // Bootstrap 环境关闭 bootstrapContext.close(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); // 注册命令行参数Bean beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { // 注册Banner的Bean beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof AbstractAutowireCapableBeanFactory) { ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences); if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources // 传入的主配置类 Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 加载Bean到上下文, 说白了就是向 SpringWeb 的 Application#register 注册所有主配置类的 BD load(context, sources.toArray(new Object[0])); // 发送上下文加载完成事件 listeners.contextLoaded(context); }
8. refresh
public class SpringApplication { private void refreshContext(ConfigurableApplicationContext context) { // 注册钩子, 在JVM退出时执行 if (this.registerShutdownHook) { // 添加:Runtime.getRuntime().addShutdownHook() // 移除:Runtime.getRuntime().removeShutdownHook(this.shutdownHook) shutdownHook.registerApplicationContext(context); } // ApplicationContext真正开始初始化容器和创建bean的阶段 refresh(context); } protected void refresh(ConfigurableApplicationContext applicationContext) { // 这里暂略, 里面不仅有传统的 AnnotationConfigApplicationContext#refresh 的那一套 applicationContext.refresh(); } }
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { // 空壳方法 }
9. ApplicationRunner 和 CommandLineRunner
public class SpringApplication { // 结束时间 Duration timeTakeToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakeToStartup); } // 应用程序启动事件 listeners.started(context, timeTakeToStartup); // 执行实现ApplicationRunner、CommandLineRunner的run方法 callRunners(context, applicationArguments); // --------------------- private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<>(); // ApplicationContext 中取出来的, 什么时候放入的, 待续 ....... runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } } }
10. 完成
public class SpringApplication { catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); // 应用准备好了 listeners.ready(context, timeTakenToReady); } catch (Throwable ex) { handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context; // --------------------------------------- private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception, SpringApplicationRunListeners listeners) { try { try { handleExitCode(context, exception); if (listeners != null) { // 这里 SpringApplicationRunListeners listeners.failed(context, exception); } } finally { reportFailure(getExceptionReporters(context), exception); if (context != null) { context.close(); } } } catch (Exception ex) { logger.warn("Unable to close ApplicationContext", ex); } ReflectionUtils.rethrowRuntimeException(exception); } }