写点什么

从 SpringBoot 启动,阅读源码设计

作者:知了一笑
  • 2022 年 10 月 08 日
    浙江
  • 本文字数:3511 字

    阅读完需:约 12 分钟

从SpringBoot启动,阅读源码设计

服务启动堪称 Spring 源码设计的答案;

一、背景说明

初学 SpringBoot 框架时,第一次启动服务,直呼什么鬼?只需要简单的几步配置,几个核心的注解,就可以快速实现工程的搭建和运行;


虽然从 Spring 框架迁移到 SpringBoot 框架,在初期会有很多的不适应,但是更好用的框架会快速得到认可,从而成为主流的技术选型;


对于大多数的框架或者组件来说,如果使用起来越是简便,那么其内部的封装策略就越是复杂;


比如在 Spring 框架更新到 SpringBoot 版本时,其用法的简便与内部封装的复杂性已经形成强烈的对比;再到 SpringCloud 微服务框架时,其封装逻辑复杂到离谱;


对于服务端的开发来说,绕不开对 Spring 框架的深度学习,如果单纯站在源码阅读的角度,建议先熟读 SpringBoot 启动流程,然后再适当扩展其他源码块;

二、SpringBoot 工程

首先聊一聊阅读源码的基本思路,从一个极简的案例开始,围绕案例中的核心 API 作为切入点,通过对源码逻辑的断点调试,从而体会其设计的原理;


阅读 SpringBoot 的源码,可以从服务启动方法作为切入点,然后不断的分析启动过程涉及到的核心 API 和设计原理,再基于具体的启动日志去分析抽象的加载逻辑;



在看具体的源码之前,还需要说下分析思路,Spring 项目中,要注意每个 API 所属工程与层级,然后再去分析 API 之间关系,核心的构造、属性、方法等;


在 SpringBoot 的启动类中,有两个核心的切入点,一个是类的构造方法,完成一列的初始化动作;一个是启动方法,实现应用上下文的创建和装载;


构造方法



启动方法



需要说明的是,由于 SpringBoot 服务启动过程涉及源码过多,所以上面的源码中只是罗列部分的核心切入点,然后围绕这些关键流程展开,分析一些常见的源码设计;


另外说明一点,以下源码的核心版本:JDK-1.8spring-5.2.4spring-boot-2.2.5,在不同的版本下源码会存在差异;

三、应用上下文

服务启动时,根据应用类型判断创建的上下文,此处启动的是基于 servlet 的 web 应用,所以也依赖相应的 web 服务器,默认为 Tomcat;


启动方法的核心在于对应用上下文的创建、准备、刷新,应用上下文是一个十分抽象的描述,可以理解为应用运行的整体环境,其中涉及到资源加载,配置文件装配,运行服务的管理等,后续的源码分析都围绕该 API 展开;



ApplicationContext:应用上下文核心接口,在该接口中所有的方法都是只读模式,即只能通过 Get 方法进行访问;


ConfigurableApplicationContext:上下文配置扩展接口,提供了应用上下文的配置能力,生命周期的维护,以及在关闭之后的相关资源释放;


AbstractApplicationContext:上下文接口抽象实现,核心的 API,对应用上下文中的公共能力做了实现;


ConfigurableWebApplicationContext:Web 应用上下文配置扩展接口,提供了 Web 应用的上下文配置能力;


WebServerApplicationContext:Web 服务上下文,创建并管理 Web 应用的服务器,在该流程中嵌入的是 Tomcat 服务;


根据应用上下文几个核心的 API 设计,体会 Spring 源码的设计思路,从顶级的接口开始,不断向下扩展并且新增方法,理解抽象实现类的逻辑,以及服务运行时所依赖的具体 API;

四、资源加载

什么是资源,可以是各种类型的文件和配置,字节输入流的转换,也可以是 URL 资源定位,Spring 框架在运行的过程中,需要依赖 Resource 接口实现对底层资源的访问;



Resource:资源描述的顶级接口,提供了一系列的方法,继承 InputStreamSource 接口,支持将资源转换为流的形式操作;


AbstractResource:资源访问的抽象实现类,这里的设计原理与 AbstractApplicationContext 类似,提供资源访问方法的基础实现;


ResourceLoader:资源加载的封装接口,应用下文需要依赖该接口实现资源的获取与访问;


针对不同应用场景需求,Resource 接口的实现类有如下几个:FileSystemResource 文件系统资源,ClassPathResource 类路径下资源,InputStreamResource 输入流资源等;

五、应用环境

对于 Property 和 Environment 源码设计体系,参考上述的源码模块,在思路上是相似的,此处不多描述;



应用程序的属性和环境涉及到的参数描述非常多,比较直接的手段是通过 System 类中的方法输出,至于信息如何加载,在 StandardEnvironment 类中提供了方法,可以断点查看;

六、Bean 对象

基于 Spring 框架的应用程序中,由 Spring 容器负责创建,装配,设置属性,进而管理整个生命周期的对象,称为 Bean 对象;Bean 的生命周期非常复杂,过程大致如下:实例化,属性加载,初始化前后管理,销毁;



BeanFactory:工厂类,Spring 框架的核心能力,Bean 容器的顶级接口,提供了一系列 Bean 对象的访问方法,是 IOC 思想和依赖注入的基础支撑;


ConfigurableBeanFactory:Bean 容器可配置化接口,该扩展接口只是为了允许框架内部的即插即用和访问 bean 工厂的配置方法;


AbstractBeanFactory:Bean 管理的抽象实现类,可以查看其内部 doGetBean 方法,提供 Bean 实例对象的获取逻辑,如果无法获取则执行创建逻辑;

七、Tomcat 服务

初次启动 SpringBoot 工程时,最大的疑问就是可见 Tomcat 启动日志,但是没有显式的做服务器装配,直接启动 JAR 包即可,这在流程上简化了一大步;



WebServer:Web 应用服务器接口,比如常用的 Tomcat,Jetty,Netty 等,根据应用类型选择,只提供了启动、停止、获取端口三个方法,通过 WebServerApplicationContext 与应用上下文相关联;


TomcatWebServer:SpringBoot 框架管理内置 Tomcat 服务的核心类,对 Tomcat 生命周期的管理提供了一层包装;


Tomcat:Apache 组件中轻量级 Tomcat 启动器,提供了 Tomcat 基础配置,比如默认的 Port 和 HostName,以及生命周期管理的方法,TomcatWebServer 类中调用的就是该 API 中的具体方法;

八、事件模型

事件驱动模型是复杂流程中的常用解耦手段,即通过事件发送和监听两个拆解动作,实现流程的分步执行,这在 SpringBoot 启动流程和上下文装载中更是发挥的淋漓尽致;



ApplicationEvent:应用事件基础抽象类,继承自 JDK 中 EventObject 类,具体事件会继承该类,内部声明了事件源和发生时间两个核心属性;


ApplicationEventMulticaster:应用事件广播的顶级接口,可以将指定的应用事件广播给适合的监听器;


SimpleApplicationEventMulticaster:应用事件广播接口的简单实现,可以断点该类的 multicastEvent 方法,查看广播时应用事件和其相应的监听器;


ApplicationListener:应用事件监听器接口,继承自 JDK 中 EventListener 接口,Spring 中扩展了多种具体的事件监听器,以实现各种不同的场景需求,比如最常见的 ConfigFileApplicationListener 配置文件监听器;

九、配置加载

SpringBoot 工程中,配置文件的管理策略非常复杂,有内部程序执行加载配置,也有外部集成的组件配置,当然最核心的就是工程的自定义配置;



ConfigFileApplicationListener.Loader:配置文件监听器的内部类,实现对工程中的配置源加载,其核心逻辑在 Loader.load 方法中实现,具体逻辑由相关的实现类完成;


PropertySourceLoader:配置加载的策略接口,在 Spring 工程中支持多种类型的文件配置,比如 yml、yaml、properties、xml,需要通过文件的扩展名选择相应的加载实现类;


YamlPropertySourceLoader:加载.yml或者.yaml类型的文件,SpringBoot 工程中常用的配置文件类型,最终转换成 Name 和 Value 的属性源集合,即通过 PropertySource 抽象类来描述;

十、数据库集成

Spring 框架的强大之处还在于能够和其他组件进行简单快速的集成,比如常用的数据库、缓存、消息队列等各种类型的组件,分析内部的集成逻辑,会发现很多原理上的相似性,尤其在 SpringBoot 框架中,约定大于配置;



DataSourceAutoConfiguration:SpringBoot 工程中数据库的自动化配置类,在配置中 Hikari 是默认选择的连接池,也是号称速度最快的;


DataSourceProperties:数据源配置相关的基础类,在 DataSourceConfiguration 配置类中,会基于参数去创建数据源对象;


HikariDataSource:Hikari 连接池组件中的数据源 API,描述数据源的具体信息,例如配置、连接池、状态等,具体的数据库连接逻辑是在该组件内部完成的;


基于 SpringBoot 集成数据库的原理,可以扩展性的看看:Redis 组件的 RedisAutoConfiguration 配置类;Kafka 组件的 KafkaAutoConfiguration 配置类,Elasticsearch 组件的 RestClientAutoConfiguration 配置类,在设计原理上都有异曲同工之妙;


写在最后


从个人经验来看,想要阅读 Spring 框架的源码设计,需要基于应用流程先构建一个大的轮廓结构,理解设计中的常用策略和原理,然后再深入单个模块的细节逻辑,这样容易找到阅读节奏;


本文并没有涉及源码中过多的细节逻辑,只是从服务启动作为切入点,整理与开发关联性较为直接的源码模块,描述个人对于 Spring 源码阅读的基础思路。

十一、参考源码

应用仓库:https://gitee.com/cicadasmile/butte-flyer-parent
组件封装:https://gitee.com/cicadasmile/butte-frame-parent
复制代码


发布于: 刚刚阅读数: 3
用户头像

知了一笑

关注

公众号:知了一笑 2020.04.08 加入

源码仓库:https://gitee.com/cicadasmile

评论

发布
暂无评论
从SpringBoot启动,阅读源码设计_Java_知了一笑_InfoQ写作社区