最值得一看的Spring实战 (第5版)上!!笔者强力推荐!!
Spring实战 第二周
笔者强力推荐大家看看Spring实战(第5版)这本书,在这本书里,你可以感受到Spring的魅力所在!!
本次介绍主要介绍了Spring实战5的1-9章 !在接下来的一小段时间里,你如果读完这篇文章,你将会对Spring增加一层更深处的理解!明白Spring的魅力所在!(希望你能读完这篇文章!)
第一部分 Spring基础
第一章 Spring入门
随着软件开发的发展,Spring框架发生了变化,以解决现代开发问题,包括微服务和响应式编程。Spring 还通过引入 Spring Boot 来简化自己的开发模型。
1、什么是Spring?
Spring 的核心是一个 容器,通常称为 Spring 应用程序上下文,用于创建和管理应用程序组件。这些组件(或 bean)在 Spring 应用程序上下文中连接在一起以构成一个完整的应用程序,就像将砖、灰浆、木材、钉子、管道和电线绑在一起以组成房屋。
将 bean 连接在一起的行为是基于一种称为 依赖注入(DI)的模式。依赖项注入的应用程序不是由组件自身创建和维护它们依赖的其他 bean 的生命周期,而是依赖于单独的实体(容器)来创建和维护所有组件,并将这些组件注入需要它们的 bean。通常通过构造函数参数或属性访问器方法完成此操作。
除了其核心容器之外,Spring 和完整的相关库产品组合还提供 Web 框架、各种数据持久性选项、安全框架与其他系统的集成、运行时监视、微服务支持、响应式编程模型以及许多其他功能,应用于现代应用程序开发。
在最新版本的 Spring 中,基于 Java 的配置更为常见。
@Configuration 注释向 Spring 表明这是一个配置类,它将为 Spring 应用程序上下文提供 beans。 配置的类方法带有 @Bean 注释,指示它们返回的对象应作为 beans 添加到应用程序上下文中(默认情况下,它们各自的 bean IDs 将与定义它们的方法的名称相同)。
与基于 XML 的配置相比,基于 Java 的配置具有多个优点,包括更高的类型安全性和改进的可重构性。即使这样,仅当 Spring 无法自动配置组件时,才需要使用 Java 或 XML 进行显式配置。
随着 Spring Boot 的推出,自动配置的优势已经远远超出了组件扫描和自动装配。Spring Boot 是 Spring 框架的扩展,它提供了多项生产力增强功能。这些增强功能中最著名的就是 自动配置,在这种配置中,Spring Boot 可以根据类路径中的条目、环境变量和其他因素,合理地猜测需要配置哪些组件,并将它们连接在一起。
1.2 初始化 Spring 应用程序
1.2.1、初始化Spring项目
Spring Initializr 既是一个基于浏览器的 Web 应用程序,又是一个 REST API,它们可以生成一个基本的 Spring 项目结构,可以使用所需的任何功能充实自己。 使用 Spring Initializr 的几种方法如下:
-
从 Web 应用程序 http://start.spring.io 创建
-
使用 curl 命令从命令行创建
-
使用 Spring Boot 命令行接口从命令行创建
-
使用 Spring Tool Suite创建一个新项目的时候
-
使用 IntelliJ IDEA 创建一个新项目的时候
-
使用 NetBean 创建一个新项目的时候
顾名思义,Spring Tool Suite 是一个绝佳的 Spring 开发环境。但是它还提供了一个方便的 Spring Boot Dashboard 功能(至少在撰写本文时)其他任何 IDE 选项中均不提供。
idea需要做一些配置!可以看我的这篇文章!!!!
https://blog.csdn.net/weixin_45395031/article/details/104532287
1.2.2、Spring 项目结构
你可能会认为这是典型的 Maven 或 Gradle 项目结构,其中应用程序源代码位于src/main/java 下,测试代码位于 src/test/java 下,非 Java 资源位于 src/main/resources 下 。
-
mvnw 和 mvnw.cmd —— 这些是 Maven 包装器脚本。即使你的计算机上没有安装 Maven,也可以使用这些脚本构建项目。
-
pom.xml —— 这是 Maven 构建规范。 Application.java —— 这是引导项目的 Spring Boot 主类。
-
application.properties ——该文件最初为空,但提供了一个可以指定配置属性的地方。我们将在本章中对此文件进行一些修改,但在第 5 章中将详细介绍配置属性。
-
static —— 在此文件夹中,可以放置要提供给浏览器的任何静态内容(图像、样式表、JavaScript 等),最初为空。
-
templates —— 在此文件夹中,放置用于向浏览器呈现内容的模板文件。最初为空,但很快会添加 Thymeleaf 模板。
-
ApplicationTests.java —— 这是一个简单的测试类,可确保成功加载 Spring
应用程序上下文。开发应用程序时,将添加更多的测试。
探索构建规范
程序清单 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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>sia</groupId>
<artifactId>taco-cloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>taco-cloud</name>
<description>Taco Cloud Example</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<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>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>htmlunit-driver</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 文件中第一个值得注意的项是 元素。你选择将应用程序构建为可执行的 JAR 文件,而不是 WAR 文件。这可能是你所做的最奇怪的选择之一,特别是对于 web 应用程序。毕竟,传统的 Java web 应用程序被打包为 WAR 文件,而 JAR 文件是库和偶尔使用的桌面 UI 应用程序的首选打包方式。
选择 JAR 打包是一种不切实际的选择。虽然 WAR 文件非常适合部署到传统的 Java 应用服务器,但是它们并不适合大多数云平台。尽管一些云平台(如 Cloud Foundry)能够部署和运行 WAR 文件,但是所有的 Java 云平台都能够运行可执行的 JAR 文件。因此,Spring Initializr 默认为 JAR 打包,除非你不让它这样做。
元素,更具体地说,注意它的 子元素。这指定您的项目将 spring-boot-starter-parent 作为它的父 POM。除此之外,这个父 POM 还为 Spring 项目中常用的几个库提供依赖项管理。对于父 POM 覆盖的那些库,不必指定版本,因为它是从父 POM 继承的。2.0.4.RELEASE 版本,表示你正在使用 Spring Boot 2.0.4,这样项目将使用继承自 Spring Boot 版本中定义的依赖项管理。
在讨论依赖项时,请注意在 元素下声明了三个依赖项。前两个看起来应该比较熟悉。它们直接对应于在单击 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 运行应用程序。
-
确保所有的依赖库都包含在可执行的 JAR 文件中,并且在运行时类路径中可用。 在 JAR 文件中生成一个 manifest文件,表示引导类(在本书例子中是 TacoCloudApplication)是可执行 JAR 的主类。
引导应用程序
因为将从一个可执行的 JAR 运行应用程序,所以在运行 JAR 文件时,有一个主类来执行是很重要的。还需要至少一个最小的 Spring 配置文件来引导应用程序。这就是将在 TacoCloudApplication 类中找到的内容,
程序清单 1.2 Taco Cloud 引导类
package tacos;
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);
}
}
@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 tacos;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class TacoCloudApplicationTests {
@Test
public void contextLoads() {
}
}
在 TacoCloudApplicationTests 中没有太多东西:类中的一个测试方法是空的。尽管如此,这个测试类确实执行了必要的检查,以确保 Spring 应用程序上下文能够成功加载。如果做了任何阻止创建 Spring 应用程序上下文的更改,则此测试将失败,这样你就可以通过解决问题来应对。
还要注意用 @RunWith(SpringRunner.class) 注释的类。@RunWith 是一个 JUnit 注释,提供了一个测试运行器来引导 JUnit 运行测试用例。请将清单 1.3 看作是对它的基准应用程序测试,即将插件应用到 JUnit 以提供自定义测试行为。在本例中,JUnit 被赋予了 SpringRunner,这是一个由 Spring 提供的测试运行程序,它提供了创建一个 Spring 应用程序上下文的功能,以供测试运行。
其他名字的测试运行器
如果你已经熟悉编写 Spring 测试,或者正在查看一些现有的基于 Spring 的测试类,那么你可能已经看到了一个名为 SpringJUnit4ClassRunner 的测试运行器。SpringRunner 是 SpringJUnit4ClassRunner 的别名,它是在 Spring 4.3 中引入的,用于删除与特定版本的 JUnit (例如,JUnit4)的关联。毫无疑问,别名更易于阅读和输入。
@SpringBootTest 告诉 JUnit 使用 Spring 引导功能引导测试。现在,把它看作是在 main() 方法中调用 SpringApplication.run() 的测试类就足够了。在本书的过程中,将多次看到 @SpringBootTest,我们将揭示它的一些功能。
最后,还有测试方法本身。尽管 @RunWith(SpringRunner.class) 和 @SpringBootTest 的任务是加载用于测试的 Spring 应用程序上下文,但是如果没有任何测试方法,它们将没有任何事情要做。即使没有任何断言或任何类型的代码,这个空的测试方法也会调用两个注释完成它们的工作,并加载 Spring 应用程序上下文。如果运行过程中有任何问题,测试就会失败。
至此,我们已经完成了对 Spring Initializr 提供的代码的回顾。看到了一些用于开发 Spring 应用程序的样板基础,但是仍然没有编写任何代码。现在,启动 IDE,掸掉键盘上的灰尘,并向 Taco Cloud 应用程序添加一些定制代码。
1.3 编写 Spring 应用程序
在刚刚开始的时候,添加到 Taco Cloud 应用程序的第一个功能是主页,这似乎是合适的。当你添加主页,你将创建两个代码构件:
-
一个处理主页请求的控制器类
-
一个视图模板,定义了主页的外观
因为测试很重要,所以还将编写一个简单的测试类来测试主页。
1.3.1 处理 web 请求
Spring 附带了一个强大的 web 框架,称为 Spring MVC。Spring MVC 的核心是控制器的概念,这是一个处理请求并使用某种信息进行响应的类。对于面向浏览器的应用程序,控制器的响应方式是可选地填充模型数据并将请求传递给视图,以生成返回给浏览器的 HTML。
编写一个简单的控制器类来处理根路径的请求(例如 /),并将这些请求转发到主页视图,而不填充任何模型数据。程序清单 1.4 显示了简单的控制器类。
程序清单 1.4 主页控制器
package tacos;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/")
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 看起来是一个不做的选择,但是在使用 JSP 进行 Spring 引导时仍然存在一些需要克服的挑战。
模板名称由逻辑视图名称派生而来,它的前缀是 /templates/,后缀是 .html。模板的结果路径是 /templates/home.html。因此,需要将模板放在项目的 /src/main/resources/templates/home.html 中。现在让我们创建该模板。
1.3.2 定义视图
程序清单 1.5 Taco Cloud 主页模板
<!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>
使用一个 Thymeleaf 的 th:src 属性和一个 @{…} 表达式引用具有上下文相对路径的图片。除去这些,它只是一个 Hello World 页面。
该图片是通过上下文相对路径 /images/TacoCloud.png 进行引用的。从我们对项目结构的回顾中可以想起,像图片这样的静态内容保存在 /src/main/resources/static 文件夹中。这意味着 Taco Cloud 标志图片也必须驻留在项目的 /src/main/resources/static/images/TacoCloud.png 中。
现在已经有了处理主页请求的控制器和呈现主页的视图模板,几乎已经准备好启动应用程序并看到它的实际运行效果了。但首先,让我们看看如何针对控制器编写测试。
1.3.3 测试控制器
1.3.4 构建并运行应用程序
Spring Boot Dashboard 显示为一个选项卡,通常位于 IDE 窗口的左下方。图 1.7 显示了 Spring Boot Dashboard 的注释截图。
到目前为止,忽略了 DevTools。在初始化项目时将其作为依赖项进行选择。它作为一个依赖项出现在生成的 pom.xml 文件中。Spring Boot Dashboard 甚至显示项目已经启用了 DevTools。但是什么是 DevTools,它能为您做什么?让我们快速浏览一下 DevTools 的几个最有用的特性。
1.3.5 了解 Spring Boot 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 等进行更改时自动刷新 —— 实际上,几乎所有最终提供给浏览器的更改都会自动刷新。
LiveReload 有针对 Google Chrome、Safari 和 Firefox 浏览器的插件。(对不起,ie 和 Edge 的粉丝们。)请访问 http://livereload.com/extensions/,了解如何为浏览器安装 LiveReload。
在 H2 控制台中构建
虽然项目还没有使用数据库,但这将在第 3 章中进行更改。如果选择使用 H2 数据库进行开发,DevTools 还将自动启用一个 H2 控制台,你可以从 web 浏览器访问该控制台。只需将 web 浏览器指向 http://localhost:8080/h2-console,就可以深入了解应用程序正在处理的数据。
至此,已经编写了一个完整但简单的 Spring 应用程序。你将在本书的整个过程中扩展它。但是现在是回顾已经完成的工作以及 Spring 如何发挥作用的好时机。
1.3.6 回顾
回想一下是如何走到这一步的。简而言之,以下是构建基于 Spring 的 Taco Cloud 应用程序的步骤:
使用 Spring Initializr 创建了一个初始项目结构。
- 写了一个控制器类来处理主页请求。
- 定义了一个视图模板来呈现主页。
- 写了一个简单的测试类来检验上诉工作。
在 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风景线
1.4.1 Spring 核心框架
正如你所期望的,Spring 核心框架是 Spring 领域中其他一切的基础。它提供了核心容器和依赖注入框架。但它也提供了一些其他的基本特性。
Spring 核心框架还提供了一些基本数据持久性支持,特别是基于模板的 JDBC 支持。将在第 3 章中看到如何使用 JdbcTemplate。
在 Spring 的最新版本(5.0.8)中,添加了对响应式编程的支持,包括一个新的响应式 web 框架 —— Spring WebFlux,它大量借鉴了 Spring MVC。
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)等。
1.4.4 Spring Security
应用程序安全性一直是一个重要的主题,而且似乎一天比一天重要。幸运的是,Spring 在 Spring security 中有一个健壮的安全框架。
Spring Security 解决了广泛的应用程序安全性需求,包括身份验证、授权和 API 安全性。
1.4.5 Spring Integration 和 Spring Batch
在某种程度上,大多数应用程序将需要与其他应用程序集成,甚至需要与同一应用程序的其他组件集成。为了满足这些需求,出现了几种应用程序集成模式。Spring Integration 和 Spring Batch 为基于 Spring 的应用程序提供了这些模式的实现。
Spring Integration 解决了实时集成,即数据在可用时进行处理。相反,Spring Batch 解决了批量集成的问题,允许在一段时间内收集数据,直到某个触发器(可能是一个时间触发器)发出信号,表示该处理一批数据了。
1.4.6 Spring Cloud
在我写这篇文章的时候,应用程序开发领域正在进入一个新时代,在这个时代中,我们不再将应用程序作为单个部署单元来开发,而是将由几个称为 微服务 的单个部署单元组成应用程序。
微服务是一个热门话题,解决了几个实际的开发和运行时问题。然而,在这样做的同时,他们也带来了自己的挑战。这些挑战都将由 Spring Cloud 直接面对,Spring Cloud 是一组用 Spring 开发云本地应用程序的项目。
Spring Cloud 覆盖了很多地方,这本书不可能涵盖所有的地方。我们将在第 13、14 和 15 章中查看 Spring Cloud 的一些最常见的组件。关于 Spring Cloud 的更完整的讨论,我建议看看 John Carnell 的 Spring Microservices in Action(Manning, 2017, www.manning.com/books/spring-microservices-in-action)。
1.5 小结
Spring 的目标是让开发人员轻松应对挑战,比如创建 web 应用程序、使用数据库、保护应用程序和使用微服务。
Spring Boot 构建在 Spring 之上,简化了依赖管理、自动配置和运行时监控,让 Spring 变得更加简单。
Spring 应用程序可以使用 Spring Initializr 进行初始化,它是基于 web 的,并且在大多数 Java 开发环境中都支持它。
在 Spring 应用程序上下文中,组件(通常称为 bean)可以用 Java 或 XML 显式地声明,可以通过组件扫描进行发现,也可以用 Spring Boot 进行自动配置。
第二章 开发Web应用程序
第 1 章中,创建了第一个 Spring MVC 控制器来显示应用程序主页。但是 Spring MVC 能做的远不止简单地显示静态内容。在本章中,将开发 Taco Cloud 应用程序的第一个主要功能 —— 设计自定义 Taco 的能力。在此过程中,将深入研究 Spring MVC,并了解如何显示模型数据和处理表单输入。
2.1 展示信息
应用程序的域是它所处理的主题领域 —— 影响应用程序理解的思想和概念。在 Taco Cloud 应用程序中,领域包括 Taco 设计、组成这些设计的成分、客户和客户下的 Taco 订单等对象。首先,我们将关注玉米饼配料。
在领域中,玉米饼配料是相当简单的对象。每一种都有一个名称和一个类型,这样就可以在视觉上对其进行分类(蛋白质、奶酪、酱汁等)。每一个都有一个 ID,通过这个 ID 可以轻松、明确地引用它。下面的成分类定义了需要的域对象。
程序清单 2.1 定义玉米饼的配料
package tacos;
import lombok.Data;
import lombok.RequiredArgsConstructor;
@Data
@RequiredArgsConstructor
public class Ingredient {
private final String id;
private final String name;
private final Type type;
public static enum Type {
WRAP, PROTEIN, VEGGIES, CHEESE, SAUCE
}
}
这是一个普通的 Java 域类,定义了描述一个成分所需的三个属性。对于程序清单 2.1 中定义的 Ingredient 类,最不寻常的事情可能是它似乎缺少一组常用的 getter 和 setter 方法,更不用说像 equals()、hashCode()、toString() 等有用的方法。
类级别的 @Data 注释是由 Lombok 提供的,它告诉 Lombok 生成所有缺少的方法,以及接受所有最终属性作为参数的构造函数。通过使用 Lombok,可以让 Ingredient 的代码保持整洁。
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2.1.2 创建控制器类
控制器是 Spring MVC 框架的主要参与者。它们的主要工作是处理 HTTP 请求,或者将请求传递给视图以呈现 HTML(浏览器显示),或者直接将数据写入响应体(RESTful)。
package tacos.web;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import lombok.extern.slf4j.Slf4j;
import tacos.Taco;
import tacos.Ingredient;
import tacos.Ingredient.Type;
@Slf4j
@Controller
@RequestMapping("/design")
public class DesignTacoController {
@GetMapping
public String showDesignForm(Model model) {
List<Ingredient> ingredients = Arrays.asList(
new Ingredient("FLTO", "Flour Tortilla", Type.WRAP),
new Ingredient("COTO", "Corn Tortilla", Type.WRAP),
new Ingredient("GRBF", "Ground Beef", Type.PROTEIN),
new Ingredient("CARN", "Carnitas", Type.PROTEIN),
new Ingredient("TMTO", "Diced Tomatoes", Type.VEGGIES),
new Ingredient("LETC", "Lettuce", Type.VEGGIES),
new Ingredient("CHED", "Cheddar", Type.CHEESE),
new Ingredient("JACK", "Monterrey Jack", Type.CHEESE),
new Ingredient("SLSA", "Salsa", Type.SAUCE),
new Ingredient("SRCR", "Sour Cream", Type.SAUCE)
);
Type[] types = Ingredient.Type.values();
for (Type type : types) {
model.addAttribute(type.toString().toLowerCase(),
filterByType(ingredients, type));
}
model.addAttribute("design", new Taco());
return "design";
}
}
关于 DesignTacoController,首先要注意的是在类级应用的一组注释。第一个是 @Slf4j,它是 Lombok 提供的注释,在运行时将自动生成类中的 SLF4J(Java 的简单日志门面,https://www.slf4j.org/)记录器。这个适当的注释具有与显式地在类中添加以下行相同的效果:
private static final org.slf4j.Logger log =
org.slf4j.LoggerFactory.getLogger(DesignTacoController.class);
下一个应用到 DesignTacoController 的注释是 @Controller。此注释用于将该类标识为控制器并将其标记为组件扫描的候选对象,以便 Spring 将发现该类并在 Spring 应用程序上下文中自动创建 DesignTacoController 实例作为 bean。
DesignTacoController 也用 @RequestMapping 注释。@RequestMapping 注释在类级应用时,指定该控制器处理的请求的类型。在本例中,它指定 DesignTacoController 将处理路径以 /design 开头的请求。
2.1.3 设计视图
2.2 处理表单提交
2.3 验证表单输入
2.3.1 声明验证规则
对于 Taco 类,希望确保 name 属性不是空的或 null 的,并且所选配料列表中至少有一项。下面的程序清单显示了一个更新后的 Taco 类,它使用 @NotNull 和 @Size 来声明这些验证规则。
程序清单 2.10 为 Taco 域类添加验证
package tacos;
import java.util.List;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.Data;
@Data
public class Taco {
@NotNull
@Size(min=5, message="Name must be at least 5 characters long")
private String name;
@Size(min=1, message="You must choose at least 1 ingredient")
private List<String> ingredients;
}
你会发现,除了要求 name 属性不为 null,同时你声明它应该有一个值是至少 5 个字符的长度。
当涉及到对提交玉米饼订单进行验证声明时,必须对 Order 类应用注解。对于地址的属性,只需要确保用户没有留下任何空白字段。对于这一点,将使用 Hibernate Validator 的 @NotBlank 注解。
支付领域的验证是一个比较奇特的存在。你不仅需要确保 ccNumber 属性不为空,还要确保它包含的是一个有效的信用卡号码的值。该 ccExpiration 属性必须符合 MM/YY(两位数的年/月)格式。而 ccCVV 属性必须是一个三位的数字。为了实现这种验证,需要使用一些其他的 Java Bean Validation API 注释,同时需要从 Hibernate Validator 集合中借用一些验证注解。下面程序清单列出了验证 Order 类所需要的改变。
程序清单 2.11 验证 Order 字段
package tacos;
import javax.validation.constraints.Digits;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.CreditCardNumber;
import javax.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class Order {
@NotBlank(message="Name is required")
private String name;
@NotBlank(message="Street is required")
private String street;
@NotBlank(message="City is required")
private String city;
@NotBlank(message="State is required")
private String state;
@NotBlank(message="Zip code is required")
private String zip;
@CreditCardNumber(message="Not a valid credit card number")
private String ccNumber;
@Pattern(regexp="^(0[1-9]|1[0-2])([\\/])([1-9][0-9])$",
message="Must be formatted MM/YY")
private String ccExpiration;
@Digits(integer=3, fraction=0, message="Invalid CVV")
private String ccCVV;
}
可以看到,ccNumber 属性用 @CreditCardNumber 进行了注释。该注释声明属性的值必须是通过 Luhn 算法(https://en.wikipedia.org/wiki/Luhn_algorithm)检查过的有效信用卡号。这可以防止用户出错的数据和故意错误的数据,但不能保证信用卡号码实际上被分配到一个帐户,或该帐户可以用于交易。
不幸的是,没有现成的注释来验证 ccExpiration 属性的 MM/YY 格式。我已经应用了 @Pattern 注释,为它提供了一个正则表达式,以确保属性值符合所需的格式。如果想知道如何破译正则表达式,我建议查看许多在线正则表达式指南,包括 http://www.regularexpressions.info/。正则表达式语法是一门黑暗的艺术,当然也超出了本书的范围。
最后,用 @Digits 注释 ccCVV 属性,以确保值恰好包含三个数字。
所有的验证注释都包含一个消息属性,该属性定义了如果用户输入的信息不符合声明的验证规则的要求时将显示给用户的消息。
2.3.2 在表单绑定时执行验证
2.3.3 显示验证错误
2.4 使用视图控制器
2.5 选择视图模板库
2.5.1 缓存模板
默认情况下,模板在第一次使用时只解析一次,解析的结果被缓存以供后续使用。对于生产环境来说,这是一个很好的特性,因为它可以防止对每个请求进行冗余的模板解析,从而提高性能。
有一种方法可以禁用缓存。只需将 templateappropriate 高速缓存属性设置为 false。
表 2.3 启用/禁用模板缓存的属性
模板 缓存使能属性
- Freemarker spring.freemarker.cache
- Groovy Templates.spring.groovy.template.cache
- Mustache spring.mustache.cache
- Thymeleaf spring.thymeleaf.cache
默认情况下,所有这些属性都设置为 true 以启用缓存。可以通过将其缓存属性设置为 false 来禁用所选模板引擎的缓存。例如,要禁用 Thymeleaf 缓存,请在 application.properties 中添加以下行:
spring.thymeleaf.cache = false
惟一的问题是,在将应用程序部署到生产环境之前,一定要删除这一行(或将其设置为 true)。一种选择是在 profile 文件中设置属性。
一个更简单的选择是使用 Spring Boot 的 DevTools。
2.6 小结
Spring 提供了一个强大的 web 框架,称为 Spring MVC,可以用于开发 Spring 应用程序的 web 前端。
Spring MVC 是基于注解的,可以使用 @RequestMapping、@GetMapping 和 @PostMapping 等注解来声明请求处理方法。
大多数请求处理方法通过返回视图的逻辑名称来结束,例如一个 Thymeleaf 模板,请求(以及任何模型数据)被转发到该模板。
Spring MVC 通过 Java Bean Validation API 和 Hibernate Validator 等验证 API 的实现来支持验证。
视图控制器可以用来处理不需要模型数据或处理的 HTTP GET 请求。
除了 Thymeleaf,Spring 还支持多种视图选项,包括 FreeMarker、Groovy Templates 和 Mustache。
第三章 处理数据
本章内容:
使用 Spring JdbcTemplate
使用 SimpleJdbcInsert 插入数据
使用 Spring Data 声明 JPA repositories
3.1 使用 JDBC 读写数据
3.2 使用 Spring Data JPA 持久化数据
Spring Data 项目是一个相当大的伞形项目,几个子项目组成,其中大多数子项目关注于具有各种不同数据库类型的数据持久化。一些最流行的 Spring 数据项目包括:
-
Spring Data JPA - 针对关系数据库的持久化
-
Spring Data Mongo - 针对 Mongo 文档数据库的持久化
-
Spring Data Neo4j - 针对 Neo4j 图形数据库的持久化
-
Spring Data Redis - 针对 Redis 键值存储的持久化
-
Spring Data Cassandra - 针对 Cassandra 数据库的持久化
第四章 Spring安全
本章内容:
自动配置 Spring Security
自定义用户存储
自定义登录页面
防御 CSRF 攻击
了解你的用户
4.1 启用 Spring Security
保护 Spring 应用程序的第一步是将 Spring Boot security starter 依赖项添加到构建中。在项目的 pom.xml 文件中,添加以下 < dependency> 内容:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
如果想尝试一下,启动应用程序并访问主页(或任何页面)。将提示使用 HTTP 基本身份验证对话框进行身份验证。要想通过认证,需要提供用户名和密码。用户名是 user。至于密码,它是随机生成并写入了应用程序日志文件。日志条目应该是这样的:
Using default security password: 087cfc6a-027d-44bc-95d7-cbb3a798a1ea
假设正确地输入了用户名和密码,将被授予对应用程序的访问权。
4.2 配置 Spring Security
多年来,有几种配置 Spring Security 的方法,包括冗长的基于 xml 的配置。幸运的是,Spring Security 的几个最新版本都支持基于 Java 的配置,这种配置更容易读写。
在本章结束之前,已经在基于 Java 的 Spring Security 配置中配置了所有 Taco Cloud 安全需求。但是在开始之前,可以通过编写下面清单中所示的基本配置类来简化它。程序清单 4.1 一个基本的 Spring Security 配置类
package tacos.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
4.2.1 内存用户存储
清单显示了如何在内存用户存储中配置两个用户 “buzz” 和 “woody”。程序清单 4.2 在内存用户存储中定义用户
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("buzz")
.password("infinity")
.authorities("ROLE_USER")
.and()
.withUser("woody")
.password("bullseye")
.authorities("ROLE_USER");
}
正如你所看到的,AuthenticationManagerBuilder 使用构造器风格的 API 来配置身份验证细节。在这种情况下,对 inMemoryAuthentication() 方法的调用,可以直接在安全配置本身中指定用户信息。
对 withUser() 的每个调用都会启动用户的配置。给 withUser() 的值是用户名,而密码和授予的权限是用 password() 和 authority() 方法指定的。如程序清单 4.2 所示,两个用户都被授予 ROLE_USER 权限。用户 “buzz” 的密码被配置为 “infinity”。同样,“woody” 的密码是 “bullseye”。
内存中的用户存储应用于测试或非常简单的应用程序时非常方便,但是它不允许对用户进行简单的编辑。如果需要添加、删除或更改用户,则必须进行必要的更改,然后重新构建、部署应用程序。
4.5 小结
Spring Security 自动配置是一种很好的开始学习安全的方式,但大多数应用程序需要明确地配置安全,以满足其独特的安全需求。
用户细节可以在关系数据库、LDAP 或完全自定义实现支持的用户存储中进行管理。
Spring Security 自动防御 CSRF 攻击。
通过 SecurityContext 对象(从 SecurityContextHolder. getcontext() 中返回)或使用 @AuthenticationPrincipal 注入控制器中,可以获得认证用户的信息。
第五章 使用配置属性
5.1 微调自动配置
在我们深入研究配置属性之前,有必要确定在 Spring 中有两种不同(但相关)的配置
Bean wiring —— 它声明应用程序组件将在 Spring 应用程序上下文中作为 bean 创建,以及它们应该如何相互注入。
Property injection —— 在 Spring 应用程序上下文中设置 bean 的值。
5.1.1 理解 Spring 环境抽象
Spring 环境抽象是任何可配置属性的一站式商店。它抽象了属性的起源,以便需要这些属性的 bean 可以从 Spring 本身使用它们。Spring 环境来自几个属性源,包括:
-
JVM 系统属性
-
操作系统环境变量
-
命令行参数
-
应用程序属性配置文件
然后,它将这些属性聚合到单一的源中,从这个源中可以注入 Spring bean。图 5.1 演示了来自属性源的属性是如何通过 Spring 环境抽象流到 Spring bean 中的。
通过 Spring Boot 自动配置的 bean 都可以通过从 Spring 环境中提取的属性进行配置。作为一个简单的例子,假设希望应用程序的底层 servlet 容器侦听某些端口上的请求,而不是默认端口 8080。为此,通过在 src/main/resources/application.properties 文件中的 server.port 属性来指定一个不同的接口,如下所示:
server.port=9090
就我个人而言,我更喜欢在设置配置属性时使用 YAML。因此,我可能设置在 /src/main/resources/application.yml 文件中的 server.port 的值,而不是使用 application.properties 文件,如下所示:
server:
port: 9090
5.1.2 配置数据源
如果打算开始使用 MySQL 数据库,可以将以下配置属性添加到 application.yml:
spring:
datasource:
url: jdbc:mysql://localhost/tacocloud
username: tacodb
password: tacopassword
虽然需要将适当的 JDBC 驱动程序添加到构建中,但通常不需要指定 JDBC 驱动程序类;Spring Boot 可以从数据库 URL 的结构中找到它。但如果有问题,可以试着设置 spring.datasource.schema 和 spring.datasource.data 属性:
spring:
datasource:
schema:
- order-schema.sql
- ingredient-schema.sql
- tao-schema.sql
- user-schema.sql
data:
- ingredients.sql
可能显式数据源配置不是你的风格。相反,你可能更喜欢在 JNDI 中配置数据源,并让 Spring 从那里查找它。在这种情况下,通过配置 spring.datasource.jndi-name 来设置数据源:
spring:
datasource:
jndi-name: java:/comp/env/jdbc/tacoCloudDS
如果设置了 spring.datasource.jndi-name 属性,那么其他数据源的连接属性(如果设置了)会被忽略。
5.1.3 配置嵌入式服务器
5.1.4 配置日志
默认情况下,Spring Boot 通过 Logback 配置日志,默认为 INFO 级别,然后写入控制台。在运行应用程序和其他示例时,可能已经在应用程序日志中看到了大量的 INFO 级别的日志条目。
要完全控制日志配置,可以在类路径的根目录(在 src/main/resources 中)创建 log .xml 文件。下面是一个简单的 log .xml 文件的例子:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<logger name="root" level="INFO"/>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
除了用于日志的模式外,Logback 配置或多或少与没有 log .xml 文件时得到的默认配置相同。但是通过编辑 logback.xml,可以完全控制应用程序的日志文件。
要设置日志记录级别,需要创建以 logging.level 为前缀的属性,后面接上要为其设置日志级别的日志记录器的名称。例如,假设想将 root 日志级别设置为 WARN,但是将 Spring 安全日志设置为 DEBUG 级别。可以像下面这样设置:
logging:
level:
root: WARN
org:
springframework:
security: DEBUG
另外,可以将 Spring Security 包的名称折叠成一行,以便于阅读:
logging:
level:
root: WARN
org:
springframework.security: DEBUG
现在,假设希望将日志条目写入位于 /var/logs/ 文件夹下的 TacoCloud.log 文件。loggin.path 和 logging.file 属性可以帮助实现这一点:
logging:
path: /var/logs/
file: TacoCloud.log
level:
root: WARN
org:
springframework:
security: DEBUG
假设应用程序对 /var/logs/ 文件夹有写权限,那么日志将被写到 /var/logs/TacoCloud.log 文件中。默认情况下,日志文件在大小达到 10 MB 时就会进行循环写入。
5.2 创建自己的配置属性
…
5.3 使用 profile 文件进行配置
…
第二部分 集成Spring
第 6 章 创建 REST 服务
本章内容:
在 Spring MVC 中定义 REST 端点
启用超链接 REST 资源
自动生成基于存储库的 REST 端点
6.1.1 从服务器获取数据
@RequestMapping 注解还设置了一个 produces 属性。这指定了 DesignTacoController 中的任何处理程序方法只在请求的 Accept 头包含 “application/json” 时才处理请求。
例如,为了允许 XML 输出,可以向 produces 属性添加 “text/html”:
@RequestMapping(path="/design", produces={"application/json", "text/xml"})
@GetMapping("/{id}")
public Taco tacoById(@PathVariable("id") Long id) {
Optional<Taco> optTaco = tacoRepo.findById(id);
if (optTaco.isPresent()) {
return optTaco.get();
}
return null;
}
其中路径的 {id} 部分是占位符。请求中的实际值指定给 id 参数,该参数通过 @PathVariable 映射到 {id}占位符。
第 7 章 调用 REST 服务
Spring 应用程序可以通过以下方式使用 REST API:
- RestTemplate —— 一个由 Spring 核心框架提供的简单、同步 REST 客户端。
- Traverson ——可感知超链接的同步 REST 客户端,由 Spring HATEOAS 提供,灵感来自同名的 JavaScript 库。
- WebClient —— 一个在 Spring 5 中引入的响应式、异步 REST 客户端。
7.1 使用 RestTemplate 调用 REST 端点
表 7.1 RestTemplate 定义的 12 个唯一操作
方法 描述
-
delete(…) 对指定 URL 上的资源执行 HTTP DELETE请求
-
exchange(…) 对 URL 执行指定的
HTTP 方法,返回一个 ResponseEntity,其中包含从响应体映射的对象 -
execute(…) 对 URL 执行指定的 HTTP 方法,返回一个映射到响应体的对象
-
getForEntity(…) 发送 HTTP GET 请求,返回一个ResponseEntity,其中包含从响应体映射的对象
-
getForObject(…) 发送 HTTP GET请求,返回一个映射到响应体的对象 headForHeaders(…) 发送
HTTP HEAD 请求,返回指定资源 URL 的HTTP 请求头 -
optionsForAllow(…) 发送 HTTP OPTIONS 请求,返回指定 URL 的 Allow 头信息
-
patchForObject(…) 发送 HTTP PATCH 请求,返回从响应主体映射的结果对象
-
postForEntity(…) 将数据 POST 到一个 URL,返回一个ResponseEntity,其中包含从响应体映射而来的对象
-
postForLocation(…) 将数据 POST 到一个URL,返回新创建资源的 URL
-
postForObject(…) 将数据 POST 到一个 URL,返回从响应主体映射的对象
-
put(…) 将资源数据 PUT 到指定的URL
一旦了解了 RestTemplate 提供的 12 个操作以及每种变体的工作方式,就可以很好地编写调用资源的 REST 客户端了。
要使用 RestTemplate,需要创建一个实例:
RestTemplate rest = new RestTemplate();
或是将它声明为一个 bean,在需要它的时候将其注入:
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
让我们通过查看支持四种主要 HTTP 方法(GET、PUT、DELETE 和 POST)的操作来探寻 RestTemplate 的操作。我们将从 getForObject() 和 getForEntity() —— GET 方法开始。
7.1.1 请求 GET 资源
第 8 章 发送异步消息
本章内容:
异步消息
使用 JMS、RabbitMQ 和 Kafka 发送消息
从 Broker 拉取消息
监听消息
8.1 使用 JMS 发送消息
JMS 是一个 Java 标准,它定义了一个用于使用消息代理的公共 API。自 2001 年首次引入以来,JMS 一直是 Java 中异步消息传递的首选方法。在 JMS 之前,每个消息代理都有一个专用 API,这使得应用程序的消息代码在代理之间的可移植性更差。但是有了 JMS,所有兼容的实现都可以通过公共接口进行处理,这与 JDBC 为关系数据库操作提供公共接口的方式非常相似。
Spring 通过称为 JmsTemplate 的基于模板的抽象来支持 JMS。使用 JmsTemplate,很容易从生产者端跨队列和主题发送消息,并在消费者端接收这些消息。Spring 还支持消息驱动 POJO 的概念:简单的 Java 对象以异步方式对队列或主题上到达的消息做出响应。
我们将探讨 Spring 的 JMS 支持,包括 JmsTemplate 和消息驱动 POJO。但是在可以发送和接收消息之前,需要一个消息代理,它可以在生产者和消费者之间传递这些消息。让我们通过在 Spring 中设置消息代理来开始对 Spring JMS 的探索。
8.1.1 设置 JMS
在使用 JMS 之前,必须将 JMS 客户端添加到项目的构建中。使用 Spring Boot,这个过程简单的不能再简单了,需要做的仅仅是将 starter 依赖添加到构建中。但是,首先必须决定是使用 Apache ActiveMQ,还是使用较新的 Apache ActiveMQ Artemis Broker。
如果使用 ActiveMQ,需要添加以下依赖到项目的 pom.xml 文件中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
如果选择 ActiveMQ Artemis,starter 如下所示:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-artemis</artifactId>
</dependency>
Artemis 是 ActiveMQ 的下一代重新实现,实际上这让 ActiveMQ 成为一个遗留选项。因此,对于 Taco Cloud,将选择 Artemis。但是,这种选择最终对如何编写发送和接收消息的代码几乎没有影响。唯一显著的区别在于如何配置 Spring 来创建与 Broker 的连接。
默认情况下,Spring 假设 Artemis Broker 正在监听 localhost 的 61616 端口。对于开发目的,这是可以的,但是一旦准备好将应用程序发送到生产环境中,就需要设置一些属性来告诉 Spring 如何访问代理。表 8.1 列出了最有用的属性。
属性 描述
- spring.artemis.host broker 主机
- spring.artemis.port broker 端口
- spring.artemis.user 用于访问 broker 的用户(可选)
- spring.artemis.password 用于访问 broker 的密码(可选)
例如,考虑应用程序中的以下条目。可能用于非开发设置的 yml 文件:
spring:
artemis:
host: artemis.tacocloud.com
port: 61617
user: tacoweb
password: 13tm31n
这将设置 Spring,以创建到监听 artemis.tacocloud.com(端口 61617)的 Artemis Broker 的 broker 连接。它还设置将与该 broker 交互的应用程序的凭据,凭据是可选的,但建议用于生产部署。
如果要使用 ActiveMQ 而不是 Artemis,则需要使用表 8.2 中列出的 ActiveMQ 特定的属性。
属性 描述
- spring.activemq.broker-url Broker 的 URL
- spring.activemq.user 用于访问Broker 的用户(可选)
- spring.activemq.password 用于访问 Broker 的密码(可选)
- spring.activemq.in-memory 是否启动内存 Broker(默认:true)
请注意,不是为 Broker 的主机名和端口提供单独的属性,而是使用单个属性 spring.activemq.broker-url 指定 ActiveMQ Broker 的地址。URL 应该是 tcp:// URL,如下面的 YAML 片段所示:
spring:
activemq:
broker-url: tcp://activemq.tacocloud.com
user: tacoweb
password: 13tm31n
无论选择 Artemis 还是ActiveMQ,当 Broker 在本地运行时,都不需要为开发环境配置这些属性。
但是,如果使用 ActiveMQ,则需要设置 spring.activemq.in-memory 属性为 false,以防止 Spring 启动内存中的 Broker。内存中的 Broker可能看起来很有用,但它只在发布和消费同一个应用的消息时有用(这一点用处有限)。
在继续之前,将希望安装并启动一个 Artemis(或 ActiveMQ)Broker,而不是使用嵌入式 Broker。与其在这里重复安装说明,我建议你参考 Broker 文档了解详细信息:
-
Artemis ——
https://activemq.apache.org/artemis/docs/latest/using-server.html -
ActiveMQ ——
http://activemq.apache.org/getting-started.html#GettingStarted-PreInstallationRequirements
有了构建中的 JMS starter 和等待将消息从一个应用程序传递到另一个应用程序的 Broker,就可以开始发送消息了。
8.2 使用 RabbitMQ 和 AMQP
RabbitMQ 可以说是 AMQP 最优秀的实现,它提供了比 JMS 更高级的消息路由策略。JMS 消息使用接收方将从中检索它们的目的地的名称来寻址,而 AMQP 消息使用交换器的名称和路由键来寻址,它们与接收方正在监听的队列解耦。交换器和队列之间的这种关系如图 8.1 所示。
当消息到达 RabbitMQ broker 时,它将转到它所寻址的交换器。交换器负责将其路由到一个或多个队列,具体取决于交换器的类型、交换器与队列之间的绑定以及消息的路由键的值。
有几种不同的交换方式,包括以下几种:
- Default —— 一种特殊的交换器,通过 broker
自动创建。它将消息路由到与消息的路由键的值同名的队列中。所有的队列将会自动地与交换器绑定。 - Direct —— 路由消息到消息路由键的值与绑定值相同的队列。
- Topic —— 将消息路由到一个或多个队列,其中绑定键(可能包含通配符)与消息的路由键匹配。
- Fanout —— 将消息路由到所有绑定队列,而不考虑绑定键或路由键。
- Headers —— 与 topic 交换器类似,只是路由基于消息头值而不是路由键。
- Dead letter —— 对无法交付的消息(意味着它们不匹配任何已定义的交换器与队列的绑定)的全部捕获。
最简单的交换形式是 Default 和 Fanout,因为它们大致对应于 JMS 队列和主题。但是其他交换允许定义更灵活的路由方案。
需要理解的最重要的一点是,消息是用路由键发送到交换器的,它们是从队列中使用的。它们如何从一个交换到一个队列取决于绑定定义以及什么最适合相应的情况。
使用哪种交换类型以及如何定义从交换到队列的绑定与 Spring 应用程序中消息的发送和接收方式关系不大。因此,我们将重点讨论如何编写使用 RabbitMQ 发送和接收消息的代码。
8.4 小结
异步消息传递在通信的应用程序之间提供了一个间接层,允许更松散的耦合和更大的可伸缩性。
Spring 支持 JMS、RabbitMQ 或 Apache Kafka 的异步消息传递。
应用程序可以使用基于模板的客户端(JmsTemplate、RabbitTemplate 或 KafkaTemplate)通过消息 broker 发送消息。
接收应用程序可以使用相同的基于模板的客户端,在基于 pull 的模型中使用消息。
也可以通过向 bean 方法应用消息监听器注解(@JmsListener、@RabbitListener 或 @KafkaListener)将消息推送给消费者。
第 9 章 集成 Spring
本章内容:
实时数据处理
定义集成流程
使用 Spring Integration 的 Java DSL 定义
集成电子邮件、文件系统和其他外部系统
第三部分 响应式Spring