📖 本文目录
📖 Spring In Action
📑 第 1 章 Spring 入门
🔖 1.1 什么是 Spring ?
Spring 的核心
Spring 的核心是一个 容器,通常称为 Spring 应用程序上下文,用于创建和管理应用程序组件。这些组件(或 bean)在 Spring 应用程序上下文中连接在一起以构成一个完整的应用程序,就像将砖、灰浆、木材、钉子、管道和电线绑在一起以组成房屋。
依赖注入
将 bean 连接在一起的行为是基于一种称为 依赖注入(DI)的模式。依赖项注入的应用程序不是由组件自身创建和维护它们依赖的其他 bean 的生命周期,而是依赖于单独的实体(容器)来创建和维护所有组件,并将这些组件注入需要它们的 bean。通常通过构造函数参数或属性访问器方法完成此操作。
举个小栗子🌰
例如,假设在应用程序的许多组件中,要处理两个组件:inventory service(用于获取库存级别)和 product service(用于提供基本产品信息)。product service 取决于 inventory service,以便能够提供有关产品的完整信息。图 1.1 说明了这些 bean 与 Spring 应用程序上下文之间的关系。
从历史上看,引导 Spring 应用程序上下文将 bean 连接在一起的方式是使用一个或多个 XML 文件,这些文件描述了组件及其与其他组件的关系。例如,以下 XML 声明两个 bean,一个 InventoryService
bean 和一个 ProductService
bean,然后通过构造函数参数将 InventoryService
bean 注入到 ProductService
中:
<bean id="inventoryService" class="com.example.InventoryService" />
<bean id="productService" class="com.example.ProductService" >
<constructor-arg ref="inventoryService" />
</bean>
但是,在最新版本的 Spring 中,基于 Java 的配置更为常见。以下基于 Java 的配置类等效于 XML 配置:
@Configuration
public class ServiceConfiguration {
@Bean
public InventoryService inventoryService() {
return new InventoryService();
}
@Bean
public ProductService productService() {
return new ProductService(inventoryService());
}
}
@Configuration
注释向 Spring 表明这是一个配置类,它将为 Spring 应用程序上下文提供 beans。
配置的类方法带有 @Bean
注释,指示它们返回的对象应作为 beans 添加到应用程序上下文中(默认情况下,它们各自的 bean IDs 将与定义它们的方法的名称相同)。
与基于 XML 的配置相比,基于 Java 的配置具有多个优点,包括更高的类型安全性和改进的可重构性。即使这样,仅当 Spring 无法自动配置组件时,才需要使用 Java 或 XML 进行显式配置。
自动配置起源于 Spring 技术,即 自动装配 和 组件扫描。借助组件扫描,Spring 可以自动从应用程序的类路径中发现组件,并将其创建为 Spring 应用程序上下文中的 bean。通过自动装配,Spring 会自动将组件与它们依赖的其他 bean 一起注入。
最近,随着 Spring Boot 的推出,自动配置的优势已经远远超出了组件扫描和自动装配。Spring Boot 是 Spring 框架的扩展,它提供了多项生产力增强功能。这些增强功能中最著名的就是 自动配置,在这种配置中,Spring Boot 可以根据类路径中的条目、环境变量和其他因素,合理地猜测需要配置哪些组件,并将它们连接在一起。
Spring Boot 自动配置大大减少了构建应用程序所需的显式配置(无论是 XML 还是 Java)的数量。实际上,当完成本章中的示例时,将拥有一个正在运行的 Spring 应用程序,该应用程序仅包含一行 Spring 配置代码!
🔖 1.2 初始化 Spring 应用程序
在本书的学习过程中,将创建 Taco Cloud,这是一个在线应用程序,用于订购由真人制作的最美味的食物 - 玉米饼。 当然,将使用 Spring、Spring Boot 以及各种相关的库和框架来实现此目标。
初始化 Spring 应用程序的有多个选择。尽管我可以指导你逐步完成手动创建项目目录结构和定义构建规范的步骤,但这却浪费了时间,最好花费更多时间编写应用程序代码。因此,将依靠 Spring Initializr 来引导应用程序的创建。
Spring Initializr 既是一个基于浏览器的 Web 应用程序,又是一个 REST API,它们可以生成一个基本的 Spring 项目结构,可以使用所需的任何功能充实自己。 使用 Spring Initializr 的几种方法如下:
- 从 Web 应用程序 http://start.spring.io 创建
- 使用
curl
命令从命令行创建 - 使用 Spring Boot 命令行接口从命令行创建
- 使用 Spring Tool Suite 创建一个新项目的时候
- 使用 IntelliJ IDEA 创建一个新项目的时候
- 使用 NetBeans 创建一个新项目的时候
⭐️ 1.2.1 使用 IntelliJ IDEA 初始化 Spring 项目
利用 IntelliJ IDEA 快速创建一个 Spring Initializr 项目
导入相关依赖
⭐️ 1.2.2 检查 Spring 项目结构
项目工程目录如下
你可能会认为这是典型的 Maven 或 Gradle 项目结构,其中应用程序源代码位于src/main/java 下,测试代码位于 src/test/java 下,非 Java 资源位于 src/main/resources 下 。在该项目结构中,需要注意以下事项:
mvnw
和mvnw.cmd
—— 这些是 Maven 包装器脚本。即使你的计算机上没有安装 Maven,也可以使用这些脚本构建项目。pom.xml
—— 这是 Maven 构建规范,一会儿我们将对此进行更深入的研究。TacoCloudApplication.java
—— 这是引导项目的 Spring Boot 主类。稍后,我们将在这节详细介绍。application.properties
—— 该文件最初为空,但提供了一个可以指定配置属性的地方。我们将在本章中对此文件进行一些修改,但在第 5 章中将详细介绍配置属性。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 http://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.6.9</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.alascanfu</groupId>
<artifactId>taco-cloud</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>taco-cloud</name>
<description>Taco Cloud Example</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<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>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
xml 文件中第一个值得注意的项是 <packaging>
元素。你选择将应用程序构建为可执行的 JAR 文件,而不是 WAR 文件。这可能是你所做的最奇怪的选择之一,特别是对于 web 应用程序。毕竟,传统的 Java web 应用程序被打包为 WAR 文件,而 JAR 文件是库和偶尔使用的桌面 UI 应用程序的首选打包方式。
接下来,请注意 <parent>
元素,更具体地说,注意它的 <version>
子元素。这指定您的项目将 spring-boot-starter-parent
作为它的父 POM。除此之外,这个父 POM 还为 Spring 项目中常用的几个库提供依赖项管理。对于父 POM 覆盖的那些库,不必指定版本,因为它是从父 POM 继承的。2.6.9
版本,表示你正在使用 Spring Boot 2.6.9,这样项目将使用继承自 Spring Boot 版本中定义的依赖项管理。
在讨论依赖项时,请注意在 <dependencies>
元素下声明了三个依赖项。前两个看起来应该比较熟悉。它们直接对应于在单击 Spring Tool Suite 新建项目向导中的 Finish 按钮之前选择的 Web 和 Thymeleaf 依赖项。第三个依赖项提供了许多有用的测试功能,你不必选中包含它的方框,因为 Spring Initializr 假定(希望是正确的)你将编写测试。
所有这三个依赖项的 artifact ID 中都有 starter 这个词。Spring Boot starter 依赖项的特殊之处在于,它们本身通常没有任何库代码,而是间接地引入其他库。这些 starter 依赖提供了三个主要的好处:
- 构建的文件将会小得多,也更容易管理,因为不需要对每一个可能需要的库都声明一个依赖项。
- 可以根据它们提供的功能来考虑需要的依赖关系,而不是根据库名来考虑。如果正在开发一个 web 应用程序,那么将添加 web starter 依赖项,而不是一个编写 web 应用程序的各个库的清单。
- 不用担心 library 版本问题。可以相信的是,对于给定版本的 Spring Boot,可间接地引入的库的版本将是兼容的,只需要考虑使用的是哪个版本的 Spring Boot。
最后,构建规范以 Spring Boot 插件结束。这个插件执行一些重要的功能:
- 提供了一个 Maven 编译目标,让你能够使用 Maven 运行应用程序。这将在第 1.3.4 节中尝试实现这个目标。
- 确保所有的依赖库都包含在可执行的 JAR 文件中,并且在运行时类路径中可用。
- 在 JAR 文件中生成一个 manifest 文件,表示引导类(在本书例子中是
TacoCloudApplication
)是可执行 JAR 的主类。
引导应用程序
因为将从一个可执行的 JAR 运行应用程序,所以在运行 JAR 文件时,有一个主类来执行是很重要的。还需要至少一个最小的 Spring 配置文件来引导应用程序。这就是将在 TacoCloudApplication
类中找到的内容,如下程序清单 1.2 所示。
package com.alascanfu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class TacoCloudApplication {
public static void main(String[] args) {
SpringApplication.run(TacoCloudApplication.class, args);
}
}
虽然 TacoCloudApplication
中只有很少的代码,但是其中包含了相当丰富的内容。最强大的代码行之一也是最短的代码行之一。
@SpringBootApplication
注释清楚地表明这是一个 Spring 引导应用程序。
@SpringBootApplication
中有更多的东西。@SpringBootApplication
是一个组合了其他三个注释的复合应用程序:
@SpringBootConfiguration
—— 指定这个类为配置类。尽管这个类中还没有太多配置,但是如果需要,可以将 Javabased Spring Framework 配置添加到这个类中。实际上,这个注释是@Configuration
注释的一种特殊形式。@EnableAutoConfiguration
—— 启用 Spring 自动配置。稍后我们将详细讨论自动配置。现在,要知道这个注释告诉 Spring Boot 自动配置它认为需要的任何组件。@ComponentScan
—— 启用组件扫描。这允许你声明其他带有@Component
、@Controller
、@Service
等注释的类,以便让 Spring 自动发现它们并将它们注册为 Spring 应用程序上下文中的组件。
TacoCloudApplication
的另一个重要部分是 main()
方法。这个方法将在执行 JAR 文件时运行。在大多数情况下,这种方法是样板代码;编写的每个 Spring 引导应用程序都有一个类似或相同的方法(尽管类名不同)。
main()
方法调用 SpringApplication 类上的静态 run()
方法,该方法执行应用程序的实际引导,创建Spring
应用程序上下文。传递给 run()
方法的两个参数是一个配置类和命令行参数。虽然传递给 run()
的配置类不必与引导类相同,但这是最方便、最典型的选择。
你可能不需要更改引导类中的任何内容。对于简单的应用程序,你可能会发现在引导类中配置一两个其他组件很方便,但是对于大多数应用程序,最好为任何没有自动配置的东西创建一个单独的配置类。你将在本书的整个过程中定义几个配置类,因此请注意这些细节。
测试应用程序
测试是软件开发的一个重要部分。认识到这一点后,Spring Initializr 提供了一个测试类。程序清单 1.3 显示了基准测试类。
package com.alascanfu;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class TacoCloudApplicationTests {
@Test
void contextLoads() {
}
}
在 TacoCloudApplicationTests
中没有太多东西:类中的一个测试方法是空的。尽管如此,这个测试类确实执行了必要的检查,以确保 Spring 应用程序上下文能够成功加载。如果做了任何阻止创建 Spring 应用程序上下文的更改,则此测试将失败,这样你就可以通过解决问题来应对。
🔖 1.3 编写 Spring 应用程序
因为才刚刚开始,所以我们将从对 Taco Cloud 应用程序的一个相对较小的更改开始,但是这个更改将展示 Spring 的很多优点。在刚刚开始的时候,添加到 Taco Cloud 应用程序的第一个功能是主页,这似乎是合适的。当你添加主页,你将创建两个代码构件:
- 一个处理主页请求的控制器类
- 一个视图模板,定义了主页的外观
⭐️ 1.3.1 处理 web 请求
Spring 附带了一个强大的 web 框架,称为 Spring MVC。Spring MVC 的核心是控制器的概念,这是一个处理请求并使用某种信息进行响应的类。对于面向浏览器的应用程序,控制器的响应方式是可选地填充模型数据并将请求传递给视图,以生成返回给浏览器的 HTML。
将编写一个简单的控制器类来处理根路径的请求(例如 /
),并将这些请求转发到主页视图,而不填充任何模型数据。程序清单 1.4 显示了简单的控制器类。
/***
* @author: Alascanfu
* @date : Created in 2022/6/30 22:53
* @description:
* @modified By: Alascanfu
**/
@Controller
public class HomeController {
@RequestMapping(path = "/")
public String home(){
return "home";
}
}
可以看到,这个类是用 @Controller
注释的。@Controller
本身并没有做多少事情。它的主要目的是将该类识别为组件扫描的组件。由于 HomeController
是用 @Controller
注释的,因此 Spring 的组件扫描会自动发现它,并在 Spring 应用程序上下文中创建一个 HomeController
实例作为 bean。
实际上,其他一些注释(包括 @Component
、@Service
和 @Repository
)的用途与 @Controller
类似。你可以用任何其他的注解来有效地注释 HomeController
,它仍然可以工作。但是,选择 @Controller
更能描述该组件在应用程序中的角色。
home()
方法与控制器方法一样简单。它使用 @GetMapping
进行注释,以指示如果接收到根路径 /
的 HTTP GET 请求,则此方法应该处理该请求。除了返回 home
的 String
值外,它什么也不做。
此值被解释为视图的逻辑名称。如何实现该视图取决于几个因素,但是因为 Thymeleaf 在类路径中,所以可以使用 Thymeleaf 定义该模板。
为什么是 Thymeleaf?
你可能想知道为什么选择 Thymeleaf 作为模板引擎。为什么不是 JSP?为什么不是 FreeMarker?为什么不是其他几个选项中的一个呢?
简单地说,我必须选择一些东西,我喜欢 Thymeleaf,相比其他选项更喜欢。尽管 JSP 看起来是一个不错的选择,但是在使用 JSP 进行 Spring 引导时仍然存在一些需要克服的挑战。我不想在第 1 章中掉进那个陷阱。不要紧,我们将在第 2 章中讨论其他模板选项,包括 JSP。
模板名称由逻辑视图名称派生而来,它的前缀是 /templates/,后缀是 .html。模板的结果路径是 /templates/home.html。因此,需要将模板放在项目的 /src/main/resources/templates/home.html 中。现在让我们创建该模板。
⭐️ 1.3.2 定义视图
为了保持你的主页简洁,它应该做的只是欢迎用户访问网站。程序清单 1.5 显示了定义 Taco Cloud 主页的基本 Thymeleaf 模板。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Taco Cloud</title>
</head>
<body>
<h1>Welcome to...</h1>
<img th:src="@{/images/TacoCloud.png}" />
</body>
</html>
关于这个模板没有太多要讨论的。唯一值得注意的代码行是显示 Taco Cloud 标志的 <img>
标记。它使用一个 Thymeleaf 的 th:src
属性和一个 @{…}
表达式引用具有上下文相对路径的图片。除去这些,它只是一个 Hello World 页面。
该图片是通过上下文相对路径 /images/TacoCloud.png 进行引用的。从我们对项目结构的回顾中可以想起,像图片这样的静态内容保存在 /src/main/resources/static 文件夹中。这意味着 Taco Cloud 标志图片也必须驻留在项目的 /src/main/resources/static/images/TacoCloud.png 中。
⭐️ 1.3.3 测试控制器
在对 HTML 页面的内容进行断言时,测试 web 应用程序可能比较棘手。幸运的是,Spring 提供了一些强大的测试支持,使测试 web 应用程序变得很容易。
就主页而言,你将编写一个与主页本身复杂度相当的测试。你的测试将对根路径 /
执行一个 HTTP GET 请求并期望得到一个成功的结果,其中视图名称为 home,结果内容包含短语 “Welcome to…”。程序清单 1.6 应该可以达到目的。
单元测试——MockMvc详解:MockMvc详解
package com.alascanfu;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import com.alascanfu.controller.HomeController;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
/***
* @author: Alascanfu
* @date : Created in 2022/6/30 23:08
* @description:
* @modified By: Alascanfu
**/
@SpringBootTest
@WebMvcTest(HomeController.class)
@RunWith(SpringRunner.class)
public class HomeControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testHomePage() throws Exception {
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(view().name("home"))
.andExpect(content().string(containsString("Welcome to...")));
}
}
关于这个测试,你可能注意到的第一件事是,它与 TacoCloudApplicationTests
类在应用到它的注释方面略有不同。HomeControllerTest
使用 @WebMvcTest
注释,而不是 @SpringBootTest
标记。这是 Spring Boot 提供的一个特殊测试注释,它安排测试在 Spring MVC 应用程序的上下文中运行。更具体地说,在本例中,它安排 HomeController
在 Spring MVC 中注册,这样你就可以对它进行请求。
@WebMvcTest
还为测试 Spring MVC 提供了 Spring 支持。虽然可以让它启动服务器,但模拟 Spring MVC 的机制就足以满足你的目的了。测试类被注入了一个 MockMvc
对象中,以此用来测试来驱动模型。
testHomePage()
方法定义了要对主页执行的测试。它从 MockMvc
对象开始,执行针对 /
(根路径)的 HTTP GET 请求。该请求规定了下列期望值:
- 响应应该有一个HTTP 200(OK)状态。
- 视图应该有一个合理的主页名称。
- 呈现的视图应该包含 “Welcome to…”
如果在 MockMvc
对象执行请求之后,这些期望中的任何一个都没有满足,那么测试就会失败。但是控制器和视图模板是为了满足这些期望而编写的,所以测试应该能够通过,或者至少能够通过一些表示测试通过的绿色提示。
⭐️ 1.3.4 构建并运行应用程序
当应用程序启动时,将在控制台中看到一些 Spring ASCII 图飞过,然后看到一些日志条目描述应用程序启动时的步骤。在停止日志记录之前,将看到一个日志条目,其中说 Tomcat 在 port(s): 8080 (http) 上启动,这意味着已经准备好将 web 浏览器指向主页,以查看结果。
等一下,Tomcat 启动?何时将应用程序部署到 Tomcat?
Spring Boot 应用程序倾向于裹挟所有需要的东西,而不需要部署到某个应用服务器。你从未将应用程序部署到 Tomcat… 其实 Tomcat 是应用程序的一部分!(将在 1.3.6 小节中详细描述 Tomcat 如何成为应用程序的一部分的。)
现在应用程序已经启动,将 web 浏览器指向 http://localhost:8080
它可能没什么好看的。但这并不是一本关于平面设计的书。主页的简陋外观现在已经足够了。它为你了解 Spring 提供了一个坚实的开端。
在初始化项目时将其作为依赖项进行选择。它作为一个依赖项出现在生成的 pom.xml 文件中。Spring Boot Dashboard 甚至显示项目已经启用了 DevTools。但是什么是 DevTools,它能为您做什么?让我们快速浏览一下 DevTools 的几个最有用的特性。
⭐️ 1.3.5 了解 SpringBoot DevTools
顾名思义,DevTools 为 Spring 开发人员提供了一些方便的开发同步工具。这些是:
- 当代码更改时自动重启应用程序
- 当以浏览器为目标的资源(如模板、JavaScript、样式表等)发生变化时,浏览器会自动刷新
- 自动禁用模板缓存
- 如果 H2 数据库正在使用,则在 H2 控制台中构建
理解 DevTools 不是 IDE 插件是很重要的,它也不要求您使用特定的 IDE。它在 Spring Tool Suite、IntelliJ IDEA 和 NetBeans 中工作得同样好。此外,由于它仅用于开发目的,所以在部署生产环境时禁用它本身是非常明智的。(我们将在第 19 章中讨论如何部署应用程序。)现在,让我们关注一下 Spring Boot DevTools 最有用的特性,首先是自动重启应用程序。
自动重启应用程序
使用 DevTools 作为项目的一部分,将能够对项目中的 Java 代码和属性文件进行更改,并在短时间内查看这些更改的应用。DevTools 监视更改,当它看到某些内容发生更改时,它会自动重新启动应用程序。
更准确地说,当 DevTools 起作用时,应用程序被加载到 Java 虚拟机(JVM)中的两个单独的类加载器中。一个类装入器装入 Java 代码、属性文件以及项目的 src/main/path 中的几乎所有东西。这些项目可能会频繁更改。另一个类加载器加载了依赖库,它们不太可能经常更改
当检测到更改时,DevTools 只重新加载包含项目代码的类加载器,并重新启动 Spring 应用程序上下文,但不影响其他类加载器和 JVM。尽管这一策略很微妙,但它可以略微减少启动应用程序所需的时间。
这种策略的缺点是对依赖项的更改在自动重新启动时不可用。这是因为类装入器包含依赖项库 不是自动重新加载。这意味着,每当在构建规范中添加、更改或删除依赖项时,都需要重新启动应用程序才能使这些更改生效。
自动刷新浏览器和禁用模板缓存
默认情况下,模板选项(如 Thymeleaf 和 FreeMarker)被配置为缓存模板解析的结果,这样模板就不需要对它们所服务的每个请求进行修复。这在生产中非常有用,因为它可以带来一些性能上的好处。
缓存的模板在开发时不是很好。缓存的模板使它不可能在应用程序运行时更改模板,并在刷新浏览器后查看结果。即使做了更改,缓存的模板仍将继续使用,直到重新启动应用程序。
DevTools 通过自动禁用所有模板缓存来解决这个问题。对模板进行尽可能多的修改,并且要知道只有浏览器刷新才能看到结果。
但如果像我一样,甚至不想被点击浏览器的刷新按钮所累,如果能够立即在浏览器中进行更改并查看结果,那就更好了。幸运的是,DevTools 为我们这些懒得点击刷新按钮的人提供了一些特别的功能。
当 DevTools 起作用时,它会自动启用 LiveReload (http://livereload.com/)服务器和应用程序。就其本身而言,LiveReload 服务器并不是很有用。但是,当与相应的 LiveReload 浏览器插件相结合时,它会使得浏览器在对模板、图像、样式表、JavaScript 等进行更改时自动刷新 —— 实际上,几乎所有最终提供给浏览器的更改都会自动刷新。
在 H2 控制台中搭建
虽然项目还没有使用数据库,但这将在第 3 章中进行更改。如果选择使用 H2 数据库进行开发,DevTools 还将自动启用一个 H2 控制台,你可以从 web 浏览器访问该控制台。只需将 web 浏览器指向 http://localhost:8080/h2-console,就可以深入了解应用程序正在处理的数据。
⭐️ 1.3.6 回顾
回想一下是如何走到这一步的。简而言之,以下是构建基于 Spring 的 Taco Cloud 应用程序的步骤:
- 使用 Spring Initializr 创建了一个初始项目结构。
- 写了一个控制器类来处理主页请求。
- 定义了一个视图模板来呈现主页。
- 写了一个简单的测试类来检验上诉工作。
看起来很简单,不是吗?除了启动项目的第一步之外,所采取的每一个行动都是为了实现创建主页的目标。
事实上,编写的几乎每一行代码都是针对这个目标的。不计算 Java import
语句,只计算控制器类中的两行代码,而视图模板中没有 Spring 的特定代码。尽管测试类的大部分都使用了 Spring 的测试支持,但是在测试上下文中,它的侵入性似乎要小一些。
这是使用 Spring 开发的一个重要好处。可以关注于满足应用程序需求的代码,而不是满足框架的需求。尽管确实需要不时地编写一些特定于框架的代码,但这通常只是代码库的一小部分。如前所述,Spring (通过 Spring Boot)可以被认为是 无框架的框架。
这到底是怎么回事?Spring 在幕后做了什么来确保您的应用程序需求得到满足?为了理解 Spring 在做什么,让我们从构建规范开始。
在 pom.xml 文件中,声明了对 Web 和 Thymeleaf 启动器的依赖。这两个依赖关系带来了一些其他的依赖关系,包括:
- Spring MVC 框架
- 嵌入式 Tomcat
- Thymeleaf 和 Thymeleaf 布局方言
它还带来了 Spring Boot 的自动配置库。当应用程序启动时,Spring Boot 自动配置自动检测这些库并自动执行:
- 在 Spring 应用程序上下文中配置 bean 以启用 Spring MVC
- 将嵌入式 Tomcat 服务器配置在 Spring 应用程序上下文中
- 为使用 Thymeleaf 模板呈现 Spring MV C视图,配置了一个 Thymeleaf 视图解析器
简而言之,自动配置完成了所有繁重的工作,让你专注于编写实现应用程序功能的代码。如果你问我这样好不好,我会说这是一个很好的安排!
🔖 1.4 俯瞰 Spring 风景线
要了解 Spring 的风景线,只需查看完整版 Spring Initializr web 表单上的大量复选框列表即可。它列出了 100 多个依赖项选择,所以我不会在这里全部列出或者提供一个屏幕截图。但我鼓励你们去看看。与此同时,我将提到一些亮点。
⭐️ 1.4.1 Spring 核心框架
正如你所期望的,Spring 核心框架是 Spring 领域中其他一切的基础。它提供了核心容器和依赖注入框架。但它也提供了一些其他的基本特性。
其中包括 Spring MVC 和 Spring web 框架。已经了解了如何使用 Spring MVC 编写控制器类来处理 web 请求。但是,您还没有看到的是,Spring MVC 也可以用于创建产生非 HTML 输出的 REST API。我们将在第 2 章深入研究 Spring MVC,然后在第 6 章中讨论如何使用它来创建 REST API。
Spring 核心框架还提供了一些基本数据持久性支持,特别是基于模板的 JDBC 支持。将在第 3 章中看到如何使用 JdbcTemplate。
在 Spring 的最新版本(5.0.8)中,添加了对响应式编程的支持,包括一个新的响应式 web 框架 —— Spring WebFlux,它大量借鉴了 Spring MVC。将在第 3 部分中看到 Spring 的响应式编程模型,并在第 10 章中看到 Spring WebFlux。
⭐️ 1.4.2 Spring Boot
我们已经看到了 Spring Boot 的许多好处,包括启动依赖项和自动配置。在本书中我们确实会尽可能多地使用 Spring Boot,并避免任何形式的显式配置,除非绝对必要。但除了启动依赖和自动配置,Spring Boot 还提供了一些其他有用的特性:
- Actuator 提供了对应用程序内部工作方式的运行时监控,包括端点、线程 dump 信息、应用程序健康状况和应用程序可用的环境属性。
- 灵活的环境属性规范。
- 在核心框架的测试辅助之外,还有额外的测试支持。
Spring Boot 提供了一种基于 Groovy 脚本的替代编程模型,称为 Spring Boot CLI(命令行界面)。使用 Spring Boot CLI,可以将整个应用程序编写为 Groovy 脚本的集合,并从命令行运行它们。我们不会在 Spring Boot CLI 上花太多时间,但是当它适合我们的需要时,我们会接触它。
⭐️ 1.4.3 Spring Data
尽管 Spring 核心框架提供了基本的数据持久性支持,但 Spring Data 提供了一些非常惊人的功能:将应用程序的数据存储库抽象为简单的 Java 接口,同时当定义方法用于如何驱动数据进行存储和检索的问题时,对方法使用了命名约定。
更重要的是,Spring Data 能够处理几种不同类型的数据库,包括关系型(JPA)、文档型(Mongo)、图型(Neo4j)等。在第 3 章中,将使用 Spring Data 来帮助创建 Taco Cloud 应用程序的存储库。
⭐️ 1.4.4 Spring Security
应用程序安全性一直是一个重要的主题,而且似乎一天比一天重要。幸运的是,Spring 在 Spring security 中有一个健壮的安全框架。
Spring Security 解决了广泛的应用程序安全性需求,包括身份验证、授权和 API 安全性。尽管 Spring Security 的范围太大,本书无法恰当地涵盖,但我们将在第 4 章和第 12 章中讨论一些最常见的用例。
⭐️ 1.4.5 Spring Integration 和 Spring Batch
在某种程度上,大多数应用程序将需要与其他应用程序集成,甚至需要与同一应用程序的其他组件集成。为了满足这些需求,出现了几种应用程序集成模式。Spring Integration 和 Spring Batch 为基于 Spring 的应用程序提供了这些模式的实现。
Spring Integration 解决了实时集成,即数据在可用时进行处理。相反,Spring Batch 解决了批量集成的问题,允许在一段时间内收集数据,直到某个触发器(可能是一个时间触发器)发出信号,表示该处理一批数据了。将在第 9 章中研究 Spring Batch 和 Spring Integration。
⭐️ 1.4.6 Spring Cloud
在这个时代中,我们不再将应用程序作为单个部署单元来开发,而是将由几个称为 微服务 的单个部署单元组成应用程序。
微服务是一个热门话题,解决了几个实际的开发和运行时问题。然而,在这样做的同时,他们也带来了自己的挑战。这些挑战都将由 Spring Cloud 直接面对,Spring Cloud 是一组用 Spring 开发云本地应用程序的项目。
Spring Cloud 覆盖了很多地方,这本书不可能涵盖所有的地方。我们将在第 13、14 和 15 章中查看 Spring Cloud 的一些最常见的组件
🔖 1.5 小结
- Spring 的目标是让开发人员轻松应对挑战,比如创建 web 应用程序、使用数据库、保护应用程序和使用微服务。
- Spring Boot 构建在 Spring 之上,简化了依赖管理、自动配置和运行时监控,让 Spring 变得更加简单。
- Spring 应用程序可以使用 Spring Initializr 进行初始化,它是基于 web 的,并且在大多数 Java 开发环境中都支持它。
- 在 Spring 应用程序上下文中,组件(通常称为 bean)可以用 Java 或 XML 显式地声明,可以通过组件扫描进行发现,也可以用 Spring Boot 进行自动配置。
📚 参考文献
[ 1 ] [ 美 ] 克雷格·沃斯(Craig Walls).Spring 实战(第 5 版) [Z]. 北京: 人民邮电出版社,2020.