浅谈springboot启动过程

news2024/9/17 7:29:36

1. 知识回顾

为了后文方便,我们先来回顾一下spring的一些核心概念。

spring最核心的功能无非是ioc容器,这个容器里管理着各种bean。ioc容器反映在java类上就是spring的核心类ApplicationContext。ApplicationContext有众多的子接口和子类,不同的实现类有不同的功能。比如ClassPathXmlApplicationContext支持从xml读取bean定义并注册到容器中,AnnotationConfigApplicationContext支持读取@Configuration、@Service等注解定义的bean。

ClassPathXmlApplicationContext和AnnotationConfigApplicationContext代表了我们定义bean的两种最常见方式,即xml和注解方式。还有一种方式是通过@Import来导入bean的定义。如下代码示例了@Import的一种用法。

@Configuration
public class AppConfig {
    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

@Import(AppConfig.class)
public class AppMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppMain.class);
        MyBean bean = context.getBean(MyBean.class);
        System.out.println(bean);
}

@Import还有一种用法就是@Import(XxxImportSelector.class),其中XxxImportSelector是ImportSelector这个接口的实现类。下面演示了@Import(XxxImportSelector.class)的用法。

// 一个普通的java类
public class TestImportSelector {
}

// ImportSelector的实现类。
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {"com.example.springbootsimple.service.TestImportSelector"};
    }
}

// 测试ImportSelector
@Import(MyImportSelector.class)
public class AppMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppMain.class);
        TestImportSelector bean = context.getBean(TestImportSelector.class);
        System.out.println(bean);
    }
}

如上代码运行后,spring容器中就会有TestImportSelector这个bean了,原理是如果@Import()中要import的是一个ImportSelector的实现类,spring就会自动调用ImportSelector.selectImports方法,这个方法会返回一个包含类名的String数组,spring会根据这些类名实例化类,注册到spring容器中。

2. 最简单的springboot启动

我们用springboot启动web应用用得比较多,用多了以后,往往会觉得springboot只能这么用,其实不然,我们先抛开各种配置,从最简单的springboot启动开始看,如下代码是最简单的springboot启动,注意AppMain类上没有任何注解。

public class AppMain {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(AppMain.class);
        // 打印 org.springframework.context.annotation.AnnotationConfigApplicationContext        
        System.out.println(context.getClass().getName());
    }
}

上面代码可以成功运行(注意成功运行的前提是只引入org.springframework.boot:spring-boot,不要引入spring-boot-starter-web)。我把SpringApplication.run的返回值也打印出来了,是想告诉你,springboot的启动过程其实就是创建一个ApplicationContext的过程。所以,别人问你springboot启动过程是什么时,你就可以说,就是创建了一个ApplicationContext,完事。如果我们从宏观角度来看,这个问题就是这么简单。当然,这个答案肯定不会让人满意,但是不要着急,我们要从简单到复杂,从宏观到微观,这样才能看清事物的本质。

3. 带Web应用的springboot启动

3.1 启动示例

我们引入spring-boot-starter-web,然后执行如下代码。

@SpringBootApplication
public class AppMain {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(AppMain.class);
        System.out.println(context.getClass().getName());
    }
}

此时,打印出的结果是org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext,可以看到和我们第一个例子打印的ApplicationContext不一样了。springboot是如何确定用哪个ApplicationContext的?AnnotationConfigServletWebServerApplicationContext做了什么事情,让我们什么也没做就启动了一个tomcat的web容器?

3.2 如何确定用哪个ApplicationContext?

我们追溯SpringApplication.run方法的源码,会看到如下run方法,这个方法就是比较核心的启动流程了。

我们不需要每一行代码都关心,因为那样会让我们迷失在源码里,我们通过方法名,我们很容易看出来 context = createApplicationContext();这一行代码就是在创建一个ApplicationContext,所以我们看下这个方法源码。

 createApplicationContext();会调用DefaultApplicationContextFactory.create(WebApplicationType webApplicationType)这个工厂类来创建ApplicationContext。从上面代码可以看出,DefaultApplicationContextFactory相当于是一个代理,它会通过SpringFactoriesLoader.loadFactories加载其他ApplicationContextFactory来创建Application,如果其他工厂类没的返回结果,就会用默认的AnnotationConfigApplicationContext。SpringFactoriesLoader.loadFactories这个方法有兴趣的可以研究下,这个方法会到spring.factories这个文件里加载你想要的类,spring-boot jar包的spring.factories里定义了如下两个ApplicationContextFactory类,所以这两个类会被加载进来用于创建ApplicationContext。

上面两个Factory从名字也能很容易辨别,一个是用来创建ServletWeb应用的,一个是用来创建ReactiveWeb应用的。如下图是AnnotationConfigServletWebServerApplicationContext.Factory的create方法,可以看到如果webApplicationType是SERVLET类型,就会创建AnnotationConfigServletWebServerApplicationContext。

从上述源码来看,我们第一个疑问已经解决了,springboot会根据wepApplicationType的类型来决定创建什么样的ApplicationContext。至于WebApplicationType如何确定,看下WebApplicationType#deduceFromClasspath就可以了,比较简单,比如会根据类路径下是否有javax.servlet.Servlet等来判断web类型是否是SERVLET等等。当我们引入spring-boot-starter-web时,这个引用就会包含对servlet的引用,所以我们创建的就是SERVLET类型的WebApplicationType。

 3.3 AnnotationConfigServletWebServerApplicationContext做了什么?

现在ApplicationContext创建好了,我们也知道什么情况下会创建什么类型的ApplicationContext了,但是这个ApplicationContext还是个比较原始的,很多事情都还没做。了解过一些spring源码的应该知道,ConfigurableApplicationContext有个非常重要的方法:refresh方法。这个方法内部会解析bean的定义,创建好单例bean,应用bean后处理器等。AnnotationConfigServletWebServerApplicationContext就是在refresh方法中搞了点事情,启动了tomcat容器。org.springframework.context.support.AbstractApplicationContext#refresh方法内会调用onRefresh()方法,AnnotationConfigServletWebServerApplicationContext就是重写了onRefresh方法,在这个方法里启动了tomcat。

至于tomcat是如何创建并启动的,这又是另一个话题了,感兴趣的可以自行研究。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/482000.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SAP重复制造入门到放弃系列之基本配置

目录 前言 主要配置清单: REM参数文件: 计划订单计划参数文件 维护输入项参数 维护行选择 确认和物流信息系统全局设置 定义确认处理 操作方法表 其他 前言 重复制造中的配置步骤包括创建重复制造配置文件、为运行计划数量(计划订单&a…

Excel技能之打印,19+技巧超省纸

颜色太多,重新打印。 没有边框,重新打印。 少了几列,重新打印。 整个工作表打印出来,拿剪刀把自己需要的数据剪下来,用胶水贴到另一张新的A4纸。 你上班打印资料,浪费了多少纸,认真算一下&…

PyCharm with Remote Interpreters

文章目录 一: 利用 Docker 镜像搭建1.编写 Dockerfile2.构建并配置 Remote Interpreters3.结论0.FAQ 二: 利用 SSH Docker 镜像搭建1.编写 Dockerfile2.构建并运行3.构建镜像4.运行容器5.验证并配置0.FAQ 起因是需要在 M2 芯片的 Mac 上调试基于 Python3.6.1 构建的程序. 而 M…

每天一道算法练习题--Day18 第一章 --算法专题 --- ----------前缀树

前缀树 字典树也叫前缀树、Trie。它本身就是一个树型结构,也就是一颗多叉树,学过树的朋友应该非常容易理解,它的核心操作是插入,查找。删除很少使用,因此这个讲义不包含删除操作。 截止目前(2020-02-04&a…

bootloader编写——MCU固件升级系列2(STM32)

本系列将从升级流程、boot代码编写、APP代码编写以及固件打包来介绍,硬件选用STM32F407ZGT6(手里只有),来完成这系列教程。 前言 开发STM32固件升级并编写Bootloader时,需要注意以下几个关键点: 熟悉硬件和…

数据结构篇四:栈

文章目录 前言1.栈1.1 栈的概念及结构1.2 栈的实现 2.栈功能的解析及实现2.1 栈的创建2.2 初始化2.3 入栈2.4 出栈2.5 检查栈是否为空2.6 获取栈顶元素2.7 栈中的有效元素个数2.8 销毁 3.代码实现3.1 Stack.h3.2 Stack.c3.3 test.c 4.总结 前言 前面学习的一些结构都比较普通&a…

13 | visual studio与Qt的结合

1 前提 Qt 5.15.2 visual studio 2019 vsaddin 2.8 2 具体操作 2.1 visual studio tool 2.1.1 下载 https://visualstudio.microsoft.com/zh-hans/downloads/2.1.2 安装 开发

推荐算法实战项目:WideDeep原理以及案例实战(附完整 Python 代码)

本文要介绍的是Google于2016年提出的Wide&Deep模型,此模型的提出对业界产生了非常大的影响,不仅其本身成功地应用在多家一线互联网公司,而且其后续的改进工作也一直延续至今。 Wide&Deep模型正如其名,分别包含了Wide部分…

golang - 函数的使用

核心化编程 为什么需要函数? 代码冗余问题不利于代码维护函数可以解决这个问题 函数 函数:为完成某一功能的程序指令(语句)的集合,称为函数 在 Go 中,函数分为:自定义函数(自己写…

Apache Solr Velocity模板注入RCE

Apache Solr Velocity模板注入RCE 一、Apache Solr介绍 Solr是一个独立的企业级搜索应用服务器,它对外提供类似于web-service的API接口,用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引,也可以通过http get操作提出查找请求,并得到XML格式的返回结果。…

【openFrameworks】跨平台的 C++ 开源框架 | oF 文件结构 | 图形基础介绍

💭 写在前面:本章我们将介绍一个非常好用的跨平台的 C 开源框架 —— openFrameworks。它是一个开源的跨平台的 C工具包,方便开发者创建出一个更简单和直观的框架,擅长开发图像和动画。 📜 本章目录: 0x0…

SpringBoot集成SpringSecurity从0到1搭建权限管理详细过程(认证+授权)

前言 最近工作需要给一个老系统搭建一套权限管理,选用的安全框架是SpringSecurity,基本上是结合业务从0到1搭建了一套权限管理,然后想着可以将一些核心逻辑抽取出来写一个权限通用Demo,特此记录下。 文章目录 前言1、SpringSecuri…

CSS中4个定位设计与实现

1.相对定位 说明&#xff1a;相对原来元素的定位。开启定位后&#xff0c;元素层级高&#xff0c;会置于最上层 作用&#xff1a;用于元素的微调&#xff0c;不会脱离文档流 1.1代码实现 <!DOCTYPE html> <html lang"zh"> <head><meta charset…

外卖项目优化-01-redis缓存短信验证码、菜品数据、Spring Cache(注解开发缓存)、(注解开发)缓存套餐数据

文章目录 外卖项目优化-01课程内容前言1. 环境搭建1.1 版本控制解决branch和tag命名冲突 1.2 环境准备 2. 缓存短信验证码2.1 思路分析2.2 代码改造2.3 功能测试 3. 缓存菜品信息3.1 实现思路3.2 代码改造3.2.1 查询菜品缓存3.2.2 清理菜品缓存 3.3 功能测试3.4 提交并推送代码…

每日一题133——环形链表

给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&#xff08;…

5. 操作系统基础

5. 操作系统基础 常考面试题 说说你对进程的理解⭐⭐⭐ 程序是指令、数据及其组织形式的描述,而进程则是程序的运行实例,包括程序计数器、寄存器和变量的当前值。 Linux的进程结构,一般分为三部分:代码段、数据段(.data与.bss)和堆栈段。 代码段用于存放程序代码,如果有…

【计算机图形学基础教程】面向对象程序设计基础

构造函数与析构函数 例1 设计一个长方形CRectangle类&#xff0c;调用类的成员函数计算长方形的周长和面积。 #include <iostream>class CRectangle { public:CRectangle(); // 声明默认构造函数CRectangle(int width, int height); // 声明带…

Python基础合集 练习21 (错误与异常处理语句)

‘’‘try: block1 except[ExceptionName]: block2 ‘’’ block1:执行代码,表示可能会出现错误的代码块 ExceptionName: 表示要捕获的异常名称,为可选参数.如果不指定异常名称,则表示捕获所有异常 block2:表示发生异常时执行的代码块 while True: try: num int(input(请输…

测试时,可快速调用 Mapper 的 Mapper Generator

项目 Gitee 地址&#xff1a;MapperGenerator (当前使用的是 JDK17&#xff0c;JDK8 的需改下 pom.xml 文件&#xff09; 解决的问题&#xff1a;SpringBootTest 启动太慢 使用方式 假设有这样一个数据库&#xff0c;名为 a SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;…

推荐 3 个令你惊艳的 GitHub 项目

昨日 GitHub Trending 上榜的开源项目&#xff0c;基于 AI 技术提高你的生产力。借助 AI 你能搭建自己的数字人、搭建自己的法律助手、文档分析助手。 本期推荐开源项目目录&#xff1a; 1. 数字人开源项目 2. AI 法律助手 3. 为 PDF 文档打招一个聊天机器人 01 数字人开源项目…