SpringBoot是现在使用是如此的广泛,几乎所有的后端面试官都会就它的使用和基本原理方面进行考察。
现在就来盘一盘springboot的启动流程!
回答这个问题只需要查看SpringApplication.class就行了,下面截取了主要启动代码片段。
public ConfigurableApplicationContext run(String... args) { ... try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 1. 准备环境变量 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); // 2. 准备上下文、主要是创建上下文和扩展点调用 context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 3. 刷新上下文 refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } listeners.started(context); // 这里调用了CommandLineRunner.class和ApplicationRunner.class所以我们写的Runner才能自动执行呢 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } ... return context; } 复制代码
把各种渠道配置的变量都整合到Environment对象中,方便后面的使用。
springboot通过事件机制把属性配置进行了解耦,可以方便的扩展其他的属性配置方式。
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 首先创建Environment对象 ConfigurableEnvironment environment = getOrCreateEnvironment(); // 根据main中输入的参数配置变量 configureEnvironment(environment,applicationArguments.getSourceArgs()); // 发送ApplicationEnvironmentPreparedEvent事件 // 他会通知其他监听者完成属性的配置 // 比如通知BootstrapApplicationListener完成springcloud配置文件的环境配置 // 通知ConfigFileApplicationListener完成application.yml文件的读取配置 listeners.environmentPrepared(environment); // 绑定"spring.main"为当前的application bindToSpringApplication(environment); if (!this.isCustomEnvironment) { // 如果自定义Environment就转换成StandardEnvironment environment = new EnvironmentConverter(getClassLoader()) .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; } 复制代码
在容器初始化之前做一些工作,如ApplicationContextInitializer的调用,事件发送。
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); // 预处理,如果beanNameGenerator、resourceLoader不为空就将其添加到上下文中,并将ConversionService也添加到上下文中 postProcessApplicationContext(context); // 调用所有的ApplicationContextInitializer,在刷新上下文之前做一些可能的工作。 applyInitializers(context); // 发送ApplicationContextInitializedEvent事件 listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } ... // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); } 复制代码
刷新上下文完成ioc容器的初始化!
private void refreshContext(ConfigurableApplicationContext context) { // 调用AbstractApplicationContext.refresh()完成ioc容器的初始化 refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } 复制代码