Spring实战(第6版)
上QQ阅读APP看书,第一时间看更新

在本书的课程中,我们将会创建一个名为Taco Cloud的在线应用,它能够订购人类所发明的一种美食——墨西哥煎玉米卷(taco)[1]。当然,在这个过程中,为了达成目标,我们将会用到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项目,我们首先要点击File菜单,选择New,接下来选择Spring Starter Project。图1.2展现了要查找的菜单结构。

1-2

图1.2 在Spring Tool Suite中使用Initializr初始化一个新项目

在选择Spring Starter Project之后,将会出现一个新的向导对话框(见图1.3)。向导的第一页会询问一些项目的通用信息,比如项目名称、描述和其他必要的信息。如果你熟悉Maven pom.xml文件的内容,那么就可以识别出大多数的输入域条目最终都会成为Maven的构建规范。对于Taco Cloud应用来说,我们可以按照图1.3的样子来填充对话框,然后选择Next。

向导的下一页会让我们选择要添加到项目中的依赖(见图1.4)。注意,在对话框的顶部,我们可以选择项目要基于哪个Spring Boot版本。它的默认值是最新的可用版本。一般情况下,最好使用默认值,除非你需要使用不同的版本。

至于依赖项本身,你可以打开各个区域并手动查找所需的依赖项,也可以在Available顶部的搜索框中对依赖进行搜索。对于Taco Cloud应用来说,我们最初的依赖项如图1.4所示。

1-3

图1.3 为Taco Cloud应用指定通用的项目信息

1-4

图1.4 选择Starter依赖

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

1-5

图1.5 指定备用的Initializr地址

默认情况下,新项目的向导会调用Spring Initializr来生成项目。通常情况下,没有必要覆盖默认值,这也是我们可以在向导的第二页直接选择Finish的原因。但是,如果你基于某种原因托管了自己的Initializr克隆版本(可能是本地机器上的副本或者公司防火墙内部运行的自定义克隆版本),那么你可能需要在选择Finish之前修改Base Url输入域的值,使其指向自己的Initializr实例。

选择Finish之后,项目会从Initializr下载并加载到工作空间中。此时,要等待它加载和构建,然后你就可以开始开发应用的功能了。但首先,我们看一下Initializr都为我们提供了什么。

项目加载到IDE中之后,我们将其展开,看一下其中都包含什么内容。图1.6展现了Spring Tool Suite中已展开的Taco Cloud项目。

1-6

图1.6 Spring Tool Suite中所展现的初始Spring项目结构

你可能已经看出来了,这就是一个典型的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。

程序清单1.1 初始的Maven构建规范

<?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所示。

程序清单1.2 Taco Cloud的引导类

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展现了这个测试类的概况。

程序清单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应用程序添加一些自定义的代码了。