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);}}} 

相关内容

热门资讯

银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
AsusVivobook无法开... 首先,我们可以尝试重置BIOS(Basic Input/Output System)来解决这个问题。...
ASM贪吃蛇游戏-解决错误的问... 要解决ASM贪吃蛇游戏中的错误问题,你可以按照以下步骤进行:首先,确定错误的具体表现和问题所在。在贪...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...