vlambda博客
学习文章列表

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Kotlin基础知识和Spring Data Redis

Chapter 4. Kotlin Basics and 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

让我们现在开始吧。

Learning Kotlin basics


Kotlin 语言于 2016 年 2 月正式发布。JetBrains 创建了它并从那时起一直在开发该语言。该公司是 IntelliJ IDEA IDE 的所有者。 

2012 年 2 月,JetBrains 在 Apache v2 许可下将该语言开源;该许可证允许开发人员创建应用程序。

该语言是 JVMJava 虚拟机)的一种选择 Clojure 和 Scala 等语言,这意味着该语言可以为 JVM 编译字节码。正如我们将看到的,Kotlin 与 Scala 有许多相似之处。 Kotlin 有 Scala 语言作为参考,但 JetBrains  团队认为 Scala 存在编译时间问题。

Kotlin 正在成为 Android 世界采用的语言,因此,在 2017 年的 Google I/O 上,Google 团队宣布正式支持 Android 生态系统。从那时起,该语言逐年增长并越来越受欢迎。

Main characteristics of Kotlin

Kotlin 语言旨在维护与 Java 代码的互操作性。这意味着我们可以开始在 Kotlin 文件中使用 Java 惯用语进行编码。

该语言是静态类型的,它是一个极好的属性,因为它可以帮助我们在编译时发现一些问题。此外,静态类型语言比动态语言快得多。 IDE 也可以比动态语言更好地帮助开发人员。

Syntax

语法与 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.

变量声明有一个名称,在所需的数据类型之后,中间需要冒号作为分隔符。如果变量已初始化,则类型不是必需的,因为编译器可以推断出正确的数据类型。让我们尝试一下以更好地理解它。

这是一个指定数据类型的变量:

var bookName: String

在这种情况下,我们需要保留数据类型,因为 variable 没有初始化,那么编译器无法推断类型。由于修饰符var,可以 重新分配变量bookName

这是一个没有数据类型的变量:

val book = "Spring 5.0 by Example"

不必声明数据类型,因为我们已经用值初始化了变量, Spring 5.0 by Example。编译器可以推断出类型是一种 语法糖。由于修饰符,变量不能被重新赋值值。如果我们尝试重新分配指令,我们会得到一个编译错误。

分号在 Kotlin 中是可选的,编译器可以检测到语句终止符。这是 Kotlin 与 Java 编程语言不同的另一点:

val book = "Spring 5.0 by Example"
var bookName: String
println("Hello, world!")

没有提供分号,并且编译了说明。

Note

推荐使用 Kotlin 语言进行不可变编程。它在多核环境中表现更好。此外,它使开发人员的生活更容易调试和排除场景故障。

 

Semantics

在 Kotlin 中,有 classes 和函数。然而,已经没有办法了。 fun 关键字应该用于声明一个函数。

Kotlin 获得了 Scala 语言的一些概念,并带来了一些特殊的类,例如 Data 类和 Object 类(我们将很快学习这些类) )。在此之前,我们将了解如何在 Kotlin 中声明一个函数。让我们这样做!

Declaring functions in Kotlin

函数声明中有许多变体。我们将创建 一些 声明来了解与 Java 方法的细微差别。

Simple function with parameters and return type

这个简单的函数有两个 parameters 和一个 String 作为返回类型。查看参数声明并观察顺序、名称和数据类型。

fun greetings(name:String,greeting:String):String{
  return greeting + name
}

正如我们所见,变量名之后的参数类型与变量声明中的 相同。返回类型位于 arguments 列表之后,用分号分隔。在 Java 中可以通过以下方式声明相同的函数:

public String greetings(String name,String greeting){
  return greeting + name;
}

这里有一些区别。首先,Java代码中有分号,我们可以看到方法和函数声明的顺序。

Simple function without return

让我们了解一下如何construct 函数没有返回值,以下函数不会返回任何值:

fun printGreetings(name:String,greeting:String):Unit{
  println(greeting + name)
}

 

有一个区别,在这种情况下,Unit 被引入了 这种类型的对象对应于Java语言中的void。然后,在前面的代码中,我们有一个没有返回的函数。 Unit 对象可以被移除,如果你想让编译器理解函数没有返回值。

Single expressions functions

当函数只有一个表达式我们可以去掉花括号,和Scala中的 一样,函数体应该在 = 符号之后指定。让我们重构我们的第一个函数,如下所示:

fungreetings(name:String,greeting:String) = greeting + name

我们也可以删除 return 关键字。我们的函数现在非常简洁。我们删除了 return 以及返回的类型。正如我们所看到的,代码现在更具可读性。如果需要,也可以声明返回类型。

Overriding a function

要在 Kotlin 上覆盖 function,需要放置一个 override函数声明中的关键字,基函数也需要有 open 关键字。

让我们看一个例子:

open class Greetings {
open fun greeting() {}
}

class SuperGreeting() : Greetings() {
  override fun greeting() {
  // my super greeting
  }
}

这种方式比 Java 更明确,它也增加了代码的易读性。

Data classes

当我们想要在系统层之间保存和传输 数据时,数据类是正确的解决方案。与 Scala 一样,这些类提供了一些内置功能,例如 getters/settersequalshashCodetoString 方法和 copy 函数。

 

让我们为此创建一个示例:

data class Book(valauthor:String,valname:String,valdescription:String,valnew:Boolean = false)

我们在代码中有一些有趣的东西。我们注意到的第一件事是所有属性都是不可变的。这意味着所有这些都没有二传手。第二个是在类声明中,我们可以看到一个属性列表。在这种情况下,Kotlin 将创建一个包含该类中存在的所有属性的构造函数,因为它们是 val,所以它意味着最终属性。

在这种情况下,不再有默认构造函数。

Kotlin 中另一个有趣的特性是它使开发人员能够在构造函数上使用默认值,在我们的例子中,new 属性,如果省略,将假定 值。我们也可以在函数的参数列表中获得相同的行为。

最后,还有一种复制对象的绝妙方法。 copy 方法允许开发人员复制带有命名参数的对象。这意味着我们只能根据需要更改属性。让我们看一个例子:

funmain(args : Array<String>) {
  val springFiveOld = Book("Claudio E. de Oliveira","Spring 5.0 by Example","Amazing example of Spring Boot Apps",false)
val springFiveNew = springFiveOld.copy(new = true)
println(springFiveOld)
println(springFiveNew)
}

在第一个对象中,我们为 new 属性创建了一个带有 false new 属性的书籍实例,然后我们复制了一个新的truenew 属性的对象,其他属性不变。告别复杂的克隆逻辑,迎接新的对象复制方式。

此代码的输出应如下所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Kotlin基础知识和Spring Data Redis

正如我们所看到的,只有 new 属性发生了变化,并且 toString 函数也以良好的形式生成。

Data 类有一些限制。它们不能是抽象的、开放的、密封的或内在的。

 

Objects

单例模式在应用程序中很常用,Kotlin 提供了一种简单的方法,无需太多样板代码。

我们可以指示 Kotlin 使用 object 关键字创建一个单例对象。再次,  Kotlin 使用 Scala 作为参考,因为 Scala 语言中有相同的功能。

让我们尝试一下:

object BookNameFormatter{
  funformat(book: Book):String = "The book name is" + book.name
}

我们创建了一个格式化程序来返回带有书名的消息。然后,我们尝试使用这个函数:

val springFiveOld = Book("Claudio E. de Oliveira","Spring 5.0 by Example","Amazing example of Spring Boot Apps",false)
BookNameFormatter.format(springFiveOld)

函数格式可以在静态上下文中调用。没有实例可以调用该函数,因为它是一个单例对象。

Companion objects

companion object 是一个 object对于该类的所有实例都是通用的。例如,这意味着一本书有很多实例,但它们的伴生对象只有一个实例。通常,developers 使用伴随对象作为工厂方法。让我们创建我们的第一个 companion object

data class Book(valauthor:String,valname:String,val description:String,valnew:Boolean = false{

companion object {
    funcreate(name:String,description: String,author: String):Book{
      return Book(author,name,description)
    }
  }

}

 

如果 companion object 的名称被省略,则可以以单例方式调用该函数,而无需实例,如下所示:

val myBookWithFactory = Book.create("Claudio E. de Oliveira","Spring 5.0 by Example","Amazing example of Spring Boot Apps")

它就像一个 object 行为。我们可以在静态上下文中调用它。

Kotlin idioms

Koltin 习语是 Java 程序员的一种语法糖。它是一组代码,可帮助开发人员用 Kotlin 语言创建简洁的代码。我们来看看常见的 Kotlin 习语。

String interpolation

Kotlin 支持字符串插值,用 Java 语言做有点复杂,但对 Kotlin 来说不是问题。我们不需要很多代码来完成这项任务,因为 Kotlin 本身就支持它。它使代码更易于阅读和理解。让我们创建一个示例:

val bookName = "Spring 5.0"
val phrase = "The name of the book is$bookName"

正如我们所见, 在 Kotlin 中插入字符串是小菜一碟。再见 String.format() 有很多参数。我们可以使用 $bookName 来替换 bookName 变量值。此外,我们可以访问对象中存在的函数,但为此,我们需要放置花括号。检查以下代码:

val springFiveOld = Book("Claudio E. de Oliveira","Spring 5.0 by Example","Amazing example of Spring Boot Apps",false)
val phrase = "The name of the book is${springFiveOld.name}"

谢谢,Kotlin 我们很欣赏这个功能。

Smart Casts

Kotlin 支持 功能 称为 Smart 使开发人员能够自动使用强制转换运算符的强制转换。检查变量类型后,在 Java 中,强制转换运算符必须是显式的。让我们来看看:

funreturnValue(instance: Any): String {
if (instance is String) {
return instance
}
throw IllegalArgumentException("Instance is not String")
} 

正如我们所见,演员表运算符不再存在。检查类型后,Kotlin 可以推断出预期的 类型。让我们检查同一段代码的 Java 版本:

public String returnValue(Object instance) {
  if (instance instanceof String) {
String value = (String) instance;
return value;
}
    throw IllegalArgumentException("Instance is not String");
}

它使强制转换更安全,因为我们不需要检查和应用强制转换运算符。

Range expressions

范围表达式允许 developersfor 循环和 中使用范围class="literal">if 比较。在 Kotlin 中有很多方法可以使用范围。我们将在这里查看大多数common

Simple case

我们来看一个简单的案例:

for ( i in1..5){
  println(i)
}

它将从 1 迭代到 5 包含在内,因为我们已经在in< /代码> 关键字。

The until case

我们也可以在 for 循环中使用 until 关键字,在这种情况下,结束 element< id="id326128830" class="indexterm"> 将被排除在交互之外。让我们看一个例子:

for (i in1until5) {
  println(i)
}

 

在这种情况下,5 值将不会打印在控制台上,因为 end 元素不包含在交互中。

The downTo case

downTo 关键字使开发人员能够以相反的顺序interact。该指令也是不言自明的。让我们在实践中看看:

for (i in5downTo1) {
  println(i)
}

这也很容易。交互将以相反的顺序发生,在这种情况下,将包含值 1。正如我们所看到的,代码很容易理解。

Step case

有时我们需要interact 值,但使用任意步骤,而不是一一进行,例如。然后我们可以使用 step 指令。让我们练习:

for (i in1..6step2) {
  print(i)
}

在这里,我们将看到以下输出: 135,因为交互将从  1 值开始,并将为增加了两点。

惊人的。 Kotlin 范围可以为我们的源代码增加更多可读性,并有助于提高代码质量。

Null safety

Kotlin 有惊人的东西可以处理空引用。空引用是 Java 开发人员的噩梦。 Java 8 有一个 Optional 对象,它可以帮助开发人员处理可空对象,但不像 Kotlin 那样简洁。

现在,我们将探索 Kotlin 如何帮助开发人员避免 NullPointerException .让我们了解一下。

Kotlin 类型系统区分了可以保存 null 的引用和不能保存 null 的引用。因此,代码更加简洁易读,因为它是对开发人员的一种建议。

当引用不允许为 null 时,声明应为:

var myNonNullString:String = "my non null string"

前面的变量不能分配给空引用,如果这样做,我们会得到一个编译错误。看看代码是多么容易理解。

有时,我们需要允许变量有空引用,在这种情况下,我们可以使用 ? 作为运算符,如下所示:

var allowNull:String? = "permits null references"

简单的。注意 ? 运算符上的变量声明,它使变量接受空引用。 

有两种不同的方法可以避免 Kotlin 中的 NullPointerReference。第一个可以被称为安全调用,另一个可以称为Elvis Operator。让我们来看看那些。

Safe calls

安全调用可以使用 .?编写。当引用持有非空值时可以调用它,当值持有空引用时,将返回空值:

val hash:TrackedHashTag? = TrackedHashTag(hashTag="java",queue="java")
val queueString = hash?.queue

当 hash? 为空时,空值将分配给 queueString 属性。如果 hash? 具有有效引用,则 queue 属性将分配给 queueString 属性。

Elvis operator

可以developers 当引用为空时,期望返回一个默认值:

val hash:TrackedHashTag? = TrackedHashTag(hashTag="java",queue="java")
val queueString = hash?.queue ?: "unrecognized-queue"

当该值保持为空时,将返回默认值。

是时候在现实世界中使用 Kotlin 了。让我们开始。

Wrapping it up


现在,我们可以使用 Kotlin 语言的基础知识了。我们看到了一些例子并进行了一些练习。

我们查看了 Kotlin 的主要概念。我们已经了解了数据类如何帮助开发人员在应用层之间传输数据。此外,我们还了解了单例对象和伴生对象。现在我们可以尝试使用 Spring Framework 的全新支持创建一个真实的项目。

在接下来的部分中,我们将使用 Kotlin 语言创建一个项目,现在我们可以忘记 Java 语言。

Creating the project


现在,我们对如何在 Kotlin 语言中使用 programming 有了一个很好的想法。在本节中,我们将为我们的新项目创建基本结构,其中主要功能是使用 Twitter 流。让我们这样做。

Project use case

在我们开始编码之前,我们需要跟踪应用程序的需求。该应用程序是消息驱动的,我们将使用代理来提供消息传递基础设施。我们选择 RabbitMQ 代理是因为它提供了可靠性、高可用性和集群选项。此外,RabbitMQ 是现代消息驱动应用程序的流行选择。

该软件由 Pivotal 公司提供支持,该公司也是维护 Spring Framework 的 company。有一个庞大的社区支持该项目。 

我们将有三个项目。这三个项目将收集 Twitter 流并将其发送给接收者,以便以格式化的方式向最终用户显示 Tweets。

第一个将在本章中创建,负责将跟踪的主题标签保存在 Redis 缓存中。

注册新的主题标签后,它将向第二个项目发送一条消息,该项目将开始使用 Twitter 流并将其重定向到所需的队列。该队列将被另一个项目使用,该项目将格式化推文,最后将它们显示给最终用户。

我们将拥有三个微服务。让我们创造这些东西。

Creating the project with Spring Initializr

我们已经学习了如何使用 Spring Initializr 页面。我们将进入页面,然后选择以下模块:

  • 响应式网络

  • 反应式 Redis

页面内容应如下所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Kotlin基础知识和Spring Data Redis

我们可以选择组和神器。使用不同的名称没有问题。然后,我们可以点击Generate Project 等到下载结束。

Adding Jackson for Kotlin

我们需要为 KotlinJackson > Maven 项目的依赖项。事实上,我们的 pom.xml 上需要一个 Kotlin 标准库。另外,我们需要放入 jackson-module-kotlin,它允许我们在 Kotlin 上使用 JSON,在这些部分与 Java 有一些不同。

这部分非常简单,我们将在 pom.xml 的依赖项部分添加以下依赖项。依赖关系如下:

<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<version>${jackson.version}</version>
</dependency>

现在,我们已经配置了依赖项,我们可以设置插件来编译 Kotlin 源代码。在下一节中,我们将这样做。

Looking for the Maven plugins for Kotlin

该项目是使用 Kotlin configured 成功创建的。现在,我们来看看 pom.xml 中的 Maven 插件。该配置对于指导 Maven 如何编译 Kotlin 源代码和添加工件是必要的。

我们将在插件部分添加以下插件:

<plugin>
  <artifactId>kotlin-maven-plugin</artifactId>
  <groupId>org.jetbrains.kotlin</groupId>
  <version>${kotlin.version}</version>
  <configuration>
    <jvmTarget>1.8</jvmTarget>
  </configuration>
  <executions>
    <execution>
      <id>compile</id>
      <phase>process-sources</phase>
      <goals>
        <goal>compile</goal>
      </goals>
    </execution>
    <execution>
      <id>test-compile</id>
      <phase>process-test-sources</phase>
      <goals>
        <goal>test-compile</goal>
      </goals>
    </execution>
  </executions>
</plugin>

还有一件事要做。看看 Maven 如何为我们的 Kotlin 代码配置路径。这很容易。请看以下内容:

    <build>

<sourceDirectory>${project.basedir}/src/main/kotlin<
    /sourceDirectory<testSourceDirectory>${project.basedir}/src/
    test/kotlin</testSourceDirectory>

    .....

    </build>

我们在源路径中添加了 Kotlin 文件夹。

太棒了,项目结构已经准备好了,我们可以开始编码了!

 

Creating a Docker network for our application

为了为我们的应用程序创建隔离,我们将创建一个 custom Docker 网络。该网络是使用网桥驱动程序创建的。让我们使用以下命令来做到这一点:

docker network create twitter

好的,现在我们可以通过输入以下命令来检查网络列表:

docker network list

Twitter 网络应该在列表中,如下所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Kotlin基础知识和Spring Data Redis

最后一个是我们的 Twitter 网络。让我们从 Docker Hub 中拉取 Redis 映像。请看下一节。

Pulling the Redis image from the Docker Hub

我们需要做的第一件事是从 Docker下载 Redis 映像"id326223510" class="indexterm"> 集线器。为此,需要执行以下命令:

docker pull redis:4.0.6-alpine

我们使用了 Redis 的 alpine 版本,因为它比其他版本更小,并且具有合理的安全性。下载图像时,我们可以看到下载状态进度。

我们可以使用以下命令检查结果:

docker images

结果应如下所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Kotlin基础知识和Spring Data Redis

看看下载的图像。 Redis 必须在列表中。 

太棒了,现在我们将启动 Redis 实例。

Running the Redis instance


image 已下载,然后我们将为我们的应用程序启动 Redis 实例。命令可以是:

docker run -d --name redis --net twitter -p 6379:6379 redis:4.0.6-alpine

我们在这里有有趣的属性。我们用 redis 命名了我们的 Redis 实例, 这对于在接下来的章节中在容器中运行我们的应用程序很有用。此外,我们将 Redis 容器端口暴露给主机,使用的命令参数是 -p。最后,我们将容器附加到我们的 Twitter 网络。

好的,Redis 实例可以使用了。让我们看看 Spring Data Reactive Redis 的东西。

Configuring the redis-cli  tool

有一个很好的工具可以 connect 与 Redis 实例,称为 redis-cli< /代码>。有一些 Docker 镜像,但我们将把它安装在我们的 Linux 机器上。

要安装它,我们可以执行以下命令:

sudo apt-get install redis-tools -y

太好了,现在我们可以与我们的 Redis 容器连接和交互了。该工具可以执行读写指令,那么我们需要小心避免无意中的指令。

让我们连接起来。默认配置对我们来说已经足够了,因为我们已经在 run 指令上导出了端口 6379。在终端中键入以下命令:

redis-cli

然后我们将连接我们正在运行的实例。命令行应显示 Redis 主机和端口,如下图所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Kotlin基础知识和Spring Data Redis

太好了,客户端已配置和测试。

现在,我们将在我们的容器上执行一些 Redis 命令。

Understanding Redis


Redis 是一个开放的source 内存数据结构。 Redis 非常适合数据库缓存并且不常见,但它可以用作使用发布-订阅功能的消息代理,它可以用于解耦应用程序。

Redis 支持一些有趣的功能,例如事务、原子操作和对生存时间密钥的支持。 Time-to-live 对于给 key 一个时间很有用,驱逐策略总是很难实现,Redis 为我们提供了一个内置的解决方案。

Data types

Redis有很多支持的数据类型。最常见的 是字符串、散列、列表和排序集。我们将稍微了解其中的每一个,因为帮助我们为用例选择正确的数据类型很重要。

Strings

字符串是 Redis 更基本的数据类型。 string 值的最大长度为 512 MB。我们可以将它作为 JSON 存储在键的值中,或者也可以作为图像存储,因为 Redis 是二进制安全的。

Main commands

让我们看看我们需要的一些重要的命令

  • SET: It sets the key and holds the value. It is a simple and basic command of Redis. Here's an example:
       SET "user:id:10" "joe"

命令的返回应该是 OK。表示指令已成功执行。

  • GET: This command gets the value of the requested key. Remember GET can only be used with a string data type:
         GET "user:id:10"

如我们所见,该命令的返回应该是 joe

  • INCR: The INCR 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:
        SET "users" "0"
        INCR "users"
        GET "users"

正如我们所见, INCR 命令返回 1 作为命令输出,然后我们可以检查一下使用 GET 获取值。

  • DECR: The DECR command is opposite of INCR, it will decrement the value atomically as well:
        GET "users"
        DECR "users"
        GET "users"

 users key 的值减一,然后转换为 0。 

  • INCRBY: It will increment the value of the key by the argument. The new incremented value will be returned:
         GET "users"
         INCRBY "users" 2
         GET "users"

新值作为命令输出返回。

Lists

Lists 是简单的字符串列表。它们由 insertionordered 顺序。 Redis 还提供了在列表的头部或尾部添加新元素的说明。

列表可用于存储事物组、类别组,例如,按 categories 键分组。

Main commands

LPUSH:在键的头部插入新的 element。该命令还支持多个参数,在这种情况下,值将按照我们传递参数的相反顺序存储。

以下是一些命令示例:

    LPUSH "categories" "sports"
    LPUSH "categories" "movies"
    LRANGE "categories" 0 -1

看看 LRANGE 输出,我们可以看到 movie的值是第一个一个在列表中,因为 LPUSH 在头部插入了新元素。

RPUSH:在键的尾部插入新的元素。该命令也支持多个参数,在这种情况下,值将遵循各自的顺序。

以下是一些命令示例:

    RPUSH "categories" "kitchen"
    RPUSH "categories" "room"
    LRANGE "categories" 0 -1

正如我们所见,在 LRANGE 输出中,新值被插入到值的尾部。这是 RPUSH 命令的行为。

LSET: 它将元素设置在请求的索引上。

以下是一些命令示例:

    LSET "categories" 0 "series""
    LRANGE "categories" 0 -1

零索引的新值是 series。  LSET 命令为我们做到了这一点。

LRANGE:返回key的指定元素。命令参数是键、开始索引,最后是停止元素。  -1 在 stop 参数上将返回整个列表:

      LRANGE "categories" 0 2
      LRANGE "categories" 0 -1

正如我们所看到的,第一个命令将返回三个元素,因为零索引将被分组。

Sets

set 是字符串的集合。它们有一个不允许重复值的属性。 这意味着如果我们在集合上添加预先存在的值,它将导致相同的元素,在这种情况下,优点是不需要验证 < span>元素 存在于片场。 另一个重要的特征 是集合是无序的。此行为与 Redis 列表不同。它在不同的用例中很有用,例如计算唯一身份访问者、跟踪唯一 IP 等等。

Main commands

以下是列出的主要命令及其用法:

  • 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:
        SADD "unique-visitors" "joe"
        SADD "unique-visitors" "mary"

如我们所见,该命令返回一个,因为我们每次添加一个用户。

  • SMEMBERS: It returns all the members of a requested key:
       SMEMBERS "unique-visitors"

该命令将返回 joe 和 mary 因为这些是存储在 唯一访问者 key。

  • SCARD: It returns the numbers of elements of a requested key:
        SCARD "unique-visitors"

该命令将返回存储在请求键中的元素数量,在这种情况下, 输出 将是 2

Spring Data Reactive Redis


Spring Data Redis 提供了一种简单的 方式 从 Spring Boot 应用程序与 Redis 服务器交互。该项目是 Spring Data 系列的一部分,为开发人员提供高级和低级抽象。

支持 Jedis 和 Lettuce 连接器作为该项目的驱动程序。

该项目提供了许多与 Redis 交互的功能和设施。 Repository 接口也受支持。 Redis 有一个 CrudRepository ,就像在其他实现中一样,例如 Spring Data JPA。 

该项目的中心类是 RedisTemplate,它提供了高级 API 执行 Redis 操作和序列化支持。我们将使用这个类与 Redis 上的集合数据结构进行交互。

这个项目支持 Reactive 实现,这些对我们来说是重要的特征,因为我们正在寻找 Reactive 实现。

Configuring the ReactiveRedisConnectionFactory

要配置 ReactiveRedisConnectionFactory,我们可以使用application.yaml文件,因为它更容易维护和集中我们的配置。

原理和其他 Spring Data 项目一样,我们应该application.yaml 文件,如下:

spring:
redis:
host: localhost
port:6379

在前面的配置文件中,我们将Redis配置指向了 localhost, 我们可以看到。配置非常简单,也很容易理解。

完毕。连接工厂已配置。下一步是提供一个 RedisTemplate 来与我们的 Redis 实例交互。请看下一节。

Providing a ReactiveRedisTemplate

Spring Data Redis 的主类是 ReactiveRedisTemplate,那么我们需要为 Spring 容器配置并提供一个实例。

我们需要提供一个实例并为所需的 ReactiveRedisTemplate 配置正确的序列化程序。 Serializers 是 Spring Data Redis 用来从存储在 Redis 中的 Key 中的原始字节序列化和反序列化对象的方式 字段。

我们将只使用 StringRedisSerializer,因为我们的 KeyValue 很简单字符串和 Spring Data Redis 已经为我们准备好了这个序列化程序。

让我们生成我们的 ReactiveRedisTemplate实现 应该如下所示:

package springfive.twittertracked.infra.redis

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory
import org.springframework.data.redis.core.ReactiveRedisTemplate
import org.springframework.data.redis.serializer.RedisSerializationContext

@Configuration
open class RedisConfiguration {

  @Bean
open funreactiveRedisTemplate(connectionFactory:ReactiveRedisConnectionFactory):  
                                 ReactiveRedisTemplate<String, String> {
return ReactiveRedisTemplate(connectionFactory, RedisSerializationContext.string())
  }

}

惊人的。这是我们在 Spring Framework 中使用 Kotlin 的第一个代码。关键字 open 与 Java 的 final 关键字相反。这意味着这个函数可以从这个类继承。默认情况下,Kotlin 中的所有类都是 final 的。 Spring Framework 需要 @Bean 上的非 final 函数在 @Configuration 类上,然后我们需要插入 打开

我们收到了 ReactiveRedisConnectionFactory 作为参数。 Spring 知道我们使用 Redis 的配置在 application.yaml 文件中生成了什么。然后容器可以注入工厂。

最后,我们声明 ReactiveRedisTemplate<String, String> 作为我们函数的返回值。

有趣的工作,我们已经准备好使用我们的 Redis 模板了。现在,我们将实现我们的第一个 Redis 存储库。下一节见。

Creating Tracked Hashtag repository

我们已经创建了 ReactiveRedisTemplate,然后我们可以在我们的存储库实现中使用这个对象。我们将创建一个简单的存储库来interact 与 Redis,记住存储库应该是响应式的,这是我们应用程序的一个重要特征.然后我们需要返回 MonoFlux 以使存储库 Reactive。让我们看看我们的存储库实现:

package springfive.twittertracked.domain.repository

import org.springframework.data.redis.core.ReactiveRedisTemplate
import org.springframework.stereotype.Service
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import springfive.twitterconsumer.domain.TrackedHashTag

@Service
class TrackedHashTagRepository(private val redisTemplate: ReactiveRedisTemplate<String, String>){

  fun save(trackedHashTag: TrackedHashTag): Mono<TrackedHashTag>? {
return this.redisTemplate
             .opsForSet().add("hash-tags","${trackedHashTag.hashTag}:${trackedHashTag.queue}")
             .flatMap { Mono.just(trackedHashTag) }
  }

fun findAll(): Flux<TrackedHashTag> {
return this.redisTemplate.opsForSet().members("hash-tags").flatMap { el ->
val data = el.split(":")
      Flux.just(TrackedHashTag(hashTag = data[0],queue = data[1]))
}
  }
}

我们收到了 ReactiveRedisTemplate<String, String> 作为我们类的注入,Spring Framework 可以检测构造函数并注入正确的实现。

现在,我们需要这两个函数。第一个负责在 Redis 的集合结构上插入我们的实体TrackedHashTag。我们在 Redis 上添加 hash-tags key 的值。此函数返回一个 Mono 和 TrackedHashTag 值。注意 save 功能。我们为我们的价值创建了一个模式,该模式遵循 hashtagqueue,其中 hashtag 是收集推文和我们将在下一节中使用的队列发送到 RabbitMQ 队列。

第二个函数返回hash-tags 键的所有值,表示所有被跟踪来自我们系统的标签。此外,我们还需要执行一些逻辑来创建我们的模型, TrackedHashTag

存储库已经完成,现在我们可以创建我们的服务层来封装存储库。让我们在下一节中这样做。

Creating the service layer

我们的存储库已经可以使用了,现在我们 可以创建我们的服务层了。这一层负责编排我们的存储库调用。在我们的例子中,它非常简单,但在一些复杂的场景中,它可以帮助我们封装存储库调用。

我们的服务将被称为  TrackedHashTagService,它将负责与我们之前创建的存储库进行交互。实现应如下所示:

package springfive.twittertracked.domain.service

import org.springframework.stereotype.Service
import springfive.twitterconsumer.domain.TrackedHashTag
import springfive.twitterconsumer.domain.repository.TrackedHashTagRepository

@Service
class TrackedHashTagService(private val repository: TrackedHashTagRepository) {

funsave(hashTag:TrackedHashTag) = this.repository.save(hashTag)

funall() = this.repository.findAll()

}

做得好。在这里,有基本的东西。我们有注入我们的存储库以与 Redis 交互的构造。这里有趣的一点是函数声明。没有主体和返回类型,因为 Kotlin 编译器可以推断返回类型,这有助于开发人员避免编写样板代码。 

Exposing the REST resources

现在,我们已经创建了 repository 和服务层,我们准备通过 HTTP 端点公开我们的服务:

package springfive.twittertracked.domain.resource

import org.springframework.web.bind.annotation.*
import springfive.twitterconsumer.domain.TrackedHashTag
import springfive.twitterconsumer.domain.service.TrackedHashTagService


@RestController
@RequestMapping("/api/tracked-hash-tag")
class TrackedHashTagResource(private val service:TrackedHashTagService) {

  @GetMapping
  funall() = this.service.all()

  @PostMapping
  funsave(@RequestBody hashTag:TrackedHashTag) = this.service.save(hashTag)

}

代码非常简洁明了。看看这段代码有多简洁。 前面的代码是 Kotlin 如何帮助开发人员创建可读代码的示例。谢谢,科特林。

Creating a Twitter application


对于这个项目,我们需要在 Twitter 平台上配置 一个应用程序。这是必要的,因为我们将使用 Twitter 的 API 来搜索推文,例如,Twitter 帐户是必需的。我们不会解释如何创建 Twitter 帐户。网上有很多关于这方面的文章。

Twitter账号创建后,我们需要去 https://apps.twitter.com/ 并创建一个新应用。该页面与 以下 屏幕截图非常相似:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Kotlin基础知识和Spring Data Redis

我们将点击 Create New App 按钮开始创建过程。当我们单击该按钮时,将显示以下页面。我们需要填写必填字段并接受 Twitter 协议:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Kotlin基础知识和Spring Data Redis

我们可以选择应用程序名称,填写描述和网站。这些细节由你决定。

然后,我们需要接受协议并点击创建你的Twitter应用

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Kotlin基础知识和Spring Data Redis

好工作。我们的 Twitter 应用程序几乎可以使用了。

现在,我们只需要配置应用程序以供使用。

我们需要检查我们的密钥和访问令牌是否配置正确。让我们点击 Keys and Access Tokens选项卡并检查值,如下所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Kotlin基础知识和Spring Data Redis

如我们所见,前面的截图中有一些重要的配置。 Consumer KeyConsumer Secret 是通过 Twitter 进行身份验证所必需的蜜蜂。这里的另一个重点是 Access Level;确保它被配置为 只读,如 在前面的截图中,我们不会在Twitter上做写操作。

让我们对它进行 Docker 化。

惊人的。我们拥有将跟踪的主题标签保存在 Redis 实例上的系统。该应用程序是完全反应式的,没有阻塞线程。 

现在,我们将配置 Maven 插件以生成 Docker 映像。配置与我们在第 3 章中所做的非常相似, 持久化 Spring Data 和 Reactive Fashion。但是,现在我们将创建一个使用 Kotlin 语言运行的第一个容器。让我们这样做。

Configuring pom.xml

现在,我们将配置我们的 pom.xml 使其能够generate我们的 Docker 镜像。我们需要更改的第一件事是我们的最终名称工件,因为 Docker 映像不允许 - 字符,那么我们需要正确配置。

配置非常简单,将 <finalName> 标签放在 <build> 节点上。让我们这样做:

<build>

  <finalName>tracked_hashtag</finalName>

  ....

</build>

好的。我们已正确配置最终名称以正确生成 Docker 映像。现在,我们将配置 Maven Docker 插件以通过 Maven 目标生成 Docker 映像。

在构建节点内的插件部分,我们应该放入以下插件配置:

<plugin>
  <groupId>io.fabric8</groupId>
  <artifactId>docker-maven-plugin</artifactId>
  <version>0.21.0</version>
  <configuration>
    <images>
      <image>
        <name>springfivebyexample/${project.build.finalName}</name>
        <build>
          <from>openjdk:latest</from>
          <entryPoint>java -Dspring.profiles.active=container -jar 
       /application/${project.build.finalName}.jar</entryPoint>
          <assembly>
            <basedir>/application</basedir>
            <descriptorRef>artifact</descriptorRef>
            <inline>
              <id>assembly</id>
              <files>
                <file>
               <source>target/${project.build.finalName}.jar</source>
                </file>
              </files>
            </inline>
          </assembly>
          <tags>
            <tag>latest</tag>
          </tags>
          <ports>
            <port>9090</port>
          </ports>
        </build>
        <run>
          <namingStrategy>alias</namingStrategy>
        </run>
        <alias>${project.build.finalName}</alias>
      </image>
    </images>
  </configuration>
</plugin>

配置非常简单。我们以前这样做过。在配置部分,我们从镜像进行配置,在我们的例子中是 openjdk:latest、Docker 入口点和公开的端口。

让我们在下一节中创建我们的 Docker 映像。

Creating the image

我们的项目之前配置了 Maven Docker 插件。我们可以使用 Maven Docker 插件使用 docker:build 目标生成 Docker 镜像。然后,是时候生成我们的Docker镜像了。

要生成 Docker 映像,请键入以下命令:

mvn clean install docker:build

现在,我们必须等待 Maven 构建并检查 Docker 镜像是否成功生成。

检查 Docker 镜像,我们应该会看到生成的新镜像。为此,我们 可以使用docker images 命令:

docker images

对了,我们应该在图片列表中看到 springfivebyexample/tracked_hashtag:latest,如下图所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Kotlin基础知识和Spring Data Redis

太棒了,我们的 Docker 镜像已经准备好与我们的第一个 Kotlin 语言的 Spring Boot 应用程序一起运行了。让我们现在运行它。

Running the container

让我们运行我们的容器。在此之前,我们需要记住 一些事情。该容器应该在 Twitter 网络上运行,以便能够连接到我们也在 Twitter 网络上运行的 Redis 实例。请记住,在容器基础架构中运行时,Redis 的 localhost 地址不再起作用。

要运行我们的容器,我们可以执行以下命令:

docker run -d --name hashtag-tracker --net twitter -p 9090:9090 springfivebyexample/tracked_hashtag

恭喜,我们的应用程序正在 Docker 容器中运行并连接到我们的 Redis 实例。让我们创建并测试我们的 API 以检查所需的行为。

Testing APIs


我们的容器正在运行。现在,我们可以尝试调用 API 来检查行为。在这一部分中,我们将使用 curl 命令行。 curl 允许我们在 Linux 上通过命令行调用 API。此外,我们将使用 jq 使 JSON 在命令行上可读,如果您没有这些,请查看提示框以安装这些工具。

让我们调用我们的创建 API,记住创建我们可以在 API 的基本路径中使用 POST 方法。然后键入以下命令:

curl -H "Content-Type: application/json" -X POST -d '{"hashTag":"java","queue":"java"}' 
 http://localhost:9090/api/tracked-hash-tag

这里有一些有趣的事情。 -H 参数指示 curl 将其放入请求标头和 -d< /code> 表示请求体。此外,最后,我们有服务器地址。

我们创建了新的 tracked-hash-tag。让我们检查我们的 GET API 来获取这些数据:

curl 'http://localhost:9090/api/tracked-hash-tag' | jq '.'

太棒了,我们调用了 curl 工具并使用 jq 工具打印了 JSON 值。命令输出应类似于以下屏幕截图:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Kotlin基础知识和Spring Data Redis

Note

要在 Ubuntu 上安装 curl,我们可以使用 sudo apt-get install curl -y。此外,要安装 jq, 我们可以使用 sudo apt-get install jq -y

Summary


在本章中,我们介绍了 Kotlin 语言,这是 JVM 最突出的语言,因为它具有超快的编译器,例如,如果我们将其与 Scala 进行比较。它还带来了代码的简单性,并帮助开发人员创建更简洁易读的代码。

我们还使用 Kotlin 作为语言的基本概念在 Spring 框架中创建了我们的第一个应用程序,并且我们看到了 Kotlin 如何以一种实用的方式帮助开发人员。

我们已经介绍了 Redis 作为缓存和 Spring Data Reactive Redis,它支持  Redis 在响应式范式中。

在本章的最后一部分,我们学习了如何创建一个 Twitter 应用程序,这需要我们创建下一个应用程序,并开始使用响应式 Rest 客户端在响应式编程中使用 Twitter API。

让我们跳到下一章,了解更多关于 Spring Reactive 的知识。