发布时间:2026-02-28
浏览次数:0
虽瞧着 Boot 借那仅一行的 run 方法便开启了,然而实则于其内部的背后,已然执行了一整套繁杂无比的启动流程。
当年我第一次接触 Boot的时候,第一个疑问就是:
跑哪儿去了?
为什么能够在不经过配置的情况下就将java web程序启动起来呢?那时真心觉得特别神奇。比如说像我这样的老程序员,当那个时间段去编写java web程序的时候,可真是麻烦透顶了:。
处在我所在同一时代时候的程序员,那时应当都存有这样一个想法,那便是要是属于内置的该多好呀。没曾想到在若干年之后,Boot实施了这样一件事情。
我们可以先从【哪儿去了】这个问题入手,慢慢铺开来讲。
问题 : Boot 为什么不需要外置的?
Boot要把它嵌入进去随后启动,这靠它自身是不行的,非得依靠其本身这般intellij idea activiti,也就是说得它自己先去达成一件事情:
支持使用方可以通过new的方式,将自己启动起来。
也即是说,容器原本只以独立进程的形式存在,如今却转变成了可编程对象,能够在代码当中直接开启。实际上在版本为7的时候,其内部额外增添了一个被称作的事物。
org....的类,就是用来支持内嵌启动的:
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);
tomcat.start();
tomcat.getServer().await();
就是这个类被Boot所使用,在代码里是直接用来启动的。当这样的能力被提供之后,Boot只需要再去做一件事情就行,什么事情呢,就是把它当成jar包引入进来就可以了。
org.springframework.boot
spring-boot-starter-web
这个 里直接包含了:
org.springframework.boot
spring-boot-starter-tomcat
--boot--会将那个实实在在的 jar 包,也就是 embed-core.jar 引入进来的。
但是,因为存在诸如Jetty之类的容器,所以在这方面,势必要进行统一的封装intellij idea activiti,而这便是ory接口所具备的作用。
ory接口借助相关手段,助力集成拥有Web容器的方式,使其具备了标准化特征,而Boot在其中起到了关键作用。
那么,到达这里,我们能够开启讲解代码细节的进程了吗,不可以,鉴于还欠缺一个上帝一般的视角,要是你即刻就迈入代码细节,极易致使迷失方向的,没错。
要理解 Boot的启动流程,是得有一些前置的基础知识的。
理解 Boot启动流程的前置知识点
我所讲解的2.7版本的Boot的启动流程,其本质实际上是一场经过精密编排的自动化构建进程,要切实去领会它,你理应先去明白接下来的这四个核心概念。
第一个核心机制:Java SPI 与.
起始于一套增强版的Java SPI机制,Boot的零配置形成了,它所要解决的问题存在着。
Boot 怎么知道要加载哪些类?配置都藏在哪?
JDK原生的SPI是去查找META-INF/ , 而Boot 2.7的核心借助r类 , 专门对所有jar包下的META-INF/.文件进行扫描。 这是一个庞大的清单文件。 在-boot-包里 , 这个文件罗列了相当多的自动配置类 ( 比如on)。
)。
那知道这个对理解启动流程有什么作用呢?
有的,鉴于启动时所要着手处理的头一件事情便是开展信息搜集工作。Boot 会对这些文件予以读取,将全部潜在的扩展点,包括监听器、初始化器、自动配置类,统统加载至内存里以供备用。
第二个核心机制:按需装配和约定优于配置
于运用SPI将有关的配置类加载进来之后,尚需执行一项按需装配的逻辑,这是旨在解决如下这般的一个问题 :。
所加载的配置类数量众多,将它们全部启动,会导致出现卡死以及冲突方面的问题。
确实是得进行一下过滤的,我们借助十分平常的**@ 和@Bean**来予以说明。
是类存在的情况下,我才会生效,类不存在时,我就装作没看见,这就是@的意思 ,比如。
@ConditionalOnClass(RedisTemplate.class)
@Configuration
public class RedisAutoConfiguration {
...
}
也就是说,唯有在你的项目依赖当中存在这个类的情况下,我这份 Redis 自动配置才将会进行加载。
Boot属于通用框架,不清楚你究竟是否要用Redis,所以唯有条件达成了,才会去进行加载,这便是按需装配句号。
而**@Bean**呀,即是讲呢,若是你没进行配置,那么我会帮你去完成配置,要是你已经配置好了,那就听从你的安排。举例来说:
@ConditionalOnMissingBean(DataSource.class)
@Bean
public DataSource dataSource() {
...
}
这个意思是,要是容器之中还未曾有,那我便会帮你去创建一个默认的,然而要是你自行进行了设置:
@Bean
public DataSource myDataSource() {
...
}
则不创建了。这就是约定优于配置。
第三个核心机制:观察者模式与生命周期
这个是为了解决如下的问题:
启动流程这么长,如何解耦各个阶段?又如何干预?
你可千万别觉得Boot的启动流程是如同流水账那般的,它实际上更近似于:
每抵达一个时期,便发布一桩事情。无论是啥人在意这桩事情,那个人就要出来开展工作。
这就是设计模式中的观察者模式。
而其中最关键的就是【阶段性广播员】
它会担负起在启动过程里每个阶段进行广播、发布事件的职责,此时,所有经过注册的监听器都会收到。留意,这里所有监听器都会收到阶段性事件,然而只有监听此事件类型的监听器才会执行。
那启动过程中到底有哪些关键的阶段呢? 如下:
我在上面所写的这一段内容,务必要理解呀,原因在于源码之中充斥着括号。要是你没能领会这个机制,你就会感觉代码显得很是跳跃,明明是在此处展开执行的,怎么就陡然跑到那个类里面去了呢?唯有对事件具备了理解,你才能够将繁杂的长流程划分成各个阶段,随后依照阶段展开理解,如此一来就会相对清晰许多了。
最后终于可以启动的容器了
在Boot那错综复杂的启动流程当中,如果要问最终的目的究竟是什么,那便是为了启动一个特定的东西。在此处,务须要理解得透彻明白才行。Boot其底层实际上也是基于某种特定情况的,由此你能够以如下这种较为简单的方式去理解:
Boot本质上只是帮你准备好一切,最终真正干活的,还是 。
终于就是打算借助调用之中的办法,把容器全然激活。好,到了此处,可以运用最为简洁的言语来阐述 Boot 的启动流程了。
从源代码的角度来分析启动流程了
留意,鉴于篇幅方面的问题,我这儿仅会将入口的部分罗列出来,你依照入口一步步去看就行。只要你运用我上面所介绍的知识,我坚信你再度去剖析.run()当中底层的实现,就不会那般晕头转向了。
在此之前,我先用一张整体的架构图,帮你建立整体的架构认知。
核心就两步:
构造阶段之时,要去推断应用的类型,还要将那扩展组件予以加载;运行阶段当中,需得准备好环境,进而创建容器,接着刷新容器,最终启动达成完成。
.run()对应的代码入口在这里:
public ConfigurableApplicationContext run(String... args) {
// 1. 触发启动事件
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 2. 准备环境(加载 application.properties/yml)
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 3. 创建 ApplicationContext(根据应用类型创建不同的上下文)
context = createApplicationContext();
// 4. 准备上下文(注册启动类、执行初始化器)
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 5. 刷新上下文(核心!启动 Tomcat、实例化 Bean)
refreshContext(context);
// 6. 触发启动完成事件
listeners.started(context, timeTakenToStartup);
// 7. 执行 CommandLineRunner/ApplicationRunner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
}
return context;
}
当中的那个括号括着的部分,便是确切开始运行的所在之处,要是你对那里面所包含的代码存有兴趣,那就能够去瞧一瞧!
里面的方法实现就可以了。
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer(); // 创建 Web 容器
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
要是你是头一回瞧这段代码,就会存有一个疑问,当中的ory是源自何处呢? 这便是我们前边提及的自动装配以及按需装配的理念,由。
得到的。并结合和Bean以及ion注解,以及
类来完成的。
顺着.run()的入口,结合上面所讲的前置知识,大致看个几周时间,基本上便能记住些许细节,还能了解到一些设计方法以及设计思想了。
写在最后
Boot 的启动流程看起来复杂,实际上核心思路很清晰:
推导应用种类,加载自动配置,组建依据种类构建各异的上下文,刷新容器于特定钩子中开启,激发启动完毕的事件,执行用户自定的任务。
Boot可将传统SSH项目,原本通过war包与外置容器的部署形式,简化为于jar包直接开展程序运行,其关键核心在于这一套由自动配置以及内嵌容器所构成的架构设计。
2014年,Boot刚发布之际,好多人认为这不就是将其打成jar包嘛。然而,当真正领会了启动的原理之后,你就会发觉,这背后存在着一整套构思精巧的设计:
常常去阅读源代码,对于编码水平的提高,是有益处的,对于技术设计能力水平的提升,也是有益处的。
如有侵权请联系删除!
Copyright © 2023 江苏优软数字科技有限公司 All Rights Reserved.正版sublime text、Codejock、IntelliJ IDEA、sketch、Mestrenova、DNAstar服务提供商
13262879759
微信二维码