初始化Spring应用
在本书的课程中,我们将会创建一个名为 Taco Cloud 的在线应用,它能够订购人类所发明的一种美食——墨西哥煎玉米卷(taco)。当然,在这个过程中,为了达成目标,我们将会用到 Spring、Spring Boot 及各种相关的库和框架。
有多种初始化 Spring 应用的可选方案。尽管我可以教你手动创建项目目录结构和定义构建规范的各个步骤,但这无疑是浪费时间,我们最好将时间花在编写应用代码上。因此,我们将会学习如何使用 Spring Initializr 初始化应用。
Spring Initializr 是一个基于浏览器的 Web 应用,同时也是一个 REST API,它能够生成一个 Spring 项目结构的骨架,我们还可以使用各种想要的功能来填充它。使用 Spring Initializr 的几种方式如下所示:
-
通过地址为 https://start.spring.io/ 的 Web 应用;
-
在命令行中使用 curl 命令;
-
在命令行中使用 Spring Boot 命令行接口;
-
在 Spring Tool Suite 中创建新项目;
-
在 IntelliJ IDEA 中创建新项目;
-
在 NetBeans 中创建新项目;
-
在 Apache NetBeans 中创建新项目。
我将这些细节放到了附录中,这样就不用在这里花费很多页的篇幅介绍每种可选方案了。在本章和本书的其他章节中,我都会向你展示如何使用我最钟爱的方式创建新项目,也就是在 Spring Tool Suite 中使用 Spring Initializr。
顾名思义,Spring Tool Suite 是一个非常棒的 Spring 开发环境,能够以 Eclipse、Visual Studio Code 或 Theia IDE 扩展的方式来使用。我们也可以在 Spring 网站的 Spring Tools 页面下载直接可运行的 Spring Tool Suite 二进制文件。Spring Tool Suite 提供了便利的 Spring Boot Dashboard 特性,让我们能够在 IDE 中很容易地启动、重启和停止 Spring Boot 应用。
如果你不是 Spring Tool Suite 用户,那也没有关系,我们依然可以做朋友。你可以跳转到附录中,查看最适合你的 Initializr 方案,以此来替换后面小节中的内容。但是,在本书中,我偶尔会提到 Spring Tool Suite 特有的特性,比如 Spring Boot Dashboard。你如果不使用 Spring Tool Suite,那么需要调整这些指令以适配你的 IDE。
使用Spring Tool Suite初始化Spring项目
要在 Spring Tool Suite 中初始化一个新的 Spring 项目,我们首先要点击 File菜单,选择 New,接下来选择 Spring Starter Project。图1.2展现了要查找的菜单结构。

在选择 Spring Starter Project 之后,将会出现一个新的向导对话框(见图1.3)。向导的第一页会询问一些项目的通用信息,比如项目名称、描述和其他必要的信息。如果你熟悉 Maven pom.xml 文件的内容,那么就可以识别出大多数的输入域条目最终都会成为Maven的构建规范。对于 Taco Cloud 应用来说,我们可以按照图1.3的样子来填充对话框,然后选择 Next。
向导的下一页会让我们选择要添加到项目中的依赖(见图1.4)。注意,在对话框的顶部,我们可以选择项目要基于哪个 Spring Boot 版本。它的默认值是最新的可用版本。一般情况下,最好使用默认值,除非你需要使用不同的版本。
至于依赖项本身,你可以打开各个区域并手动查找所需的依赖项,也可以在 Available 顶部的搜索框中对依赖进行搜索。对于 Taco Cloud 应用来说,我们最初的依赖项如图1.4所示。


现在,你可以选择 Finish 来生成项目并将其添加到工作空间中。但是,如果你还想多体验一些功能,可以再次选择 Next,看一下新 Starter 项目向导的最后一页,如图1.5所示。

默认情况下,新项目的向导会调用 Spring Initializr 来生成项目。通常情况下,没有必要覆盖默认值,这也是我们可以在向导的第二页直接选择 Finish 的原因。但是,如果你基于某种原因托管了自己的 Initializr 克隆版本(可能是本地机器上的副本或者公司防火墙内部运行的自定义克隆版本),那么你可能需要在选择 Finish 之前修改 Base Url 输入域的值,使其指向自己的 Initializr 实例。
选择 Finish 之后,项目会从 Initializr 下载并加载到工作空间中。此时,要等待它加载和构建,然后你就可以开始开发应用的功能了。但首先,我们看一下 Initializr 都为我们提供了什么。
检查Spring项目的结构
项目加载到 IDE 中之后,我们将其展开,看一下其中都包含什么内容。图1.6展现了 Spring Tool Suite 中已展开的 Taco Cloud 项目。

你可能已经看出来了,这就是一个典型的 Maven 或 Gradle 项目结构,其中应用的源码放到了 “src/main/java” 中,测试代码放到了 “src/test/java” 中,而非 Java 的资源放到了 “src/main/resources” 中。在这个项目结构中,我们需要注意以下几点。
-
mvnw 和 mvnw.cmd:这是 Maven 包装器(wrapper)脚本。借助这些脚本,即便你的机器上没有安装 Maven,也可以构建项目。
-
pom.xml:这是 Maven 构建规范,随后我们将会深入介绍该文件。
-
TacoCloudApplication.java:这是 Spring Boot 主类,它会启动该项目。随后,我们会详细介绍这个类。
-
application.properties:这个文件起初是空的,但是它为我们提供了指定配置属性的地方。在本章中,我们会稍微修改一下这个文件,但是我会将配置属性的详细阐述放到第 6 章。
-
static:在这个文件夹下,你可以存放任意为浏览器提供服务的静态内容(图片、样式表、JavaScript等),该文件夹初始为空。
-
templates:这个文件夹中存放用来渲染内容到浏览器的模板文件。这个文件夹初始是空的,不过我们很快就会往里面添加 Thymeleaf 模板。
-
TacoCloudApplicationTests.java:这是一个简单的测试类,它能确保 Spring 应用上下文成功加载。在开发应用的过程中,我们会将更多的测试添加进来。
随着 Taco Cloud 应用功能的增长,我们会不断使用 Java 代码、图片、样式表、测试以及其他附属内容来充实这个项目结构。不过,在此之前,我们先看一下 Spring Initializr 提供的几个条目。
探索构建规范
在填充 Initializr 表单的时候,我们声明项目要使用 Maven 来进行构建。因此,Spring Initializr 所生成的 pom.xml 文件已经包含了我们所选择的依赖。程序清单1.1展示了 Initializr 为我们提供的完整 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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version> ⇽---Spring Boot的版本
<relativePath />
</parent>
<groupId>sia</groupId>
<artifactId>taco-cloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>taco-cloud</name>
<description>Taco Cloud Example</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency> ⇽---Starter依赖
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin> ⇽---Spring Boot插件
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
</project>
首先要注意的是 <parent> 元素,更具体来说是它的 <version> 子元素。这表明我们的项目要以 spring-boot-starter-parent 作为其父 POM。除了其他的一些功能之外,这个父 POM 为 Spring 项目常用的一些库提供了依赖管理。对于这些父 POM 中所涵盖到的库,我们不需要指定它们的版本,因为它会通过父 POM 继承下来。这里的 2.5.3 表明要使用 Spring Boot 2.5.3,所以就会根据这个版本的 Spring Boot 定义来继承依赖管理。除了指定其他的依赖之外,2.5.3 版本的 Spring Boot 依赖管理会指定底层的核心 Spring 框架的版本为 5.3.9。
既然我们谈到了依赖的话题,那么需要注意在 <dependencies> 元素下声明了 4 个依赖。你可能会对前三个感到更熟悉一些。它们直接对应我们在 Spring Tool Suite 新项目向导中,点击 Finish 之前所选择的 Spring Web、Thymeleaf 和 Spring Boot DevTools 依赖。第四个依赖提供了很多有用的测试功能。我们没有必要在专门的复选框中选择它,因为 Spring Initializr 会假定我们要编写测试(希望你能正确地开展这项工作)。
你可能也注意到了,除了 DevTools 依赖之外,其他的这些依赖的 artifactId 上都有 starter 这个单词。Spring Boot starter 依赖的特别之处在于它们本身并不包含库代码,而是传递性地拉取其他的库。这种 starter 依赖主要有以下几个好处。
-
构建文件会显著减小并且更易于管理,因为这样不必为所需的每个依赖库都声明依赖。
-
我们能够根据它们所提供的功能来思考依赖,而不是根据库的名称。如果要开发 Web 应用,只需添加 web starter 依赖,而不必添加一堆单独的库。
-
我们不必再担心库版本的问题。你可以直接相信给定版本的 Spring Boot,传递性引入的库的版本都是兼容的。现在,你只需要关心使用的是哪个版本的 Spring Boot 就可以了。
最后,构建规范还包含一个 Spring Boot 插件。这个插件提供了一些重要的功能,如下所示:
-
它提供了一个 Maven goal,允许我们使用 Maven 来运行应用;
-
它会确保依赖的所有库都会包含在可执行 JAR 文件中,并且能够保证它们在运行时类路径下是可用的;
-
它会在 JAR 中生成一个 manifest 文件,将引导类(在我们的场景中,也就是 TacoCloudApplication)声明为可执行 JAR 的主类。
谈到了引导类,我们打开它看一下。
引导应用
因为我们将会通过可执行 JAR 文件的形式来运行应用,所以很重要的一点就是要有一个主类,它将会在 JAR 运行的时候被执行。我们同时还需要一个最小化的 Spring 配置,用来引导该应用。这就是 TacoCloudApplication 类所做的事情,如程序清单1.2所示。
package tacos;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication ⇽---Spring Boot应用
public class TacoCloudApplication {
public static void main(String[] args) {
SpringApplication.run(TacoCloudApplication.class, args); ⇽---运行应用
}
}
TacoCloudApplication 尽管只有很少的代码,但是包含了很多的内容。其中,最强大的一行代码看上去很短:@SpringBootApplication 注解明确表明这是一个 Spring Boot 应用。但是,@SpringBootApplication 远比看上去更强大。
@SpringBootApplication 是一个组合注解,组合了 3 个其他的注解。
-
@SpringBootConfiguration:将该类声明为配置类。尽管这个类目前还没有太多的配置,但是后续我们可以按需添加基于 Java 的 Spring 框架配置。这个注解实际上是 @Configuration 注解的特殊形式。
-
@EnableAutoConfiguration:启用 Spring Boot 的自动配置。我们随后会介绍自动配置的更多功能。就现在来说,我们只需要知道这个注解会告诉 Spring Boot 自动配置它认为我们会用到的组件。
-
@ComponentScan:启用组件扫描。这样我们能够通过像 @Component、@Controller、@Service 这样的注解声明其他类,Spring 会自动发现它们并将它们注册为 Spring 应用上下文中的组件。
TacoCloudApplication 另外一个很重要的地方是它的 main() 方法。这是 JAR 文件执行的时候要运行的方法。在大多数情况下,这个方法都是样板代码,我们编写的每个 Spring Boot 应用都会有一个类似或完全相同的方法(类名不同则另当别论)。
这个 main() 方法会调用 SpringApplication 中静态的 run() 方法,后者会真正执行应用的引导过程,也就是创建 Spring 的应用上下文。传递给 run() 的两个参数中,一个是配置类,另一个是命令行参数。尽管传递给 run() 的配置类不一定要和引导类相同,但这是最便利和最典型的做法。
你可能并不需要修改引导类中的任何内容。对于简单的应用程序来说,你可能会发现在引导类中配置一两个组件是非常方便的,但是对于大多数应用,最好还是要为没有实现自动配置的功能创建单独的配置类。在本书的整个过程中,我们将会创建多个配置类,所以请继续关注后续的细节。
测试应用
测试是软件开发的重要组成部分。我们始终可以通过在命令行中构建应用、运行测试,从而实现项目的手动测试:
$ ./mvnw package
...
$ java -jar target/taco-cloud-0.0.1-SNAPSHOT.jar
或者,鉴于我们在使用 Spring Boot,Spring Boot 的 Maven 插件会使这个过程更加简单:
$ ./mvnw spring-boot:run
但是,手动测试就意味着有人类的参与,因此有可能会出现人为的错误或者不一致的测试。自动测试会更加一致和可重复。
在这一点上,Spring Initializr 为我们提供了一个测试类作为起步。程序清单1.3展现了这个测试类的概况。
package tacos;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest ⇽---Spring Boot测试
public class TacoCloudApplicationTests {
@Test ⇽---测试方法
public void contextLoads() {
}
}
TacoCloudApplicationTests 类中的内容并不多:这个类中只有一个空的测试方法。即便如此,这个测试类还是会执行必要的检查,确保 Spring 应用上下文成功加载。如果你所做的变更导致 Spring 应用上下文无法创建,这个测试将会失败,这样你就可以做出反应来解决相关的问题。
@SpringBootTest 会告诉 JUnit 在启动测试的时候要添加上 Spring Boot 的功能。像 @SpringBootApplication 一样,@SpringBootTest 也是一个组合注解,它本身使用了 ExtendWith(SpringExtension.class),从而能够将 Spring 的测试功能添加到 JUnit 5 中。就现在来讲,我们可以认为这个测试类与在 main() 方法中调用 SpringApplication.run() 是等价的。在这本书中,我们将会多次看到 @SpringBootTest,并不断见识它的威力。
最后,就是测试方法本身了。尽管 @SpringBootTest 会为测试加载 Spring 应用上下文,但是如果没有任何测试方法,那么它其实什么事情都没有做。即便没有任何断言或代码,这个空的测试方法也会提示该注解完成了它的工作并成功加载 Spring 应用上下文。这个过程中出现任何问题,测试都会失败。
要在命令行运行这个测试类及任意其他的测试类,我们都可以使用如下的 Maven 指令:
$ ./mvnw test
至此,我们已经看完了 Spring Initializr 提供的代码。我们看到了一些用来开发 Spring 应用程序的基础样板,但是还没有编写任何的代码。现在是时候启动IDE、准备好键盘,向 Taco Cloud 应用程序添加一些自定义的代码了。