springboot启动流程分析
创始人
2024-02-08 00:16:23
0次

1.从应用程序启动入口开始追踪代码:

SpringApplication.run(SpringbootDemoApplication.class, args);

2.进入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 = WebApplicationType.deduceFromClasspath();this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}

这个步骤做了以下几件事:

a.初始化资源加载器,但是从上下文可以看出是null

b.设置主资源类,即第一步传过来的SpringbootDemoApplication类

c.推断webApplicationType的类型:WebApplicationType.SERVLET

d.设置bootstrapRegistryInitializers数据,通过SpringFactoriesLoader从META-INF/spring.factories目录加载key为BootstrapRegistryInitializer的数据并初始化

e.设置initializers,通过SpringFactoriesLoader从上下文中所以jar根目录META-INF/spring.factories目录加载key为ApplicationContextInitializer的数据并初始化

f.设置listeners,通过SpringFactoriesLoader从上下文中所以jar根目录META-INF/spring.factories目录加载key为ApplicationListener的数据并初始化

j.推断主应用类,即SpringbootDemoApplication类

3.接着继续进入SpringApplication类的run方法中,如下代码:

public ConfigurableApplicationContext run(String... args) {long startTime = System.nanoTime();// a.创建一个springboot上下文对象DefaultBootstrapContext bootstrapContext = createBootstrapContext();ConfigurableApplicationContext context = null;configureHeadlessProperty();// b.获取并启动监听的listenersSpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting(bootstrapContext, this.mainApplicationClass);try {// c.初始化应用环境ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);configureIgnoreBeanInfo(environment);// 打印启动banner数据Banner printedBanner = printBanner(environment);// d.初始化应用上下文context = createApplicationContext();context.setApplicationStartup(this.applicationStartup);// e.刷新应用上下文前的准备工作prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// f.刷新应用上下文refreshContext(context);// j.刷新应用上下文之后的扩展功能afterRefresh(context, applicationArguments);Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);}listeners.started(context, timeTakenToStartup);// h.调用ApplicationRunner和CommandLineRunner的run方法callRunners(context, applicationArguments);}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;}

接下来具体解释一下,每一步的细节:

a.创建springboot上下文对象:创建一个类型为DefaultBootstrapContext的上下文对象,并且将第二步获取过来的bootstrapRegistryInitializers执行初始化操作,执行initialize方法

private DefaultBootstrapContext createBootstrapContext() {DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));return bootstrapContext;}
BootstrapRegistryInitializer接口类:
@FunctionalInterface
public interface BootstrapRegistryInitializer {/*** Initialize the given {@link BootstrapRegistry} with any required registrations.* @param registry the registry to initialize*/void initialize(BootstrapRegistry registry);}

可以自行实现该接口并做某些初始化作用,这个初始化的时机比较早。

b.获取并启动监听的listeners:获取并实例化所有类型为SpringApplicationRunListener的listener

private SpringApplicationRunListeners getRunListeners(String[] args) {Class[] types = new Class[] { SpringApplication.class, String[].class };return new SpringApplicationRunListeners(logger,getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),this.applicationStartup);}

启动listener

void starting(ConfigurableBootstrapContext bootstrapContext, Class mainApplicationClass) {doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),(step) -> {if (mainApplicationClass != null) {step.tag("mainApplicationClass", mainApplicationClass.getName());}});}

进入org.springframework.boot.context.event.EventPublishingRunListener#starting方法中(说明:EventPublishingRunListener这个runlistener是通过反射初始化的,通过getRunListeners反射调用的是EventPublishingRunListener(SpringApplication application, String[] args) 这个构造方法,有兴趣可以看看,里面初始化了多播器:initialMulticaster和当前应用对象:application):发布了一个ApplicationStartingEvent事件对象

@Overridepublic void starting(ConfigurableBootstrapContext bootstrapContext) {this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));}

继续追踪到org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)方法:

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));Executor executor = getTaskExecutor();for (ApplicationListener listener : getApplicationListeners(event, type)) {if (executor != null) {executor.execute(() -> invokeListener(listener, event));}else {invokeListener(listener, event);}}}

继续追到org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener:

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {listener.onApplicationEvent(event);}catch (ClassCastException ex) {// 省略部分代码...}}

最终执行各个listener的onApplicationEvent方法。

springboot中所有的事件发布都是由org.springframework.boot.context.event.EventPublishingRunListener这个类来发布完成的,它会在容器初始化的各个阶段发布各种不同的事件,关注了这个事件的监听器就能接受到,然后执行对应的方法。

c.初始化应用环境,代码如下:

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {context.setEnvironment(environment);postProcessApplicationContext(context);// 执行第二步获取的initializers方法applyInitializers(context);// 发布一个ApplicationContextInitializedEvent类型的事件,告诉listener准备监听listeners.contextPrepared(context);bootstrapContext.close(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beansConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {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());}context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));// Load the sourcesSet sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");load(context, sources.toArray(new Object[0]));listeners.contextLoaded(context);} 

d.初始化应用上下文:

context = createApplicationContext();context.setApplicationStartup(this.applicationStartup);

e.刷新应用上下文前的准备工作(跟spring无缝对接起来)org.springframework.context.support.AbstractApplicationContext#refresh:

public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);beanPostProcess.end();// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}catch (BeansException ex) {// 省略代码...}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();contextRefresh.end();}}}

h.调用ApplicationRunner和CommandLineRunner的run方法,它的执行时机是容器启动完成的时候,实现对应的接口即可:

private void callRunners(ApplicationContext context, ApplicationArguments args) {List runners = new ArrayList<>();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);}}} 

相关内容

热门资讯

前端-session、jwt 目录:   (1)session (2&#x...
linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
nodejs:本地安装nvm实... 一、背景-使用不同版本node的原因 vue3+ts、nuxt3版本,node...
JAVA集合知识整理 Java集合知识整理 HashMap相关 HashMap的底层数据结构:jdk1.8之...
无刷直流电机介绍及单片机控制实... 无刷直流电机介绍及单片机控制实例前言基本概念优势与劣势使用寿命基本结构使用单片机控制实例电子调速器&...
fwdiary(2) dp2 1.传纸条  AcWing 275. 传纸条 - AcWing 走两条路,走一条最大的...
常用的DOS命令 常用的DOS命令 DOS(Disk Operating System,磁...
<C++> 类和对象(下) 1.const成员函数将const修饰的“成员函数”称之为const成员函数,cons...