读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Kotlin基础知识和Spring Data Redis
Spring Boot 允许开发人员创建不同风格的应用程序。在第 2 章中,从 Spring World 开始——CMS 应用程序和第 3 章, 使用 Spring Data 和 Reactive Fashion 的持久性,我们已经创建了一个门户应用程序,现在我们将创建一个基于消息驱动架构的应用程序。它演示了 Spring 框架如何很好地适应各种应用程序架构。
在本章中,我们将开始创建一个将跟踪的主题标签保存在 Redis 数据库中的应用程序。该应用程序将获取主题标签并将它们放入我们其他项目的几个队列中,并适当地使用和处理它们。
正如我们在之前的项目中所做的那样,我们将继续使用 Reactive Foundation 在应用程序中提供可扩展的特性。
在本章结束时,我们将拥有:
- Learned Kotlin basics
- Created the project structure
- Created the Reactive Redis repositories
- Applied some techniques in reactive programming, using the Reactive Redis Client
让我们现在开始吧。
Kotlin 语言于 2016 年 2 月正式发布。JetBrains 创建了它并从那时起一直在开发该语言。该公司是 IntelliJ IDEA IDE 的所有者。
2012 年 2 月,JetBrains 在 Apache v2 许可下将该语言开源;该许可证允许开发人员创建应用程序。
该语言是 JVM(Java 虚拟机)的一种选择 Clojure 和 Scala 等语言,这意味着该语言可以为 JVM 编译字节码。正如我们将看到的,Kotlin 与 Scala 有许多相似之处。 Kotlin 有 Scala 语言作为参考,但 JetBrains 团队认为 Scala 存在编译时间问题。
Kotlin 正在成为 Android 世界采用的语言,因此,在 2017 年的 Google I/O 上,Google 团队宣布正式支持 Android 生态系统。从那时起,该语言逐年增长并越来越受欢迎。
Kotlin 语言旨在维护与 Java 代码的互操作性。这意味着我们可以开始在 Kotlin 文件中使用 Java 惯用语进行编码。
该语言是静态类型的,它是一个极好的属性,因为它可以帮助我们在编译时发现一些问题。此外,静态类型语言比动态语言快得多。 IDE 也可以比动态语言更好地帮助开发人员。
语法与 Java 语法不同。 first 乍一看,这可能是个问题,但在使用 Kotlin 几个小时后,这根本不是问题。
有两个有趣的保留字可以理解用法和概念:
var
: This is a variable declaration. It indicates the variable is mutable and can be reassigned, as developers need.
val
: This is a variable declaration which indicates the variable is immutable and cannot be reassigned anymore. This definition is like a final declaration in the Java language.
变量声明有一个名称,在所需的数据类型之后,中间需要冒号作为分隔符。如果变量已初始化,则类型不是必需的,因为编译器可以推断出正确的数据类型。让我们尝试一下以更好地理解它。
这是一个指定数据类型的变量:
在这种情况下,我们需要保留数据类型,因为 variable 没有初始化,那么编译器无法推断类型。由于修饰符var
,可以 重新分配变量bookName
。
这是一个没有数据类型的变量:
不必声明数据类型,因为我们已经用值初始化了变量, Spring 5.0 by Example
。编译器可以推断出类型是一种 语法糖。由于修饰符,变量不能被重新赋值值
。如果我们尝试重新分配指令,我们会得到一个编译错误。
分号在 Kotlin 中是可选的,编译器可以检测到语句终止符。这是 Kotlin 与 Java 编程语言不同的另一点:
没有提供分号,并且编译了说明。
在 Kotlin 中,有 classes 和函数。然而,已经没有办法了。 fun
关键字应该用于声明一个函数。
Kotlin 获得了 Scala 语言的一些概念,并带来了一些特殊的类,例如 Data 类和 Object 类(我们将很快学习这些类) )。在此之前,我们将了解如何在 Kotlin 中声明一个函数。让我们这样做!
函数声明中有许多变体。我们将创建 一些 声明来了解与 Java 方法的细微差别。
这个简单的函数有两个 parameters 和一个 String 作为返回类型。查看参数声明并观察顺序、名称和数据类型。
正如我们所见,变量名之后的参数类型与变量声明中的 相同。返回类型位于 arguments 列表之后,用分号分隔。在 Java 中可以通过以下方式声明相同的函数:
这里有一些区别。首先,Java代码中有分号,我们可以看到方法和函数声明的顺序。
让我们了解一下如何construct 函数没有返回值,以下函数不会返回任何值:
有一个区别,在这种情况下,Unit
被引入了 这种类型的对象对应于Java语言中的void
。然后,在前面的代码中,我们有一个没有返回的函数。 Unit
对象可以被移除,如果你想让编译器理解函数没有返回值。
当我们想要在系统层之间保存和传输 数据时,数据类是正确的解决方案。与 Scala 一样,这些类提供了一些内置功能,例如 getters/setters
、equals
和 hashCode
、toString
方法和 copy
函数。
让我们为此创建一个示例:
我们在代码中有一些有趣的东西。我们注意到的第一件事是所有属性都是不可变的。这意味着所有这些都没有二传手。第二个是在类声明中,我们可以看到一个属性列表。在这种情况下,Kotlin 将创建一个包含该类中存在的所有属性的构造函数,因为它们是 val
,所以它意味着最终属性。
在这种情况下,不再有默认构造函数。
Kotlin 中另一个有趣的特性是它使开发人员能够在构造函数上使用默认值,在我们的例子中,new
属性,如果省略,将假定 假
值。我们也可以在函数的参数列表中获得相同的行为。
最后,还有一种复制对象的绝妙方法。 copy
方法允许开发人员复制带有命名参数的对象。这意味着我们只能根据需要更改属性。让我们看一个例子:
在第一个对象中,我们为 new
属性创建了一个带有 false
new
属性的书籍实例,然后我们复制了一个新的true
为 new
属性的对象,其他属性不变。告别复杂的克隆逻辑,迎接新的对象复制方式。
此代码的输出应如下所示:
正如我们所看到的,只有 new
属性发生了变化,并且 toString
函数也以良好的形式生成。
Data 类有一些限制。它们不能是抽象的、开放的、密封的或内在的。
Koltin 习语是 Java 程序员的一种语法糖。它是一组代码,可帮助开发人员用 Kotlin 语言创建简洁的代码。我们来看看常见的 Kotlin 习语。
Kotlin 支持字符串插值,用 Java 语言做有点复杂,但对 Kotlin 来说不是问题。我们不需要很多代码来完成这项任务,因为 Kotlin 本身就支持它。它使代码更易于阅读和理解。让我们创建一个示例:
正如我们所见, 在 Kotlin 中插入字符串是小菜一碟。再见 String.format()
有很多参数。我们可以使用 $bookName
来替换 bookName
变量值。此外,我们可以访问对象中存在的函数,但为此,我们需要放置花括号。检查以下代码:
谢谢,Kotlin 我们很欣赏这个功能。
Kotlin 支持 功能 称为 Smart 使开发人员能够自动使用强制转换运算符的强制转换。检查变量类型后,在 Java 中,强制转换运算符必须是显式的。让我们来看看:
正如我们所见,演员表运算符不再存在。检查类型后,Kotlin 可以推断出预期的 类型。让我们检查同一段代码的 Java 版本:
它使强制转换更安全,因为我们不需要检查和应用强制转换运算符。
范围表达式允许 developers 在 for
循环和 中使用范围class="literal">if
比较。在 Kotlin 中有很多方法可以使用范围。我们将在这里查看大多数common。
我们也可以在 for
循环中使用 until
关键字,在这种情况下,结束 element< id="id326128830" class="indexterm"> 将被排除在交互之外。让我们看一个例子:
在这种情况下,5
值将不会打印在控制台上,因为 end 元素不包含在交互中。
Kotlin 有惊人的东西可以处理空引用。空引用是 Java 开发人员的噩梦。 Java 8 有一个 Optional
对象,它可以帮助开发人员处理可空对象,但不像 Kotlin 那样简洁。
现在,我们将探索 Kotlin 如何帮助开发人员避免 NullPointerException
.让我们了解一下。
Kotlin 类型系统区分了可以保存 null 的引用和不能保存 null 的引用。因此,代码更加简洁易读,因为它是对开发人员的一种建议。
当引用不允许为 null 时,声明应为:
前面的变量不能分配给空引用,如果这样做,我们会得到一个编译错误。看看代码是多么容易理解。
有时,我们需要允许变量有空引用,在这种情况下,我们可以使用 ?
作为运算符,如下所示:
简单的。注意 ?
运算符上的变量声明,它使变量接受空引用。
有两种不同的方法可以避免 Kotlin 中的 NullPointerReference
。第一个可以被称为安全调用,另一个可以称为Elvis Operator。让我们来看看那些。
现在,我们对如何在 Kotlin 语言中使用 programming 有了一个很好的想法。在本节中,我们将为我们的新项目创建基本结构,其中主要功能是使用 Twitter 流。让我们这样做。
在我们开始编码之前,我们需要跟踪应用程序的需求。该应用程序是消息驱动的,我们将使用代理来提供消息传递基础设施。我们选择 RabbitMQ 代理是因为它提供了可靠性、高可用性和集群选项。此外,RabbitMQ 是现代消息驱动应用程序的流行选择。
该软件由 Pivotal 公司提供支持,该公司也是维护 Spring Framework 的 company。有一个庞大的社区支持该项目。
我们将有三个项目。这三个项目将收集 Twitter 流并将其发送给接收者,以便以格式化的方式向最终用户显示 Tweets。
第一个将在本章中创建,负责将跟踪的主题标签保存在 Redis 缓存中。
注册新的主题标签后,它将向第二个项目发送一条消息,该项目将开始使用 Twitter 流并将其重定向到所需的队列。该队列将被另一个项目使用,该项目将格式化推文,最后将它们显示给最终用户。
我们将拥有三个微服务。让我们创造这些东西。
我们已经学习了如何使用 Spring Initializr 页面。我们将进入页面,然后选择以下模块:
响应式网络
反应式 Redis
页面内容应如下所示:
我们可以选择组和神器。使用不同的名称没有问题。然后,我们可以点击Generate Project
等到下载结束。
我们需要为 KotlinJackson > Maven 项目的依赖项。事实上,我们的 pom.xml
上需要一个 Kotlin 标准库。另外,我们需要放入 jackson-module-kotlin
,它允许我们在 Kotlin 上使用 JSON,在这些部分与 Java 有一些不同。
这部分非常简单,我们将在 pom.xml
的依赖项部分添加以下依赖项。依赖关系如下:
现在,我们已经配置了依赖项,我们可以设置插件来编译 Kotlin 源代码。在下一节中,我们将这样做。
该项目是使用 Kotlin configured 成功创建的。现在,我们来看看 pom.xml
中的 Maven 插件。该配置对于指导 Maven 如何编译 Kotlin 源代码和添加工件是必要的。
我们将在插件部分添加以下插件:
还有一件事要做。看看 Maven 如何为我们的 Kotlin 代码配置路径。这很容易。请看以下内容:
我们在源路径中添加了 Kotlin 文件夹。
太棒了,项目结构已经准备好了,我们可以开始编码了!
为了为我们的应用程序创建隔离,我们将创建一个 custom Docker 网络。该网络是使用网桥驱动程序创建的。让我们使用以下命令来做到这一点:
好的,现在我们可以通过输入以下命令来检查网络列表:
Twitter 网络应该在列表中,如下所示:
最后一个是我们的 Twitter 网络。让我们从 Docker Hub 中拉取 Redis 映像。请看下一节。
我们需要做的第一件事是从 Docker下载 Redis 映像"id326223510" class="indexterm"> 集线器。为此,需要执行以下命令:
我们使用了 Redis 的 alpine 版本,因为它比其他版本更小,并且具有合理的安全性。下载图像时,我们可以看到下载状态进度。
我们可以使用以下命令检查结果:
结果应如下所示:
看看下载的图像。 Redis 必须在列表中。
太棒了,现在我们将启动 Redis 实例。
image 已下载,然后我们将为我们的应用程序启动 Redis 实例。命令可以是:
我们在这里有有趣的属性。我们用 redis
命名了我们的 Redis 实例, 这对于在接下来的章节中在容器中运行我们的应用程序很有用。此外,我们将 Redis 容器端口暴露给主机,使用的命令参数是 -p
。最后,我们将容器附加到我们的 Twitter 网络。
好的,Redis 实例可以使用了。让我们看看 Spring Data Reactive Redis 的东西。
有一个很好的工具可以 connect 与 Redis 实例,称为 redis-cli< /代码>。有一些 Docker 镜像,但我们将把它安装在我们的 Linux 机器上。
要安装它,我们可以执行以下命令:
太好了,现在我们可以与我们的 Redis 容器连接和交互了。该工具可以执行读写指令,那么我们需要小心避免无意中的指令。
让我们连接起来。默认配置对我们来说已经足够了,因为我们已经在 run
指令上导出了端口 6379
。在终端中键入以下命令:
然后我们将连接我们正在运行的实例。命令行应显示 Redis 主机和端口,如下图所示:
太好了,客户端已配置和测试。
现在,我们将在我们的容器上执行一些 Redis 命令。
Redis 是一个开放的source 内存数据结构。 Redis 非常适合数据库缓存并且不常见,但它可以用作使用发布-订阅功能的消息代理,它可以用于解耦应用程序。
Redis 支持一些有趣的功能,例如事务、原子操作和对生存时间密钥的支持。 Time-to-live 对于给 key 一个时间很有用,驱逐策略总是很难实现,Redis 为我们提供了一个内置的解决方案。
Redis有很多支持的数据类型。最常见的 是字符串、散列、列表和排序集。我们将稍微了解其中的每一个,因为帮助我们为用例选择正确的数据类型很重要。
字符串是 Redis 更基本的数据类型。 string 值的最大长度为 512 MB。我们可以将它作为 JSON 存储在键的值中,或者也可以作为图像存储,因为 Redis 是二进制安全的。
SET
: It sets the key and holds the value. It is a simple and basic command of Redis. Here's an example:
命令的返回应该是 OK
。表示指令已成功执行。
GET
: This command gets the value of the requested key. RememberGET
can only be used with a string data type:
如我们所见,该命令的返回应该是 joe
。
INCR
: TheINCR
command increments the key by one. It can be useful to handle sequential numbers atomically in distributed systems. The number increment will be returned as a command output:
正如我们所见, INCR
命令返回 1
作为命令输出,然后我们可以检查一下使用 GET
获取值。
users
key 的值减一,然后转换为 0
。
INCRBY
: It will increment the value of the key by the argument. The new incremented value will be returned:
新值作为命令输出返回。
Lists 是简单的字符串列表。它们由 insertionordered 顺序。 Redis 还提供了在列表的头部或尾部添加新元素的说明。
列表可用于存储事物组、类别组,例如,按 categories
键分组。
LPUSH
:在键的头部插入新的 element。该命令还支持多个参数,在这种情况下,值将按照我们传递参数的相反顺序存储。
以下是一些命令示例:
看看 LRANGE
输出,我们可以看到 movie
的值是第一个一个在列表中,因为 LPUSH
在头部插入了新元素。
RPUSH
:在键的尾部插入新的元素。该命令也支持多个参数,在这种情况下,值将遵循各自的顺序。
以下是一些命令示例:
正如我们所见,在 LRANGE
输出中,新值被插入到值的尾部。这是 RPUSH
命令的行为。
LSET
: 它将元素设置在请求的索引上。
以下是一些命令示例:
零索引的新值是 series
。 LSET
命令为我们做到了这一点。
LRANGE
:返回key的指定元素。命令参数是键、开始索引,最后是停止元素。 -1
在 stop 参数上将返回整个列表:
正如我们所看到的,第一个命令将返回三个元素,因为零索引将被分组。
set 是字符串的集合。它们有一个不允许重复值的属性。 这意味着如果我们在集合上添加预先存在的值,它将导致相同的元素,在这种情况下,优点是不需要验证 < span>元素 存在于片场。 另一个重要的特征 是集合是无序的。此行为与 Redis 列表不同。它在不同的用例中很有用,例如计算唯一身份访问者、跟踪唯一 IP 等等。
SADD
: It adds the element in a requested key. Also, the return of this command is the number of the element added to the set:
如我们所见,该命令返回一个,因为我们每次添加一个用户。
SMEMBERS
: It returns all the members of a requested key:
该命令将返回 joe
和 mary
因为这些是存储在 唯一访问者
key。
SCARD
: It returns the numbers of elements of a requested key:
该命令将返回存储在请求键中的元素数量,在这种情况下, 输出 将是 2
。
Spring Data Redis 提供了一种简单的 方式 从 Spring Boot 应用程序与 Redis 服务器交互。该项目是 Spring Data 系列的一部分,为开发人员提供高级和低级抽象。
支持 Jedis 和 Lettuce 连接器作为该项目的驱动程序。
该项目提供了许多与 Redis 交互的功能和设施。 Repository
接口也受支持。 Redis 有一个 CrudRepository
,就像在其他实现中一样,例如 Spring Data JPA。
该项目的中心类是 RedisTemplate
,它提供了高级 API 执行 Redis 操作和序列化支持。我们将使用这个类与 Redis 上的集合数据结构进行交互。
这个项目支持 Reactive 实现,这些对我们来说是重要的特征,因为我们正在寻找 Reactive 实现。
要配置 ReactiveRedisConnectionFactory
,我们可以使用application.yaml文件
,因为它更容易维护和集中我们的配置。
原理和其他 Spring Data 项目一样,我们应该在application.yaml
文件,如下:
在前面的配置文件中,我们将Redis配置指向了 localhost
, 我们可以看到。配置非常简单,也很容易理解。
完毕。连接工厂已配置。下一步是提供一个 RedisTemplate
来与我们的 Redis 实例交互。请看下一节。
Spring Data Redis 的主类是 ReactiveRedisTemplate
,那么我们需要为 Spring 容器配置并提供一个实例。
我们需要提供一个实例并为所需的 ReactiveRedisTemplate
配置正确的序列化程序。 Serializers
是 Spring Data Redis 用来从存储在 Redis 中的 Key
中的原始字节序列化和反序列化对象的方式值
字段。
我们将只使用 StringRedisSerializer
,因为我们的 Key
和 Value
很简单字符串和 Spring Data Redis 已经为我们准备好了这个序列化程序。
让我们生成我们的 ReactiveRedisTemplate
。 实现 应该如下所示:
惊人的。这是我们在 Spring Framework 中使用 Kotlin 的第一个代码。关键字 open
与 Java 的 final
关键字相反。这意味着这个函数可以从这个类继承。默认情况下,Kotlin 中的所有类都是 final 的。 Spring Framework 需要 @Bean
上的非 final 函数在 @Configuration
类上,然后我们需要插入 打开
。
我们收到了 ReactiveRedisConnectionFactory
作为参数。 Spring 知道我们使用 Redis 的配置在 application.yaml
文件中生成了什么。然后容器可以注入工厂。
最后,我们声明 ReactiveRedisTemplate<String, String>
作为我们函数的返回值。
有趣的工作,我们已经准备好使用我们的 Redis 模板了。现在,我们将实现我们的第一个 Redis 存储库。下一节见。
我们已经创建了 ReactiveRedisTemplate
,然后我们可以在我们的存储库实现中使用这个对象。我们将创建一个简单的存储库来interact 与 Redis,记住存储库应该是响应式的,这是我们应用程序的一个重要特征.然后我们需要返回 Mono
或 Flux
以使存储库 Reactive。让我们看看我们的存储库实现:
我们收到了 ReactiveRedisTemplate<String, String>
作为我们类的注入,Spring Framework 可以检测构造函数并注入正确的实现。
现在,我们需要这两个函数。第一个负责在 Redis 的集合结构上插入我们的实体TrackedHashTag
。我们在 Redis 上添加 hash-tags
key 的值。此函数返回一个 Mono
和 TrackedHashTag
值。注意 save
功能。我们为我们的价值创建了一个模式,该模式遵循 hashtag
、queue
,其中 hashtag 是收集推文和我们将在下一节中使用的队列发送到 RabbitMQ 队列。
第二个函数返回hash-tags
键的所有值,表示所有被跟踪来自我们系统的标签。此外,我们还需要执行一些逻辑来创建我们的模型, TrackedHashTag
。
存储库已经完成,现在我们可以创建我们的服务层来封装存储库。让我们在下一节中这样做。
对于这个项目,我们需要在 Twitter 平台上配置 一个应用程序。这是必要的,因为我们将使用 Twitter 的 API 来搜索推文,例如,Twitter 帐户是必需的。我们不会解释如何创建 Twitter 帐户。网上有很多关于这方面的文章。
Twitter账号创建后,我们需要去 https://apps.twitter.com/ 并创建一个新应用。该页面与 以下 屏幕截图非常相似:
我们将点击 Create New App
按钮开始创建过程。当我们单击该按钮时,将显示以下页面。我们需要填写必填字段并接受 Twitter 协议:
我们可以选择应用程序名称,填写描述和网站。这些细节由你决定。
好工作。我们的 Twitter 应用程序几乎可以使用了。
现在,我们只需要配置应用程序以供使用。
我们需要检查我们的密钥和访问令牌是否配置正确。让我们点击 Keys and Access Tokens
选项卡并检查值,如下所示:
如我们所见,前面的截图中有一些重要的配置。 Consumer Key
和 Consumer Secret
是通过 Twitter 进行身份验证所必需的蜜蜂。这里的另一个重点是 Access Level
;确保它被配置为 只读,如 在前面的截图中,我们不会在Twitter上做写操作。
让我们对它进行 Docker 化。
惊人的。我们拥有将跟踪的主题标签保存在 Redis 实例上的系统。该应用程序是完全反应式的,没有阻塞线程。
现在,我们将配置 Maven 插件以生成 Docker 映像。配置与我们在第 3 章中所做的非常相似, 持久化 Spring Data 和 Reactive Fashion。但是,现在我们将创建一个使用 Kotlin 语言运行的第一个容器。让我们这样做。
现在,我们将配置我们的 pom.xml
使其能够generate我们的 Docker 镜像。我们需要更改的第一件事是我们的最终名称工件,因为 Docker 映像不允许 - 字符,那么我们需要正确配置。
配置非常简单,将 <finalName>
标签放在 <build>
节点上。让我们这样做:
好的。我们已正确配置最终名称以正确生成 Docker 映像。现在,我们将配置 Maven Docker 插件以通过 Maven 目标生成 Docker 映像。
在构建节点内的插件部分,我们应该放入以下插件配置:
配置非常简单。我们以前这样做过。在配置部分,我们从镜像进行配置,在我们的例子中是 openjdk:latest
、Docker 入口点和公开的端口。
让我们在下一节中创建我们的 Docker 映像。
我们的项目之前配置了 Maven Docker 插件。我们可以使用 Maven Docker 插件使用 docker:build
目标生成 Docker 镜像。然后,是时候生成我们的Docker镜像了。
要生成 Docker 映像,请键入以下命令:
现在,我们必须等待 Maven 构建并检查 Docker 镜像是否成功生成。
检查 Docker 镜像,我们应该会看到生成的新镜像。为此,我们 可以使用docker images
命令:
对了,我们应该在图片列表中看到 springfivebyexample/tracked_hashtag:latest
,如下图所示:
太棒了,我们的 Docker 镜像已经准备好与我们的第一个 Kotlin 语言的 Spring Boot 应用程序一起运行了。让我们现在运行它。
我们的容器正在运行。现在,我们可以尝试调用 API 来检查行为。在这一部分中,我们将使用 curl
命令行。 curl
允许我们在 Linux 上通过命令行调用 API。此外,我们将使用 jq
使 JSON 在命令行上可读,如果您没有这些,请查看提示框以安装这些工具。
让我们调用我们的创建 API,记住创建我们可以在 API 的基本路径中使用 POST
方法。然后键入以下命令:
这里有一些有趣的事情。 -H
参数指示 curl
将其放入请求标头和 -d< /code> 表示请求体。此外,最后,我们有服务器地址。
我们创建了新的 tracked-hash-tag
。让我们检查我们的 GET
API 来获取这些数据:
太棒了,我们调用了 curl
工具并使用 jq
工具打印了 JSON 值。命令输出应类似于以下屏幕截图:
在本章中,我们介绍了 Kotlin 语言,这是 JVM 最突出的语言,因为它具有超快的编译器,例如,如果我们将其与 Scala 进行比较。它还带来了代码的简单性,并帮助开发人员创建更简洁易读的代码。
我们还使用 Kotlin 作为语言的基本概念在 Spring 框架中创建了我们的第一个应用程序,并且我们看到了 Kotlin 如何以一种实用的方式帮助开发人员。
我们已经介绍了 Redis 作为缓存和 Spring Data Reactive Redis,它支持 Redis 在响应式范式中。
在本章的最后一部分,我们学习了如何创建一个 Twitter 应用程序,这需要我们创建下一个应用程序,并开始使用响应式 Rest 客户端在响应式编程中使用 Twitter API。
让我们跳到下一章,了解更多关于 Spring Reactive 的知识。