Spring 实战(第五版)
  • Spring 实战(第 5 版)
  • 第一部分 Spring 基础
  • 第 1 章 Spring 入门
    • 1.1 什么是 Spring?
    • 1.2 初始化 Spring 应用程序
      • 1.2.1 使用 Spring Tool Suite 初始化 Spring 项目
      • 1.2.2 检查 Spring 项目结构
    • 1.3 编写 Spring 应用程序
      • 1.3.1 处理 web 请求
      • 1.3.2 定义视图
      • 1.3.3 测试控制器
      • 1.3.4 构建并运行应用程序
      • 1.3.5 了解 Spring Boot DevTools
      • 1.3.6 回顾
    • 1.4 俯瞰 Spring 风景线
      • 1.4.1 Spring 核心框架
      • 1.4.2 Spring Boot
      • 1.4.3 Spring Data
      • 1.4.4 Spring Security
      • 1.4.5 Spring Integration 和 Spring Batch
      • 1.4.6 Spring Cloud
    • 1.5 小结
  • 第 2 章 开发 Web 应用程序
    • 2.1 展示信息
      • 2.1.1 建立域
      • 2.1.2 创建控制器类
      • 2.1.3 设计视图
    • 2.2 处理表单提交
    • 2.3 验证表单输入
      • 2.3.1 声明验证规则
      • 2.3.2 在表单绑定时执行验证
      • 2.3.3 显示验证错误
    • 2.4 使用视图控制器
    • 2.5 选择视图模板库
      • 2.5.1 缓存模板
    • 2.6 小结
  • 第 3 章 处理数据
    • 3.1 使用 JDBC 读写数据
      • 3.1.1 为域适配持久化
      • 3.1.2 使用 JdbcTemplate
      • 3.1.3 定义模式并预加载数据
      • 3.1.4 插入数据
    • 3.2 使用 Spring Data JPA 持久化数据
      • 3.2.1 添加 Spring Data JPA 到数据库中
      • 3.2.2 注解域作为实体
      • 3.2.3 声明 JPA repository
      • 3.2.4 自定义 JPA repository
    • 3.3 小结
  • 第 4 章 Spring 安全
    • 4.1 启用 Spring Security
    • 4.2 配置 Spring Security
      • 4.2.1 内存用户存储
      • 4.2.2 基于 JDBC 的用户存储
      • 4.2.3 LDAP 支持的用户存储
      • 4.2.4 自定义用户身份验证
    • 4.3 保护 web 请求
      • 4.3.1 保护请求
      • 4.3.2 创建用户登录页面
      • 4.3.3 登出
      • 4.3.4 阻止跨站请求伪造攻击
    • 4.4 了解你的用户
    • 4.5 小结
  • 第 5 章 使用配置属性
    • 5.1 微调自动配置
      • 5.1.1 理解 Spring 环境抽象
      • 5.1.2 配置数据源
      • 5.1.3 配置嵌入式服务器
      • 5.1.4 配置日志
      • 5.1.5 使用特殊的属性值
    • 5.2 创建自己的配置属性
      • 5.2.1 定义配置属性持有者
      • 5.2.2 声明配置属性元数据
    • 5.3 使用 profile 文件进行配置
      • 5.3.1 定义特定 profile 的属性
      • 5.3.2 激活 profile 文件
      • 5.3.3 有条件地使用 profile 文件创建 bean
    • 5.4 小结
  • 第二部分 集成 Spring
  • 第 6 章 创建 REST 服务
    • 6.1 编写 RESTful 控制器
      • 6.1.1 从服务器获取数据
      • 6.1.2 向服务器发送数据
      • 6.1.3 更新服务器上的资源
      • 6.1.4 从服务器删除数据
    • 6.2 启用超媒体
      • 6.2.1 添加超链接
      • 6.2.2 创建资源装配器
      • 6.2.3 嵌套命名关系
    • 6.3 启用以数据为中心的服务
      • 6.3.1 调整资源路径和关系名称
      • 6.3.2 分页和排序
      • 6.3.3 添加用户端点
      • 6.3.4 向 Spring Data 端点添加用户超链接
    • 6.4 小结
  • 第 7 章 调用 REST 服务
    • 7.1 使用 RestTemplate 调用 REST 端点
      • 7.1.1 请求 GET 资源
      • 7.1.2 请求 PUT 资源
      • 7.1.3 请求 DELETE 资源
      • 7.1.4 请求 POST 资源
    • 7.2 使用 Traverson 引导 REST API
    • 7.3 小结
  • 第 8 章 发送异步消息
    • 8.1 使用 JMS 发送消息
      • 8.1.3 接收 JMS 消息
      • 8.1.2 使用 JmsTemplate 发送消息
      • 8.1.1 设置 JMS
    • 8.2 使用 RabbitMQ 和 AMQP
      • 8.2.1 添加 RabbitMQ 到 Spring 中
      • 8.2.2 使用 RabbitTemplate 发送消息
      • 8.2.3 从 RabbitMQ 接收消息
    • 8.3 使用 Kafka 发送消息
      • 8.3.1 在 Spring 中设置 Kafka
      • 8.3.2 使用 KafkaTemplate 发送消息
      • 8.3.3 编写 Kafka 监听器
    • 8.4 小结
  • 第 9 章 集成 Spring
    • 9.1 声明简单的集成流
      • 9.1.1 使用 XML 定义集成流
      • 9.1.2 在 Java 中配置集成流
      • 9.1.3 使用 Spring Integration 的 DSL 配置
    • 9.2 探索 Spring Integration
      • 9.2.1 消息通道
      • 9.2.2 过滤器
      • 9.2.3 转换器
      • 9.2.4 路由
      • 9.2.5 分割器
      • 9.2.6 服务激活器
      • 9.2.7 网关
      • 9.2.8 通道适配器
      • 9.2.9 端点模块
    • 9.3 创建 Email 集成流
    • 9.4 总结
  • 第三部分 响应式 Spring
  • 第 10 章 Reactor 介绍
    • 10.1 理解响应式编程
      • 10.1.1 定义响应式流
    • 10.2 Reactor
      • 10.2.1 图解响应式流
      • 10.2.2 添加 Reactor 依赖
    • 10.3 通用响应式操作实战
      • 10.3.1 创建响应式类型
      • 10.3.2 响应式类型结合
      • 10.3.3 转换和过滤响应式流
      • 10.3.4 对反应类型执行逻辑操作
    • 10.4 总结
  • 第 11 章 开发响应式 API
    • 11.1 使用 Spring WebFlux
      • 11.1.1 Spring WebFlux 介绍
      • 11.1.2 编写响应式 Controller
    • 11.2 定义函数式请求处理程序
    • 11.3 测试响应式 Controller
      • 11.3.1 测试 GET 请求
      • 11.3.2 测试 POST 请求
      • 11.3.3 使用线上服务器进行测试
    • 11.4 响应式消费 REST API
      • 11.4.1 通过 GET 方式获取资源
      • 11.4.2 通过 POST 方式发送资源
      • 11.4.3 删除资源
      • 11.4.4 处理请求错误
      • 11.4.5 请求转换
    • 11.5 保护响应式 web API
      • 11.5.1 配置响应式 Web 安全
      • 11.5.2 配置响应式用户信息服务
    • 11.6 总结
  • 第 12 章 响应式持久化数据
    • 12.1 理解 Spring Data 响应式历程
      • 12.1.1 Spring Data 响应式精髓
      • 12.1.2 在响应式与非响应式之间进行转换
      • 12.1.3 开发响应式库
    • 12.2 使用响应式 Cassandra 库
      • 12.2.1 开启 Spring Data Cassandra
      • 12.2.2 理解 Cassandra 数据模型
      • 12.2.3 Cassandra 持久化实体映射
      • 12.2.4 编写响应式 Cassandra 库
    • 12.3 编写响应式 MongoDB 库
      • 12.3.1 开启Spring Data MongonDB
      • 12.3.2 MongoDB 持久化实体映射
      • 12.3.3 编写响应式 MongoDB 库
    • 12.4 总结
  • 第四部分 云原生 Spring
  • 第 13 章 服务发现
    • 13.1深入思考微服务
    • 13.2 配置服务注册
      • 13.2.1 配置 Eureka
      • 13.2.2 扩展 Eureka
    • 13.3 注册并发现服务
      • 13.3.1 配置 Eureka 客户端属性
      • 13.3.2 消费服务
    • 13.4 总结
  • 第 14 章 配置管理
    • 14.1 共享配置
    • 14.2 运行配置服务器
      • 14.2.1 启动配置服务器
      • 14.2.2 填写配置库
    • 14.3 消费共享的配置
    • 14.4 服务应用程序和特定配置文件的属性
      • 14.4.1 服务特定应用程序的属性
      • 14.4.2 服务配置文件属性
    • 14.5 为配置的属性加密
      • 14.5.1 在 Git 中加密属性
      • 14.5.2 在 Vault 中存储密码
    • 14.6 远程刷新配置属性
      • 14.6.1 手动刷新配置属性
      • 14.6.2 自动刷新配置属性
    • 14.7 总结
  • 第 15 章 处理失败和时延
    • 15.1 了解断路器
    • 15.2 定义断路器
      • 15.2.1 缓解时延
      • 15.2.2 管理断路器阈值
    • 15.3 管理失败事件
      • 15.3.1 介绍 Hystrix 面板
      • 15.3.2 了解 Hystrix 线程池
    • 15.4 聚合多个 Hystrix 流
    • 15.5 总结
  • 第五部分 部署Spring
  • 第 16 章 使用 SpringBoot Actuator
    • 16.1 介绍 Actuator
      • 16.1.1 配置 Actuator 基本路径
      • 16.1.2 启用和禁用 Actuator 端点
    • 16.2 使用 Actuator 端点
      • 16.2.1 获取重要的应用程序信息
      • 16.2.2 查看配置详细信息
      • 16.2.3 查看应用程序活动
      • 16.2.4 利用运行时指标
    • 16.3 自定义 Actuator
      • 16.3.1 向 /info 端点提供信息
      • 16.3.2 自定义健康指标
      • 16.3.3 注册自定义指标
      • 16.3.4 创建自定义端点
    • 16.4 保护 Actuator
    • 16.5 总结
  • 第 17 章 管理 Spring
    • 17.1 使用 SpringBoot Admin
      • 17.1.1 创建 Admin 服务端
      • 17.1.2 注册 Admin 客户端
    • 17.2 深入 Admin 服务端
      • 17.2.1 查看普通应用程序运行状况和信息
      • 17.2.2 观察关键指标
      • 17.2.3 检查环境属性
      • 17.2.4 查看并设置 log 级别
      • 17.2.5 监控线程
      • 17.2.6 追踪 HTTP 请求
    • 17.3 保护 Admin 服务端
      • 17.3.1 在 Admin 服务端中启用登录
      • 17.3.2 使用 Actuator 进行认证
    • 17.4 总结
  • 第 18 章 使用 JMX 监控 Spring
    • 18.1 使用 Actuator MBean
    • 18.2 创建自己的 MBean
    • 18.3 发送通知
    • 18.4 总结
  • 第 19 章 部署 Spring
    • 19.1 权衡部署选项
    • 19.2 构建并部署 WAR 文件
    • 19.3 将 JAR 文件推送到 Cloud Foundry
    • 19.4 在 Docker 容器中运行 SpringBoot
    • 19.5 终章
    • 19.6 总结
由 GitBook 提供支持
在本页

这有帮助吗?

  1. 第 11 章 开发响应式 API
  2. 11.1 使用 Spring WebFlux

11.1.2 编写响应式 Controller

你可能还记得,在第 6 章中,你为 Taco Cloud 的 REST API 创建了一些 controller。这些 controller 具有处理请求的方法,这些方法根据域类型(如 Order 和 Taco)或域类型的集合,处理输入和输出。提醒一下,请考虑你在第 6 章中写过的 DesignTacoController 中的以下片段:

@RestController
@RequestMapping(path="/design", produces="application/json")
@CrossOrigin(origins="*")
public class DesignTacoController {
    ...
    @GetMapping("/recent")
    public Iterable<Taco> recentTacos() {
        PageRequest page = PageRequest.of(
            0, 12, Sort.by("createdAt").descending());
        
        return tacoRepo.findAll(page).getContent();
    }
    ...
}

如前所述,recentTacos() controller 处理 /design/recent 的 HTTP GET 请求,以返回最近创建的 tacos 的列表。更具体地说,它返回一个 Iterable 类型的 Taco。这主要是因为这是从 respository 的 findAll() 方法返回的,或者更准确地说,是从 findAll() 返回的页面对象的 getContent() 方法返回的。

这很好,但是 Iterable 不是一个响应式的。你将不能对它应用任何响应式操作,也不能让框架利用它作为响应式类型在多个线程上分割任何工作。你想要的是 recentTacos() 返回一个 Flux<Taco>。

这里有一个简单但有点有限的选项,就是重写 recentTacos() 将 Iterable 转换为 Flux。而且,当你使用它时,可以去掉分页代码,并用调用 take() 来替换它:

@GetMapping("/recent")
public Flux<Taco> recentTacos() {
    return Flux.fromIterable(tacoRepo.findAll()).take(12);
}

使用 Flux.fromIterable(),可以将 Iterable<Taco> 转换为 Flux<Taco>。现在你正在使用一个 Flux,可以使用take() 操作将返回的 Flux 限制为最多 12 个 Taco 对象。不仅代码简单,它还处理一个响应式 Flux,而不是一个简单的 Iterable。

迄今为止,编写响应式代码是一个成功的举措。但是,如果 repository 提供了一个可以开始使用的 Flux,那就更好了,这样就不需要进行转换。如果是这样的话,那么 recentTacos() 可以写成如下:

@GetMapping("/recent")
public Flux<Taco> recentTacos() {
    return tacoRepo.findAll().take(12);
}

那就更好了!理想情况下,一个响应式 cotroller 将是一个端到端的响应式栈的顶端,包括 controller、repository、database 和任何可能位于两者之间的 serviec。这种端到端的响应式栈如图 11.3 所示:

这样的端到端的栈要求 repository 被写入以返回一个 Flux,而不是一个Iterable。在下一章中,我们将探讨如何编写响应式 repostitory,但下面我们将看一看响应式 TacoRepository 可能是什么样子:

public interface TacoRepository extends ReactiveCrudRepository<Taco, Long> {
}

然而,在这一点上,最重要的是,除了使用 Flux 而不是 Iterable 以及如何获得 Flux 外,定义响应式 WebFlux controller 的编程模型与非响应式 Spring MVC controller 没有什么不同。两者都用 @RestController 和类级别的 @RequestMapping 进行了注解。它们都有请求处理函数,在方法级别用 @GetMapping 进行注解。真正的问题是处理程序方法返回什么类型。

另一个要做的重要观察是,尽管从 repository 中获得了一个 Flux<Taco>,但你可以在不调用 subscribe() 的情况下返回它。实际上,框架将为你调用 subscribe()。这意味着当处理对 /design/recent 的请求时,recentTacos() 方法将被调用,并在从数据库中获取数据之前返回!

返回单个值

作为另一个例子,请考虑 DesignTacoController 中的 tacoById() 方法,如第 6 章中所述:

@GetMapping("/{id}")
public Taco tacoById(@PathVariable("id") Long id) {
    Optional<Taco> optTaco = tacoRepo.findById(id);
    
    if (optTaco.isPresent()) {
        return optTaco.get();
    }
    
    return null;
}

在这里,这个方法处理 /design/{id} 的 GET 请求并返回一个 Taco 对象。因为 repository 的 findById() 返回一个 Optional,所以还必须编写一些笨拙的代码来处理这个问题。但是假设 findById() 返回 Mono<Taco> 而不是 Optional<Taco>。在这种情况下,可以重写 controller 的 tacoById(),如下所示:

@GetMapping("/{id}")
public Mono<Taco> tacoById(@PathVariable("id") Long id) {
    return tacoRepo.findById(id);
}

哇!这就简单多了。然而,更重要的是,通过返回 Mono<Taco> 而不是 Taco,可以使 Spring WebFlux 以一种被动的方式处理响应。因此,你的API将更好地响应大的负载。

使用 RxJava 类型

值得指出的是,虽然在使用 Spring WebFlux 时,像 Flux 和 Mono 这样的 Reactor 类型是一个自然的选择,但是你也可以选择使用像 Observable 和 Single 这样的 RxJava 类型。例如,假设 DesignTacoController 和后端 repository 之间有一个 service,它处理 RxJava 类型。在这种情况下,recentTacos() 方法的编写方式如下:

@GetMapping("/recent")
public Observable<Taco> recentTacos() {
    return tacoService.getRecentTacos();
}

类似地,可以编写 tacoById() 方法来处理 RxJava 的 Single 元素,而不是 Mono:

@GetMapping("/{id}")
public Single<Taco> tacoById(@PathVariable("id") Long id) {
    return tacoService.lookupTaco(id);
}

此外,Spring WebFlux controller 方法还可以返回 RxJava 的 Completable,这相当于 Reactor 中的 Mono<Void>。WebFlux 还可以返回一个 Flowable,作为 Observable 或 Reactor 的 Flux 的替代。

响应式地处理输入

到目前为止,我们只关心控制器方法返回的响应式类型。但是使用 Spring WebFlux,你还可以接受 Mono 或 Flux 作为处理程序方法的输入。请考虑 DesignTacoController 中 postTaco() 的原始实现:

@PostMapping(consumes="application/json")
@ResponseStatus(HttpStatus.CREATED)
public Taco postTaco(@RequestBody Taco taco) {
    return tacoRepo.save(taco);
}

正如最初编写的,postTaco() 不仅返回一个简单的 Taco 对象,而且还接受一个绑定到请求主体内容的 Taco 对象。这意味着在请求有效负载完全解析并用于实例化 Taco 对象之前,无法调用 postTaco()。这也意味着postTaco() 在对 repository 的 save() 方法的阻塞调用,在返回之前无法返回。简言之,请求被阻塞了两次:当它进入 postTaco() 时,然后在 postTaco() 内部被再次阻塞。但是,通过对 postTaco() 应用一点响应式编码,可以使其成为一种完全无阻塞的请求处理方法:

@PostMapping(consumes="application/json")
@ResponseStatus(HttpStatus.CREATED)
public Mono<Taco> postTaco(@RequestBody Mono<Taco> tacoMono) {
    return tacoRepo.saveAll(tacoMono).next();
}

在这里,postTaco() 接受 Mono<Taco> 并调用 repository 的 saveAll() 方法,正如你将在下一章中看到的,该方法接受 Reactive Streams Publisher 的任何实现,包括 Mono 或 Flux。saveAll() 方法返回一个 Flux<Taco>,但是因为是从 Mono 开始的,所以 Flux 最多会发布一个 Taco。因此,你可以调用 next() 来获取将从 postTaco() 返回的 Mono<Taco>。

通过接受 Mono<Taco> 作为输入,可以立即调用该方法,而无需等待 Taco 从请求体被解析。由于 repository 也是被动的,它将接受一个 Mono 并立即返回一个 Flux<Taco>,从中调用 next() 并返回 Mono<Taco>。所有这些都是在处理请求之前完成的!

Spring WebFlux 是 Spring MVC 的一个极好的替代品,它提供了使用与 Spring MVC 相同的开发模型编写响应式 web 应用程序的选项。不过,Spring 5 还有另一个新的窍门。让我们看看如何使用 Spring 5 的新函数式编程风格创建响应式 API。

上一页11.1.1 Spring WebFlux 介绍下一页11.2 定义函数式请求处理程序

最后更新于5年前

这有帮助吗?

图 11.3 为了最大限度地发挥响应式 web 框架的优势,它应该是完整的端到端响应式堆栈的一部分