Appearance
Spring In Action 6th:Spring 入门
1. 初识 Spring
以下 XML 声明两个 bean,一个 InventoryService bean 和一个 ProductService bean,然后通过构造函数参数将 InventoryService bean 注入到 ProductService 中:
XML
<bean id="inventoryService"
class="com.example.InventoryService" />
<bean id="productService"
class="com.example.ProductService" />
<constructor-arg ref="inventoryService" />
</bean>1
2
3
4
5
6
7
2
3
4
5
6
7
不过现在基于 Java 的配置更为常见。以下基于 Java 的配置类等效于 XML 配置:
Java
@Configuration
public class ServiceConfiguration {
@Bean
public InventoryService inventoryService() {
return new InventoryService();
}
@Bean
public ProductService productService() {
return new ProductService(inventoryService());
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
@Configuration 注释向 Spring 表明这是一个配置类,它将为 Spring 应用程序上下文提供 beans。@Bean 注释指示方法返回的对象应作为 beans 添加到应用程序上下文中(默认情况下,它们各自的 bean IDs 将与定义它们的方法的名称相同)。
2. 初始化 Spring 应用程序
2.1. 使用 Spring Tool Suite 初始化 Spring 项目
可以使用 Spring Initializr 来创建 Spring 项目,可以通过以下方式来使用 Spring Initializr:
- 从 Web 应用程序
https://start.spring.io/创建; - 使用
curl命令从命令行创建; - 使用 Spring Boot 命令行接口从命令行创建;
- 使用 Spring Tool Suite 创建一个新项目的时候;
- 使用 IntelliJ IDEA 创建一个新项目的时候;
- 使用 NetBeans 创建一个新项目的时候;
Note 1:Spring Tool Suite 可运行二进制文件的下载地址:https://spring.io/tools。
Note 2:Spring 2.x 版本于 2023-11-24 停止维护,因此后续新建的 Spring 项目时不再有 Spring 2.x 版本的选项,只能从 Spring 3.x 版本开始选择(Spring 3.x 版本最低支持 JDK 17,对应 Java 语言版本 17)。若仍需要创建 Spring 2.x 版本的项目则可以尝试将 Server URL 修改为
https://start.aliyun.com/。
下面使用 IntelliJ IDEA 来创建一个新项目:
创建一个空项目:

图 2.1 - 创建空项目 新建 Spring Web Module:

图 2.2 - 新建 Spring Web Module 
图 2.3 - 使用 Spring Initializer 创建 Module 因为 Spring Boot 3.x 要求 JDK 17+,所以我们这里选择 2.7.17 版本:

图 2.4 - 选择 Spring Boot 版本及依赖
2.2. 检查 Spring 项目结构

与典型的 Maven 或 Gradle 项目结构一样,其中应用程序源代码位于 src/main/java 下,测试代码位于 src/test/java 下,非 Java 资源位于 src/main/resources 下。在该项目结构中,需要注意以下事项:
| 文件/文件夹 | 描述 |
|---|---|
mvnw 和 mvnw.cmd | 这些是 Maven 包装器脚本。即使您的计算机上没有安装 Maven,也可以使用这些脚本构建项目 |
pom.xml | 这是 Maven 构建规范 |
WebApplication.java | 这是引导项目的 Spring Boot 主类 |
application.properties | 该文件最初为空,但提供了一个可以指定配置属性的地方 |
static | 在此文件夹中,可以放置要提供给浏览器的任何静态内容(图像、样式表、JavaScript 等),最初为空 |
templates | 在此文件夹中,放置用于向浏览器呈现内容的模板文件,例如 Thymeleaf 模板,最初为空 |
WebApplicationTests.java | 这是一个简单的测试类,可确保成功加载 Spring 应用程序上下文 |
2.2.1. 探索构建规范
pom.xml 文件的完整内容如下所示:
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.7.17</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>graceful.hello.spring</groupId>
<artifactId>web</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>web</name>
<description>web</description>
<properties>
<java.version>11</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>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
第一个值得注意的项是 <parent> 元素,这指定项目将以 spring-boot-starter-parent 作为它的父 POM。
XML
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.17</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>1
2
3
4
5
6
2
3
4
5
6
在 <dependencies> 元素下声明了四个依赖项。前三个看起来应该比较熟悉。它们直接对应于在选择 Spring 依赖时的 Thymeleaf、Web 以及 Spring Boot DevTools 依赖项。Spring Initializr 默认包含第四个依赖项。
XML
<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>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
除 Spring Boot DevTools 以外的这些依赖项的 artifact ID 中都有 “starter” 这个词。Spring Boot starter 依赖项的特殊之处在于,它们本身通常没有任何库代码,而是间接地引入其他库。这些 starter 依赖主要提供了三个好处:
构建的文件将会小得多,也更容易管理,因为不需要对每一个可能需要的库都声明一个依赖项;
可以根据它们提供的功能来考虑需要的依赖关系,而不是根据库名来考虑。如果正在开发一个 Web 应用程序,那么将添加 web starter 依赖项,而不是一个编写 Web 应用程序的各个库的清单;
不用担心 library 版本问题。可以相信的是,对于给定版本的 Spring Boot,其间接地引入的库的版本将是兼容的,只需要考虑使用的是哪个版本的 Spring Boot;
最后,构建规范以 spring-boot-maven-plugin(Spring Boot 插件)结束。这个插件执行一些重要的功能:
提供了一个 Maven 编译目标,让您能够使用 Maven 运行应用程序。这将在构建并运行应用程序小节中尝试实现这个目标;
确保所有的依赖库都包含在可执行的 JAR 文件中,并且在运行时类路径中可用;
在 JAR 文件中生成一个 manifest 文件,表示引导类(在本例子中是
WebApplication)是可执行 JAR 的主类;
2.2.2. 引导应用程序
WebApplication 类的代码如下所示:
Java
package graceful.hello.spring.web;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
@SpringBootApplication 注释表明这是一个 Spring 引导应用程序。它组合了其他三个注释:
@SpringBootConfiguration:指定这个类为配置类。尽管这个类中还没有太多配置,但是如果需要,可以将 Javabased Spring Framework 配置添加到这个类中。实际上,这个注释是@Configuration注释的一种特殊形式;@EnableAutoConfiguration:启用 Spring 自动配置。稍后我们将详细讨论自动配置。现在,要知道这个注释告诉 Spring Boot 自动配置它认为需要的任何组件;@ComponentScan:启用组件扫描。这允许您声明其他带有@Component、@Controller、@Service等注释的类,以便让 Spring 自动发现它们并将它们注册为 Spring 应用程序上下文中的组件;
main() 方法调用 SpringApplication 类上的静态 run() 方法,该方法执行应用程序的实际引导,创建 Spring 应用程序上下文。传递给 run() 方法的两个参数是一个配置类和命令行参数。虽然传递给 run() 的配置类不必与引导类相同,但这是最方便、最典型的选择。
2.2.3. 测试应用程序
项目创建好后,我们先构建它,然后从命令行运行它:
PowerShell
PS D:\hello-spring\web> mvn package
...
PS D:\hello-spring\web> java -jar .\target\web-0.0.1-SNAPSHOT.jar1
2
3
2
3
或者直接使用 Spring Boot Maven:
PowerShell
PS D:\hello-spring\web> mvn spring-boot:runSpring Initializr 还提供了一个测试类:
Java
package graceful.hello.spring.web;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class WebApplicationTests {
@Test
void contextLoads() {
}
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
@SpringBootTest 的任务是加载测试的 Spring 应用程序上下文,它告诉 JUnit 使用 Spring Boot 功能引导测试。与 @SpringBootApplication 一样,@SpringBootTest 是一个复合注解,它本身用 @ExtendWith({SpringExtension.class}) 注解,以添加 Spring 测试功能到 JUnit5 中。
要从命令行运行该类和任何测试类,可以使用以下 Maven 命令:
PowerShell
PS D:\hello-spring\web> mvn test3. 编写 Spring 应用程序
3.1. 处理 Web 请求
Spring 附带了一个强大的 Web 框架,称为 Spring MVC。控制器是 Spring MVC 的核心概念,用于处理 Web 请求。以下示例中的控制器处理根路径的请求(例如 /),并将这些请求转发到主页视图,而不填充任何模型数据。
Java
package graceful.hello.spring.web.Controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/")
public String home() {
return "home";
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
可以看到 HomeController 用 @Controller 进行注释。@Controller 本身并没有做多少事情,它的主要目的是将该类识别为组件扫描的组件。因此 Spring 的组件扫描会自动发现它,并在 Spring 应用程序上下文中创建一个 HomeController 实例作为 bean。
Tip:实际上,其他一些注释(包括
@Component、@Service和@Repository)的用途与@Controller类似。您可以用任何其他的注解来有效地注释HomeController,它仍然可以工作。但是,选择@Controller更能描述该组件在应用程序中的角色。
home() 方法与控制器方法一样简单。它使用 @GetMapping 进行注释,以指示如果接收到根路径 / 的 HTTP GET 请求,则此方法应该处理该请求。home 的 String 返回值被解释为视图的逻辑名称。如何实现该视图取决于几个因素,这里我们使用 Thymeleaf 定义该模板。
Note:Thymeleaf 是目前较为流行的模板引擎。根据实际情况也可以选择 JSP、FreeMarker 等其他模板引擎。在使用 JSP 进行 Spring 引导时会有些麻烦。我们将在《Spring In Action 6th:开发 Web 应用程序》讨论其他模板选项,包括 JSP。
模板名称由逻辑视图名称派生而来,它的前缀是 /templates/,后缀是 .html。所以模板的结果路径是 /templates/home.html,即路径 /src/main/resources/templates/home.html。
3.2. 定义视图
home.html:
HTML
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
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>1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
关于这个模板没有太多要讨论的。唯一值得注意的代码行是显示 Taco Cloud 标志的 <img> 标记。它使用一个 Thymeleaf 的 th:src 属性和一个 @{…} 表达式引用具有上下文相对路径的图片。
该图片是通过上下文相对路径 /images/TacoCloud.png 进行引用的。因为像图片这样的静态内容应保存在 /src/main/resources/static 文件夹中。所以标志图片的最终文件存放路径为 /src/main/resources/static/images/TacoCloud.png。
3.3. 测试控制器
在对 HTML 页面的内容进行断言时,测试 Web 应用程序可能比较棘手。幸运的是,Spring 提供了一些强大的测试支持,使测试 Web 应用程序变得很容易。
这里测试将对根路径 / 执行一个 HTTP GET 请求并期望得到一个成功的结果,其中视图名称为 home,结果内容包含短语 Welcome to…。下面的程序清单应该可以达到目的:
Java
package graceful.hello.spring.web;
import graceful.hello.spring.web.Controllers.HomeController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import static org.hamcrest.Matchers.containsString;
@WebMvcTest(HomeController.class)
public class HomeControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testHomePage() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("home"))
.andExpect(MockMvcResultMatchers.content().string(containsString("Welcome to...")));
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
关于这个测试,您可能注意到的第一件事是,它与 WebApplicationTests 类在应用到它的注释方面略有不同。HomeControllerTest 使用 @WebMvcTest 注释,而不是 @SpringBootTest 标记。这是 Spring Boot 提供的一个特殊测试注释,它安排测试在 Spring MVC 应用程序的上下文中运行。更具体地说,在本例中,它安排 HomeController 在 Spring MVC 中注册,这样您就可以对它进行请求。@WebMvcTest 还为测试 Spring MVC 提供了 Spring 支持。虽然可以让它启动服务器,但模拟 Spring MVC 的机制就足以满足您的目的了。
执行测试:
PowerShell
PS D:\hello-spring\web> mvn test3.4. 构建并运行应用程序
正如有多种方法可以初始化 Spring 应用程序一样,也有多种方法可以运行 Spring 应用程序。
如果选择使用 Spring Tool Suite 来初始化和处理项目,则会有一个称为 Spring Boot Dashboard 的便利功能可以帮助您在 IDE 中运行应用程序。Spring Boot Dashboard 显示为一个选项卡,通常位于 IDE 窗口的左下方。图 3.1 显示了 Spring Boot Dashboard 的注释截图。

当应用程序启动时,将看到一个日志条目,其中说 Tomcat 在 port(s): 8080 (http) 上启动。实际上我们并不需要手动将应用部署到某个应用服务器。其实 Tomcat 是应用程序的一部分!Spring Boot 应用程序倾向于裹挟所有需要的东西。
3.5. 了解 Spring Boot DevTools
DevTools 为 Spring 开发人员提供了一些方便的开发同步工具:
- 当代码更改时自动重启应用程序;
- 当以浏览器为目标的资源(如模板、JavaScript、样式表等)发生变化时,浏览器会自动刷新;
- 自动禁用模板缓存;
- 如果 H2 数据库正在使用,则在 H2 控制台中构建;
DevTools 不是 IDE 插件它也不要求您使用特定的 IDE(它在 Spring Tool Suite、IntelliJ IDEA 和 NetBeans 中工作得同样好)。此外,由于它仅用于开发目的,所以在部署生产环境时禁用它是非常明智的做法。
3.5.1. 自动重启应用程序
使用 DevTools 作为项目的一部分,将能够对项目中的 Java 代码和属性文件进行更改,并在短时间内查看这些更改的应用。DevTools 监视更改,当它看到某些内容发生更改时,它会自动重新启动应用程序。
更准确地说,当 DevTools 起作用时,应用程序被加载到 Java 虚拟机(JVM)中的两个单独的类加载器中。一个类装入器装入 Java 代码、属性文件以及项目的 src/main/path 中的几乎所有东西。这些项目可能会频繁更改。另一个类加载器加载了依赖库,它们不太可能经常更改。
当检测到更改时,DevTools 只重新加载包含项目代码的类加载器,并重新启动 Spring 应用程序上下文,但不影响其他类加载器和 JVM。尽管这一策略很微妙,但它可以略微减少启动应用程序所需的时间。
这种策略的缺点是对依赖项的更改在自动重新启动时不可用。这是因为类装入器包含依赖项库,不是自动重新加载。这意味着,每当在构建规范中添加、更改或删除依赖项时,都需要重新启动应用程序才能使这些更改生效。
3.5.2. 自动刷新浏览器和禁用模板缓存
默认情况下,模板选项(如 Thymeleaf 和 FreeMarker)被配置为缓存模板解析的结果,这样模板就不需要对它们所服务的每个请求进行修复。这在生产中非常有用,因为它可以带来一些性能上的好处。
但是,缓存的模板在开发时不是很好。缓存的模板使它不可能在应用程序运行时更改模板,并在刷新浏览器后查看结果。即使做了更改,缓存的模板仍将继续使用,直到重新启动应用程序。DevTools 通过自动禁用所有模板缓存来解决这个问题。对模板进行尽可能多的修改,并且要知道只有浏览器刷新才能看到结果。
当 DevTools 起作用时,它会自动启用 LiveReload 服务器和应用程序。就其本身而言,LiveReload 服务器并不是很有用。但是,当与相应的 LiveReload 浏览器插件相结合时,它会使得浏览器在对模板、图像、样式表、JavaScript 等进行更改时自动刷新 —— 实际上,几乎所有最终提供给浏览器的更改都会自动刷新。
LiveReload 有针对 Google Chrome、Safari 和 Firefox 浏览器的插件。请访问 http://livereload.com/extensions/,了解如何为浏览器安装 LiveReload。
3.5.3. 在 H2 控制台中构建
如果选择使用 H2 数据库进行开发,DevTools 还将自动启用一个 H2 控制台,您可以从 Web 浏览器访问该控制台。只需将 Web 浏览器指向 http://localhost:8080/h2-console,就可以深入了解应用程序正在处理的数据。
4. 俯瞰 Spring 风景线
4.1. Spring 核心框架
Spring 核心框架是 Spring 领域中其他一切的基础。它提供了核心容器和依赖注入框架。但它也提供了一些其他的基本特性。
其中包括 Spring MVC 和 Spring Web 框架(实际上 Spring MVC 控制器也可以用于创建产生非 HTML 输出的 REST API);
Spring 核心框架还提供了一些基本数据持久性支持,特别是基于模板的 JDBC(JdbcTemplate)支持;
Spring 具有对响应式编程的支持,包括一个新的响应式 Web 框架 —— Spring WebFlux,它大量借鉴了 Spring MVC;
4.2. Spring Boot
Spring Boot 除了启动依赖和自动配置,还提供了一些其他有用的特性:
Actuator 提供了对应用程序内部工作方式的运行时监控,包括端点、线程 dump 信息、应用程序健康状况和应用程序可用的环境属性;
灵活的环境属性规范;
在核心框架的测试辅助之外,还有额外的测试支持;
此外,Spring Boot 提供了一种基于 Groovy 脚本的替代编程模型,称为 Spring Boot CLI(命令行界面)。使用 Spring Boot CLI,可以将整个应用程序编写为 Groovy 脚本的集合,并从命令行运行它们。
4.3. Spring Data
虽然 Spring 框架核心提供了基本数据持久化支持,但 Spring Data 则更为强大,支持将应用程序的数据存储库定义为简单的 Java 接口,使用命名约定来定义方法以驱动数据的存储和检索。此外,Spring Data 还能够与多种数据库一起工作,包括关系型(通过 JDBC 或 JPA)、文档型(Mongo)、图形型(Neo4j)等。
4.4. Spring Security
Spring Security 解决了广泛的应用程序安全性需求,包括身份验证、授权和 API 安全性等等。
4.5. Spring Integration 和 Spring Batch
大多数应用程序将需要与其他应用程序集成,甚至需要与同一应用程序的其他组件集成。为了满足这些需求,出现了几种应用程序集成模式。Spring Integration 和 Spring Batch 为基于 Spring 的应用程序提供了这些模式的实现。Spring Integration 解决了实时集成,即数据在可用时进行处理。相反,Spring Batch 解决了批量集成的问题,允许在一段时间内收集数据,直到某个触发器(可能是一个时间触发器)发出信号。
4.6. Spring Cloud
关于 Spring Cloud 的更完整的讨论,建议看看 Thomas Vitale 的《Cloud Native Spring in Action》。
4.7. Spring Native
Spring Native 一个相对较新的扩展项目。这个实验项目使用 GraalVM 本机镜像将 Spring 启动项目编译为本机可执行文件,使镜像的启动速度大大加快,并且更轻量级。
更多信息可访问 https://github.com/spring-projects-experimental/spring-native。