本篇文章将结合源代码介绍spring boot应用的启动过程。
1 引言
Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".
上面这段话是Spring Boot工程官网上的第一句话。大概意思是Spring Boot应用很好,可以创建独立的、生产级别的、基于Spring框架的、可直接运行的应用。
- 独立:独立意味着低耦合,意味着我们不需要为应用间相互依赖付出额外的管理精力;
- 生产级别:这个中文中见得少,我们不太使用production-grade或者production-level这种表述。但既然敢宣称生产级别,那就意味着基于Spring Boot开发的应用从一开始就应该高度成熟。即使是才开始编写的应用代码,也应该能快速发布至生产环境,产生效益;
- 可直接运行:B/S架构作为如今依旧流行的主流企业级应用架构,绝大部分应用依然需要基于Web容器发布运行。可直接运行意味着我们再也不用去纠结怎样让我们的应用代码正确地嵌入到Web容器中,也不用去关心我们的应用如何才能在Web容器中正常运行了;
但是,天下没有免费的午餐。技术发展了这么多年,搭建一个应用该有的组件、套路就那么多。要想保证应用正确地、听话地运行,该有的都要有,一个都少不了。
那Spring Boot到底做了什么,它具有怎样的魔法,才能达到它所说的可以创建独立的、生产级别的、基于Spring框架的、可直接运行的应用的?
从技术的视角上来看,简单框架不意味着简化流程、去除组件。简单框架的作用大体也就只能将通用的功能和组件隐藏在背后,由框架自身来实现。这也就是一直所提倡的让业务开发人员关注业务,而不用去关注技术和技术实现。
但我们做技术的人,是绝难以承认自己是个仅会写业务代码的低级码农。技术人有技术人自己的执着,这执着大体就是所谓的“刨根问底”、“钻牛角尖”了。
那么,下面我将尝试把Spring Boot剖开,从源代码的级别深入分析Spring Boot的启动过程,去解释Spring Boot内部的魔法。
2 说明
我大体会竭尽所能将我写的内容介绍清楚。但毕竟每个人的技术水平不同、专业背景不同,且我个人水平也难以保证可以将所有内容描述透彻,故本人无法保证所写的每句话大家都能看得懂。如果有不太清楚或谬误的地方,欢迎大家留言告知,我会仔细分析确认并耐心解答。
为保证过程尽可能身心愉悦且安全,请大家自备纸笔以及速效救心丸(^.^)……
本文中的内容大部分来自Spring官方文档、Spring工程源代码、个人编写的Demo代码以及自己的思考和整理。
我尽量提供引用内容的出处,但不保证所有出处均会标注出来。若有未标明出处且影响理解的,还请留言中说明,我会补充解释的。
3 如何开始……
我想Spring Boot官网文档会是一个好的开始。Spring Boot官方文档中的Get Started给我们介绍了如何搭建一个Spring Boot的应用,相当之简单。
我们就从这开始我们的阅读源代码之旅了!另外,鉴于Spring Boot所能集成的框架非常之繁多,我们的源代码解析过程以spring-boot-starter-web
为主,也就是基于Spring Boot搭建标准的web应用。
我想,下面这可能是整个系列中最不需要动脑子的技术章节了(?♂️)。
3.1 创建工程
Spring Boot工程创建可以通过Spring社区提供的Spring Initializr创建,但我们的目的是要深入到源代码层面,所以能手动搭建尽可能手动搭建,便于理解和进一步挖掘。
那我们首先使用IDE创建一个简单的maven工程,并完成相关基础内容的调整,可以参看公众号前面发的相关文章从零搭建Web应用(二)初步了解下如何创建maven工程。
最初始的pom.xml文件的内容大致如下,我们给应用起的名字是spring-boot(这个名字可以随便取,不需要非是spring-boot)。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>spring-bootartifactId>
<version>1.0-SNAPSHOTversion>
project>
3.2 parent
在pom.xml文件中增加parent节点,parent节点的内容如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>spring-bootartifactId>
<version>1.0-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.3.RELEASEversion>
parent>
project>
在Spring官方文档中是这样解释spring-boot-starter-parent
的:
Our applications for smoke tests use the spring-boot-starter-parent in the parent section of the POM. The spring-boot-starter-parent is a special starter that provides useful Maven defaults. It also provides a dependency-management section so that you can omit version tags for “blessed” dependencies.
大体意思是说,spring-boot-starter-parent
提供了maven的默认配置,并且使用dependency-management帮助应用省去了配置依赖包版本号的麻烦。
那究竟是如何做到的呢?
首先dependencyManagement
中定义的包,如果同时出现在dependencies
中,并且dependencies
没有提供版本号,则使用dependencyManagement
版本号作为最终的版本号。比如下面这个例子:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<dependencies>
<dependency>
<groupId>org.apache.activemqgroupId>
<artifactId>activemq-amqpartifactId>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.activemqgroupId>
<artifactId>activemq-amqpartifactId>
<version>5.15.13version>
dependency>
dependencies>
dependencyManagement>
project>
activemq-amqp这个依赖在dependencies
中没有指定版本号,但dependencyManagement
指定其版本号是5.15.13,因此最后使用的activemq-amqp的版本号就是5.15.13。
在spring-boot-starter-parent
以及它的parent中,定义了大量的dependencyManagement
,这样在具体的工程中若用到相关的包就不需要再指定版本号了。
3.3 引入依赖
在pom.xml中引入依赖包,和我们以前的引入方式唯一的区别在于没有定义版本号,原因在前面章节刚介绍过。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
project>
3.4 编写代码
根据Spring Boot介绍编写一个Application的类,其中包含启动的入口main方法:
package org.example.spring.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@EnableAutoConfiguration
public class Application {
@RequestMapping("/")
String home() {
return "Hello World!";
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3.5 启动应用
启动main方法,通过浏览器访问地址http://localhost:8080,正确返回"Hello World":
4 小结
本章作为系列的开篇,介绍了下整体背景,并创建一个简单的Spring Boot应用引入后续的内容。