领先一步
VMware 提供培训和认证,以加快您的进度。
了解更多要查看此代码的更新,请访问我们的 React.js 和 Spring Data REST 教程。 |
欢迎来到 Spring 社区!
这是几篇博客文章中的第一篇。在本教程中,您将学习如何快速启动并运行一个简单的 Spring Data REST 应用程序。然后,您将使用 Facebook 的 React.js 工具集在其之上构建一个简单的 UI。
您可以从这个仓库 获取代码 并跟随操作。
如果您想自己动手,请访问 http://start.spring.io 并选择以下项目
此演示使用 Java 8、Maven 项目和最新稳定版本的 Spring Boot。这将为您提供一个干净的空项目。您可以从此处添加本教程中明确显示的各种文件,也可以从上面列出的仓库中借用。
起初有数据。它是美好的。但是后来人们想要通过各种方式访问数据。多年来,人们拼凑了大量的 MVC 控制器,许多控制器都使用了 Spring 强大的 REST 支持。但是反复这样做浪费了大量时间。
Spring Data REST 解决了一些假设下这个问题的简单性
任何基于 Spring Data REST 的应用程序的基石都是领域对象。在本教程中,您将构建一个用于跟踪公司员工的应用程序。通过创建如下数据类型来启动:
@Data @Entity public class Employee {
private @Id @GeneratedValue Long id; private String firstName; private String lastName; private String description; private Employee() {} public Employee(String firstName, String lastName, String description) { this.firstName = firstName; this.lastName = lastName; this.description = description; }
}
@Entity
是一个 JPA 注解,表示整个类用于关系表中的存储。@Id
和 @GeneratedValue
是 JPA 注解,用于标记主键,并在需要时自动生成。@Data
和 @RequiredArgsConstructor
是 Project Lombok 注解,用于自动生成 getter、setter、构造函数、toString、hash、equals 等。它减少了样板代码。此实体用于跟踪员工信息。在这种情况下,是他们的姓名和职位描述。
注意
|
Spring Data REST 不仅限于 JPA。它支持许多 NoSQL 数据存储,但这里不会介绍。 |
Spring Data REST 应用程序的另一个关键部分是创建相应的仓库定义。
public interface EmployeeRepository extends CrudRepository<Employee, Long> {
}
CrudRepository
并插入了领域对象的类型及其主键。这就可以了!事实上,如果它是顶级且可见的,您甚至不必注释它。如果您使用您的 IDE 并打开 CrudRepository
,您会发现已经定义了许多预构建的方法。
注意
|
如果您愿意,可以 定义您自己的仓库。Spring Data REST 也支持这一点。 |
要使用此应用程序,您需要使用以下代码预加载一些数据:
@Component public class DatabaseLoader implements CommandLineRunner {
private final EmployeeRepository repository; @Autowired public DatabaseLoader(EmployeeRepository repository) { this.repository = repository; } @Override public void run(String... strings) throws Exception { this.repository.save(new Employee("Frodo", "Baggins", "ring bearer")); }
}
@Component
注解标记,以便它被 @SpringBootApplication
自动拾取。CommandLineRunner
,以便在创建和注册所有 bean 后运行它。EmployeeRepository
。run()
方法使用命令行参数调用,加载您的数据。Spring Data 最强大、最重要的功能之一是它能够为您编写 JPA 查询。这不仅减少了您的开发时间,还降低了错误和 bug 的风险。Spring Data 查看仓库类中方法的名称 并找出您需要的操作,包括保存、删除和查找。
这就是我们可以编写空接口并继承已构建的保存、查找和删除操作的方式。
默认情况下,Spring Data REST 在 /
处托管根链接集合。因为您将在同一路径上托管 Web UI,所以您需要更改根 URI。
spring.data.rest.base-path=/api
启动完全可操作的 REST API 所需的最后一步是使用 Spring Boot 编写一个 public static void main
@SpringBootApplication public class ReactAndSpringDataRestApplication {
public static void main(String[] args) { SpringApplication.run(ReactAndSpringDataRestApplication.class, args); }
}
假设之前的类以及您的 Maven 构建文件是从 http://start.spring.io 生成的,您现在可以通过在 IDE 中运行该 main()
方法或在命令行中键入 ./mvnw spring-boot:run
来启动它。(对于 Windows 用户,为 mvnw.bat)。
注意
|
如果您不了解 Spring Boot 及其工作原理,您应该考虑观看 Josh Long 的入门演示之一。看完了吗?继续! |
应用程序运行后,您可以使用 cURL(或您喜欢的任何其他工具)在命令行上检查内容。
$ curl localhost:8080/api { "_links" : { "employees" : { "href" : "https://127.0.0.1:8080/api/employees" }, "profile" : { "href" : "https://127.0.0.1:8080/api/profile" } } }
当您 ping 根节点时,您会收到一个包装在 HAL 格式的 JSON 文档 中的链接集合。
EmployeeRepository
接口定义的员工对象的聚合根。您可以通过导航 employees 链接来进一步深入研究此服务。
$ curl localhost:8080/api/employees { "_embedded" : { "employees" : [ { "firstName" : "Frodo", "lastName" : "Baggins", "description" : "ring bearer", "_links" : { "self" : { "href" : "https://127.0.0.1:8080/api/employees/1" } } } ] } }
在此阶段,您正在查看员工的整个集合。
除了您之前预加载的数据外,还包含一个带有 self 链接的 _links 属性。这是特定员工的规范链接。什么是规范?这意味着没有上下文。例如,可以使用类似 /api/orders/1/processor 的链接获取相同的用户,其中员工与处理特定订单相关联。在这里,没有与其他实体的关系。
重要
|
链接是 REST 的一个关键方面。它们提供了导航到相关项目的功能。它使其他方能够在您的 API 中导航,而无需每次更改时都重写内容。当客户端硬编码资源路径时,客户端更新是一个常见问题。资源重构会导致代码发生巨大变化。如果使用链接并且维护导航路由,则可以轻松灵活地进行此类调整。 |
您可以决定查看该员工。
$ curl localhost:8080/api/employees/1 { "firstName" : "Frodo", "lastName" : "Baggins", "description" : "ring bearer", "_links" : { "self" : { "href" : "https://127.0.0.1:8080/api/employees/1" } } }
这里变化很小,除了不需要 _embedded 包装器,因为只有一个领域对象。
这一切都很好,但是您可能迫不及待地想要创建一些新条目。
$ curl -X POST localhost:8080/api/employees -d '{"firstName": "Bilbo", "lastName": "Baggins", "description": "burglar"}' -H 'Content-Type:application/json' { "firstName" : "Bilbo", "lastName" : "Baggins", "description" : "burglar", "_links" : { "self" : { "href" : "https://127.0.0.1:8080/api/employees/2" } } }
您还可以执行 PUT、PATCH 和 DELETE 操作,如 此相关指南 中所示。但是让我们不要深入探讨。您已经花了太多时间手动与此 REST 服务交互。您不想构建一个漂亮的 UI 吗?
Spring Boot 使启动自定义网页变得非常简单。首先,您需要一个 Spring MVC 控制器。
@Controller public class HomeController {
@RequestMapping(value = "/") public String index() { return "index"; }
}
@Controller
将此类标记为 Spring MVC 控制器。@RequestMapping
将 index()
方法标记为支持 /
路由。index
作为模板的名称,Spring Boot 的自动配置视图解析器会将其映射到 src/main/resources/templates/index.html
。您正在使用 Thymeleaf,尽管您不会真正使用它的许多功能。
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head lang="en"> <meta charset="UTF-8"/> <title>ReactJS + Spring Data REST</title> <script async="" data-main="/run.js" src="/bower_components/requirejs/require.js"></script> <link rel="stylesheet" href="/main.css" /> </head> <body>
<div id="react"></div>
</body> </html>
此模板中的关键部分是中间的 <div id="react"></div>
组件。您将在这里指导 React 插入渲染的输出。
本教程不会详细介绍如何使用 require.js 加载 JavaScript 模块。但是由于使用了 frontend-maven-plugin,您**不必**安装任何 node.js 工具来构建和运行代码。
将使用以下 JavaScript 模块
有了所有这些,您可以专注于在 DOM 加载后获取的 React 部分。它被分解成如下所示的部分:
由于您使用 require.js 加载内容,因此请继续获取所需的模块
var React = require('react');
var client = require('./client');
React
是 Facebook 用于构建此应用程序的主要库。client
是自定义代码,它配置 rest.js 以包括对 HAL、URI 模板和其他内容的支持。它还将默认的 Accept 请求标头设置为 application/hal+json。您可以在 此处阅读代码。React 基于定义组件。通常,一个组件可以在父子关系中保存另一个组件的多个实例。这个概念很容易扩展到多个层。
为了开始,拥有所有组件的顶级容器非常方便。(随着您在本系列中扩展代码,这一点将变得更加明显。)现在,您只有员工列表。但是您以后可能需要一些其他相关的组件,因此让我们从这里开始:
var App = React.createClass({
getInitialState: function () {
return ({employees: []});
},
componentDidMount: function () {
client({method: 'GET', path: '/api/employees'}).done(response => {
this.setState({employees: response.entity._embedded.employees});
});
},
render: function () {
return (
<EmployeeList employees={this.state.employees}/>
)
}
})
React.createClass({…})
是创建 React 组件的方法。getInitialState
是初始化状态数据(预期会变化的数据)的 API。componentDidMount
是在 React 在 DOM 中渲染组件后调用的 API。render
是在屏幕上“绘制”组件的 API。
注意
|
在 React 中,大写是命名组件的约定。 |
在 App 组件中,员工数组从 Spring Data REST 后端获取并存储在此组件的 state 数据中。
React 组件拥有两种类型的数据:状态和属性。
状态是指组件自身需要处理的数据。它也是可以波动和变化的数据。要读取状态,可以使用this.state
。要更新状态,可以使用this.setState()
。每次调用this.setState()
时,React都会更新状态,计算先前状态和新状态之间的差异,并将一组更改注入页面上的 DOM。这使得 UI 更新快速而高效。
常见的约定是在getInitialState
中将所有属性初始化为空。然后,可以使用componentDidMount
从服务器查找数据并填充属性。从那时起,更新可以由用户操作或其他事件驱动。
属性包含传递到组件的数据。属性不会更改,而是固定值。要设置属性,在创建新组件时将它们分配给属性,您很快就会看到。
警告
|
JavaScript 不会像其他语言那样锁定数据结构。您可以尝试通过赋值来破坏属性,但这不适用于 React 的差异引擎,应避免。 |
在此代码中,该函数通过client
加载数据,client
是符合 Promise 规范的rest.js 实例。从/api/employees
检索完成之后,它会调用done()
内部的函数,并根据其 HAL 文档(response.entity._embedded.employees
)设置状态。您可能还记得前面提到的curl /api/employees
的结构,并了解它如何映射到此结构。
更新状态后,框架会调用render()
函数。员工状态数据作为输入参数包含在<EmployeeList />
React 组件的创建中。
以下是EmployeeList
的定义。
var EmployeeList = React.createClass({
render: function () {
var employees = this.props.employees.map(employee =>
<Employee key={employee._links.self.href} employee={employee}/>
);
return (
<table>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Description</th>
</tr>
{employees}
</table>
)
}
})
使用 JavaScript 的 map 函数,this.props.employees
从员工记录数组转换为<Element />
React 组件数组(您稍后会看到)。
<Employee key={employee._links.self.href} data={employee} />
这显示了一个新创建的 React 组件(注意大写格式),以及两个属性:key 和 data。这些属性的值分别来自employee._links.self.href
和employee
。
重要
|
在使用 Spring Data REST 时,self 链接是给定资源的键。React 需要子节点的唯一标识符,而_links.self.href 非常适合。 |
最后,您将一个围绕使用映射构建的employees
数组的 HTML 表格返回。
<table>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Description</th>
</tr>
{employees}
</table>
这种状态、属性和 HTML 的简单布局显示了 React 如何让您声明式地创建简单易懂的组件。
这段代码是否同时包含 HTML 和 JavaScript?是的。如果您注意到文件扩展名,这就是JSX。没有必要使用它。React 可以使用纯 JavaScript 编写,但 JSX 语法非常简洁。
担心将逻辑与结构混合?React 的 API 鼓励将良好的声明式结构与状态和属性结合起来。React 不鼓励混合一堆不相关的 JavaScript 和 HTML,而是鼓励构建具有少量相关状态和属性的小型组件,这些组件可以很好地协同工作。它允许您查看单个组件并理解其设计。然后,可以轻松地将它们组合在一起以构建更大的结构。
接下来,您需要实际定义<Employee />
是什么。
var Employee = React.createClass({
render: function () {
return (
<tr>
<td>{this.props.employee.firstName}</td>
<td>{this.props.employee.lastName}</td>
<td>{this.props.employee.description}</td>
</tr>
)
}
})
此组件非常简单。它只有一个围绕员工三个属性的 HTML 表格行。属性本身是this.props.employee
。注意,传入 JavaScript 对象如何使传递从服务器获取的数据变得容易?
由于此组件不管理任何状态,也不处理用户输入,因此无需执行其他操作。这可能会诱使您将其塞入上面的<EmployeeList />
中。不要这样做!相反,将应用程序拆分成每个都执行一项工作的较小组件,将使您将来更容易构建功能。
最后一步是渲染整个内容。
React.render(
<App />,
document.getElementById('react')
)
React.render()
接受两个参数:您定义的 React 组件以及要将其注入到的 DOM 节点。还记得您之前从 HTML 页面看到的<div id="react"></div>
项吗?这就是它被拾取并插入的位置。
准备好所有这些后,重新运行应用程序(./mvnw spring-boot:run
)并访问https://127.0.0.1:8080。
您可以看到系统加载的初始员工。
还记得使用 cURL 创建新条目吗?再次执行此操作。
curl -X POST localhost:8080/api/employees -d '{"firstName": "Bilbo", "lastName": "Baggins", "description": "burglar"}' -H 'Content-Type:application/json'
刷新浏览器,您应该会看到新条目。
现在,您可以看到它们都列在网站上。
在本节中
问题?
/api/employees
获取数据。这些问题我们将在下一节中解决。在此之前,祝您编码愉快!