百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

大佬用4000字带你彻底理解SpringBoot的运行原理!

haoteby 2025-09-11 01:08 10 浏览

Spring Boot的运行原理

从前面创建的Spring Boot应用示例中可以看到,启动一个Spring Boot工程都是从SpringApplication.run()方法开始的。这个方法具体完成了哪些工作?@Spring-BootApplication注解的作用是什么?在本节内容中将找到答案。

SpringApplication启动类

通过查看SpringApplication.run()方法的源码可以看到,该方法首先生成Spring-Application的一个实例,然后调用实例的run()方法。下面来看一下SpringApplication构造函数的源码:

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.lazyInitialization = false;

//②

this.resourceLoader = resourceLoader;

Assert.notNull(primarySources, "PrimarySources must

not be null");

this.primarySources = new LinkedHashSet(Arrays.asList

(primarySources));

this.webApplicationType =

WebApplicationType.deduceFrom

Classpath();

//③

this.setInitializers(this.getSpringFactoriesInstances(Applica

tion

ContextInitializer.class));

//④

this.setListeners(this.getSpringFactoriesInstances

(ApplicationListener.class));

//⑤

this.mainApplicationClass =

this.deduceMainApplicationClass();

}

注释①:打印启动信息。

注释②:表示Bean是否要以懒加载的方式进行实例化。

注释③:初始化WebApplicationType的类型,主要包括REACTIVE、SERVLET和NONE三种类型。

注释④:加载
ApplicationContextInitializer类。

注释⑤:加载ApplicationListener类。

注释④和⑤是加载META-INF/spring.factories文件中配置的
ApplicationContext-Initializer和ApplicationListener类。具体配置代码如下:

org.springframework.context.ApplicationContextInitializer=\

org.springframework.boot.context.ConfigurationWarningsApplica

tion

ContextInitializer,\

org.springframework.boot.context.ContextIdApplicationContextI

nitializer,\

org.springframework.boot.context.config.DelegatingApplication

ContextInitializer,\

org.springframework.boot.rsocket.context.RSocketPortInfoAppli

cation

ContextInitializer,\

org.springframework.boot.web.context.ServerPortInfoApplicatio

n

ContextInitializer

org.springframework.context.ApplicationListener=\

org.springframework.boot.ClearCachesApplicationListener,\

org.springframework.boot.builder.ParentContextCloserApplicati

onListener,\

org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPos

t

Processor,\

org.springframework.boot.context.FileEncodingApplicationListe

ner,\

org.springframework.boot.context.config.AnsiOutputApplication

Listener,\

org.springframework.boot.context.config.ConfigFileApplication

Listener,\

org.springframework.boot.context.config.DelegatingApplication

Listener,\

org.springframework.boot.context.logging.ClasspathLoggingAppl

ication

Listener,\

org.springframework.boot.context.logging.LoggingApplicationLi

stener,\

org.springframework.boot.liquibase.LiquibaseServiceLocatorApp

lication

Listener

通过上面的构造函数可以看到,SpringApplication类的主要的工作是确定Web应用类型、加载上下文初始化器及监听器等。

接下来看重要的部分,即SpringApplication实例的run()方法,具体源代码如下:

public ConfigurableApplicationContext run(String... args) {

StopWatch stopWatch = new StopWatch();

stopWatch.start();

ConfigurableApplicationContext context = null;

Collection<SpringBootExceptionReporter>

exceptionReporters =

new ArrayList();

this.configureHeadlessProperty();

SpringApplicationRunListeners listeners =

this.getRunListeners

(args);

//①

listeners.starting();

Collection exceptionReporters;

try {

ApplicationArguments applicationArguments = new

Default

ApplicationArguments(args);

ConfigurableEnvironment environment =

this.prepareEnvironment

(listeners, applicationArguments);

//②

this.configureIgnoreBeanInfo(environment);

Banner printedBanner =

this.printBanner(environment);

context = this.createApplicationContext();

//③

exceptionReporters =

this.getSpringFactoriesInstances

(SpringBootExceptionReporter.class, new Class[]

{ConfigurableApplication

Context.class}, context);

this.prepareContext(context, environment,

listeners,

applicationArguments, printedBanner);

//④

this.refreshContext(context);

//⑤

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

}

}

注释①:初始化监听器,并开启监听器进行事件监听。

注释②:准备上下文环境,包括运行机器的环境变量、应用程序启动的变量和配置文件的变量等。初始化上下文环境后,启动监听器
listeners.environment-Prepared(environment)。

注释③:初始化应用上下文。根据WebApplicationType类型的不同,生成的上下文也不同。如果是SERVLET,则对应生成
AnnotationConfigServletWebServer-ApplicationContext;如果是REACTIVE,则对应生成AnnotationConfigReactive
WebServerApplicationContext。默认生成
AnnotationConfigApplicationContext。

注释④:刷新应用上下文的准备工作。此处主要用于设置容器环境,启动监听器listeners.contextPrepared(context),加载启动类,并调用listeners.contextLoaded (context)方法。

注释⑤:刷新应用上下文,主要用于进行自动化装配和初始化IoC容器。

注释⑥:容器启动事件。

注释⑦:如果有用户定义的CommandLineRunner或者

ApplicationRunner,则遍历执行它们。

注释⑧:容器运行事件。

通过分析源代码,总结出Spring Boot启动的主要流程如图3.4所示。


通过分析源代码还可以发现,事件监听是Spring框架中重要的一部分。Spring提供了多种类型的事件,常用的如表3.1所示。

@SpringBootApplication注解

在Spring Boot的入口类中,有一个重要的注解@SpringBootApplication,本节将分析该注解的作用。首先查看@SpringBootApplication的源代码,具体如下:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan(excludeFilters = { @Filter(type =

FilterType.CUSTOM,

classes = TypeExcludeFilter.class),

@Filter(type = FilterType.CUSTOM, classes =

AutoConfiguration

ExcludeFilter.class) })

public @interface SpringBootApplication {

@AliasFor(annotation = EnableAutoConfiguration.class)

Class<?>[] exclude() default {};

@AliasFor(annotation = EnableAutoConfiguration.class)

Class<?>[] exclude() default {};

@AliasFor(annotation = ComponentScan.class, attribute

= "base

Packages")

String[] scanBasePackages() default {};

@AliasFor(annotation = ComponentScan.class, attribute

= "base

PackageClasses")

Class<?>[] scanBasePackageClasses() default {};

@AliasFor(annotation = Configuration.class)

boolean proxyBeanMethods() default true;

}

通过源码可以看到,@SpringBootApplication是一个复合注解,包括@Component-Scan、@EnableAutoConfiguration和@SpringBootConfiguration等。下面具体分析这3个注解。

1. @ComponentScan注解

在第1章中讲过Bean的注解,如@Service、@Repository、@Component和@Controller等。@ComponentScan注解可以自动扫描被以上注解描述的Bean并将其加载到IoC容器中。@ComponentScan注解还有许多属性,通过这些属性可以更准确地指定哪些Bean被扫描并注入。

basePackages:指定需要扫描的包路径。

basePackageClasses:指定类,并扫描该类所在包下的所有组件。

includeFilters:指定哪些类可以通过扫描。

excludeFilters:指定哪些类不被扫描。

lazyInit:指定扫描的对象是否要懒加载。

resourcePattern:指定符合条件的类文件。

2. @EnableAutoConfiguration注解

@EnableAutoConfiguration注解是Spring Boot实现自动化配置加载的核心注解。通过@Import注入一个名为
AutoConfigurationImportSelector的类,Spring-FactoriesLoader类加载类路径下的META-INF/spring.factories文件来实现自动配置加载的过程。其中,spring.factories文件配置了
org.springframework.boot.autoconfigure.EnableAutoConfiguration属性值,可以加载配置了@Configuration注解的类到IoC容器中。

3. @SpringBootConfiguration注解

@SpringBootConfiguration注解的功能类似于@Configuration注解,声明当前类是一个配置类,它会将当前类中有@Bean注解的实例加载到IoC容器中。

相关推荐

如何随时清理浏览器缓存_清理浏览器缓存怎么弄

想随时清理浏览器缓存吗?Cookieformac版是Macos上一款浏览器缓存清理工具,所有的浏览器Cookie,本地存储数据,HTML5数据库,FlashCookie,Silverlight,...

Luminati代理动态IP教程指南配置代理VMLogin中文版反指纹浏览器

介绍如何使用在VMLogin中文版设置Luminati代理。首先下载VMLogin中文版反指纹浏览器(https://cn.vmlogin.com)对于刚接触Luminati动态ip的朋友,是不是不懂...

mac清除工具分享,解除您在安全方面的后顾之忧

想要永久的安全的处理掉重要数据,删除是之一,使用今天小编分享的mac清除工具,为您的操作再增一层“保护”,小伙伴慎用哟,一旦使用就不可以恢复咯,来吧一起看看吧~mac清除工具分享,解除您在安全方面的后...

取代cookie的网站追踪技术:”帆布指纹识别”

【前言】一般情况下,网站或者广告联盟都会非常想要一种技术方式可以在网络上精确定位到每一个个体,这样可以通过收集这些个体的数据,通过分析后更加精准的去推送广告(精准化营销)或其他有针对性的一些活动。Co...

辅助上网为啥会被抛弃 曲奇(Cookie)虽甜但有毒

近期有个小新闻,大概很多小伙伴都没有注意到,那就是谷歌Chrome浏览器要弃用Cookie了!说到Cookie功能,很多小伙伴大概觉得不怎么熟悉,有可能还不如前一段时间被弃用的Flash“出名”,但它...

浏览器指纹是什么?浏览器指纹包括哪些信息

本文关键词:浏览器指纹、指纹浏览器、浏览器指纹信息、指纹浏览器原理什么是浏览器指纹?浏览器指纹是指浏览器的各种信息,当我们访问其他网站时,即使是在匿名的模式下,这些信息也可以帮助网站识别我们的身份。...

那些通用清除软件不曾注意的秘密_清理不常用的应用软件

系统清理就像卫生检查前的大扫除,即使你使出吃奶的劲儿把一切可能的地方都打扫过,还会留下边边角角的遗漏。随着大家电脑安全意识的提高,越来越多的朋友开始关注自己的电脑安全,也知道安装360系列软件来"武装...

「网络安全宣传周」这些安全上网小知识你要知道!

小布说:互联网改变了人们的衣食住行,但与之伴生的网络安全威胁也不容忽视。近些年来,风靡全球的勒索病毒、时有发生的电信诈骗、防不胜防的个人信息泄露时时刻刻都威胁着我们的生活。9月18日-24日是第四届...

TypeScript 终极初学者指南_typescript 进阶

在过去的几年里TypeScript变得越来越流行,现在许多工作都要求开发人员了解TypeScript...

jQuery知识一览_jquery的认识和使用

一、概览jQuery官网:https://jquery.com/jQuery是一个高效、轻量并且功能丰富的js库。核心在于查询query。...

我的第一个Electron应用_electronmy

hello,好久不见,最近笔者花了几天时间入门Electron,然后做了一个非常简单的应用,本文就来给各位分享一下过程,Electron大佬请随意~笔者开源了一个Web思维导图,虽然借助showSav...

HTML5 之拖放(Drag 和 Drop)_html拖放api

简介拖放是一种常见的特性,即抓取对象以后拖到另一个位置。在HTML5中,拖放是标准的一部分,任何元素都能够拖放。先点击一个小例子:在用户开始拖动<p>元素时执行JavaScrip...

如何用JavaScript判断输入值是数字还是字母?

在日常开发中,我们有时候需要判断用户输入的是数字还是字母。本文将介绍如何用JavaScript实现这一功能。检查输入值是否是数字或字母...

图形编辑器开发:快捷键的管理_图形编辑工具

大家好,我是前端西瓜哥。...

浏览器原生剪贴板:原来它能这样读取用户截图!

当我们使用GitHub时,会发现Ctrl+V就能直接读取用户剪贴板图片进行粘贴,那么它是如何工作的?安全性如何?...