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. 第 2 章 开发 Web 应用程序

2.2 处理表单提交

如果在视图中查看 <form> 标签,可以看到它的 method 属性被设置为 POST。而且,<form> 没有声明 action 属性。这意味着在提交表单时,浏览器将收集表单中的所有数据,并通过 HTTP POST 请求将其发送到服务器,发送到显示表单的 GET 请求的同一路径 —— /design 路径。

因此,需要在该 POST 请求的接收端上有一个控制器处理程序方法。需要在 DesignTacoController 中编写一个新的处理程序方法来处理 /design 接口的 POST 请求。

在程序清单 2.2 中,使用 @GetMapping 注释指定 showDesignForm() 方法应该处理 HTTP GET 请求 /design。与 @GetMapping 处理 GET 请求一样,可以使用 @PostMapping 处理 POST 请求。为了处理玉米饼艺术家提交的设计,将以下程序清单中的 processDesign() 方法添加到 DesignTacoController 中。

程序清单 2.4 使用 @PostMapping 处理 POST 请求
@PostMapping
public String processDesign(Taco design) {
    // Save the taco design...
    // We'll do this in chapter 3
    log.info("Processing design: " + design);
    return "redirect:/orders/current";
}

当应用到 processDesign() 方法时,@PostMapping 与类级别 @RequestMapping 相协调,以表明 processDesign() 应该处理 /design 接口的 POST 请求。这正是需要处理的一个玉米饼艺术家提交的作品。

提交表单时,表单中的字段被绑定到 Taco 对象的属性(其类在下一个程序清单中显示),该对象作为参数传递给 processDesign()。从这里开始,processDesign() 方法可以对 Taco 对象做任何它想做的事情。

程序清单 2.5 域对象定义玉米饼设计
package tacos;
​
import java.util.List;
import lombok.Data;
​
@Data
public class Taco {
    private String name;
    private List<String> ingredients;
}

Taco 是一个简单的 Java 域对象,具有两个属性。与 Ingredient 类似,Taco 类也使用 @Data 进行注释,以便在运行时自动生成基本的 JavaBean 方法。

如果查看程序清单 2.3 中的表单,将看到几个 checkbox 元素,它们都带有 ingredients 名称和一个名为 name 的文本输入元素。表单中的这些字段直接对应于 Taco 类的 ingredients 和 name 属性。

表单上的 Name 字段只需要捕获一个简单的文本值。因此 Taco 的 name 属性的类型是 String。配料复选框也有文本值,但是因为可能选择了零个或多个配料,所以它们绑定到的 ingredients 属性是一个 List<String>,它将捕获每个选择的配料。

目前,processDesign() 方法对 Taco 对象没有任何作用。事实上,它什么都做不了。没关系。在第 3 章中,将添加一些持久性逻辑,将提交的 Taco 保存到数据库中。

与 showDesignForm() 方法一样,processDesign() 通过返回一个 String 结束。与 showDesignForm() 类似,返回的值指示将显示给用户的视图。但是不同的是,从 processDesign() 返回的值的前缀是 “redirect:”,表示这是一个重定向视图。更具体地说,它表明在 processDesign() 完成之后,用户的浏览器应该被重定向到相对路径 /order/current。

这样做的想法源于,在创建了一个玉米饼之后,用户将被重定向到一个订单表单,他们可以从该表单下订单,以交付他们的玉米饼。但是还没有一个控制器来处理 /orders/current 请求。

根据现在对 @Controller、@RequestMapping 和 @GetMapping 的了解,可以轻松地创建这样的控制器。它可能类似于下面的清单。

程序清单 2.6 展现玉米饼订单表单的控制器
package tacos.web;
​
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.RequestMapping;
import lombok.extern.slf4j.Slf4j;
import tacos.Order;
​
@Slf4j
@Controller
@RequestMapping("/orders")
public class OrderController {
    @GetMapping("/current")
    public String orderForm(Model model) {
        model.addAttribute("order", new Order());
        return "orderForm";
    }
}

同样,可以使用 Lombok 的 @Slf4j 注释在运行时创建一个 SLF4J Logger 对象。稍后,将使用这个 Logger 来记录提交的订单的详细信息。

类级别的 @RequestMapping 指定该控制器中的任何请求处理方法都将处理路径以 /orders 开头的请求。当与方法级 @GetMapping 结合使用时,它指定 orderForm() 方法将处理 /orders/current 的 HTTP GET 请求。

至于 orderForm() 方法本身,它非常简单,只返回 orderForm 的逻辑视图名。在第 3 章中,一旦有了把创建的 taco 持久化到数据库的方法,将重新访问该方法并修改它,以使用 taco 对象的列表填充模型,这些对象将按顺序放置。

orderForm 视图由一个名为 orderForm.html 的 Thymeleaf 模板提供,如下面显示的。

程序清单 2.7 taco 订单表单视图
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>Taco Cloud</title>
        <link rel="stylesheet" th:href="@{/styles.css}" />
    </head>

    <body>
        <form method="POST" th:action="@{/orders}" th:object="${order}">
            <h1>Order your taco creations!</h1>
            <img th:src="@{/images/TacoCloud.png}"/>
            <a th:href="@{/design}" id="another">Design another taco</a><br/>
            <div th:if="${#fields.hasErrors()}">
                <span class="validationError">
                    Please correct the problems below and resubmit.
                </span>
            </div>
            <h3>Deliver my taco masterpieces to...</h3>

            <label for="name">Name: </label>
            <input type="text" th:field="*{name}"/>
            <br/>

            <label for="street">Street address: </label>
            <input type="text" th:field="*{street}"/>
            <br/>

            <label for="city">City: </label>
            <input type="text" th:field="*{city}"/>
            <br/>

            <label for="state">State: </label>
            <input type="text" th:field="*{state}"/>
            <br/>

            <label for="zip">Zip code: </label>
            <input type="text" th:field="*{zip}"/>
            <br/>

            <h3>Here's how I'll pay...</h3>

            <label for="ccNumber">Credit Card #: </label>
            <input type="text" th:field="*{ccNumber}"/>
            <br/>

            <label for="ccExpiration">Expiration: </label>
            <input type="text" th:field="*{ccExpiration}"/>
            <br/>

            <label for="ccCVV">CVV: </label>
            <input type="text" th:field="*{ccCVV}"/>
            <br/>

            <input type="submit" value="Submit order"/>
        </form>
    </body>
</html>

在大多数情况下,orderForm.html 视图是典型的 HTML/Thymeleaf 内容,没有什么值得注意的。但是注意,这里的 <form> 标记与程序清单 2.3 中使用的 <form> 标记不同,因为它还指定了一个表单操作。如果没有指定操作,表单将向呈现表单的相同 URL 提交 HTTP POST 请求。但是在这里,指定表单应该提交到 /orders(使用 Thymeleaf 的 @{…} 操作符作为上下文相关路径)。

因此,需要添加另外一个方法到 OrderController 类中,去处理 /orders 接口的 POST 请求。在进行到下一章之前,还没有办法将订单持久化,因此在这里简化它 —— 类似于在下一个程序清单中看到的内容。

程序清单 2.8 处理 taco 订单提交
@PostMapping
public String processOrder(Order order) {
    log.info("Order submitted: " + order);
    return "redirect:/";
}

当调用 processOrder() 方法来处理提交的订单时,它将获得一个 order 对象,其属性绑定到提交的表单字段。Order 非常像 Taco,是一个相当简单的类,它携带订单信息。

程序清单 2.9 taco 订单域对象
package tacos;
​
import javax.validation.constraints.Digits;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.CreditCardNumber;
import org.hibernate.validator.constraints.NotBlank;
import lombok.Data;
​
@Data
public class Order {
    private String name;
    private String street;
    private String city;
    private String state;
    private String zip;
    private String ccNumber;
    private String ccExpiration;
    private String ccCVV;
}

在表单中填写一些字段,然后按 Submit Order 按钮。与此同时,请密切关注应用程序日志,以查看订单信息。当我尝试它,日志条目看起来像这样(重新格式化以适应这个页面的宽度):

Order submitted: Order(name=Craig Walls,street1=1234 7th Street,
    city=Somewhere, state=Who knows?, zip=zipzap, ccNumber=Who can guess?,
ccExpiration=Some day, ccCVV=See-vee-vee)

如果仔细查看来自测试订单的日志条目,可以看到,虽然 processOrder() 方法完成了它的工作并处理了表单提交,但是它让一些错误的信息进来了。表单中的大多数字段包含的数据可能是不正确的。需要添加一些验证,以确保提供的数据至少与所需的信息类型相似。

上一页2.1.3 设计视图下一页2.3 验证表单输入

最后更新于4年前

这有帮助吗?

现在已经开发了一个 OrderController 和 order 表单视图,可以开始尝试运行了。打开浏览器访问 ,为你的玉米饼选择一些原料,然后点击 Submit Your Taco 按钮。应该会看到类似于图 2.3 所示的表单。

http://localhost:8080/design
图 2.3 taco 订单表单