Spring Boot 与 Docker
观察 GraphQL 的实际运行

本指南将引导您完成创建一个应用程序的过程,该应用程序通过一个基于超媒体的 RESTful 前端访问基于文档的数据。

你将构建什么

您将构建一个 Spring 应用程序,该应用程序允许您使用 Spring Data REST 创建和检索存储在 MongoDB NoSQL 数据库中的 Person 对象。Spring Data REST 结合了 Spring HATEOASSpring Data MongoDB 的特性,并自动将它们整合在一起。

Spring Data REST 还支持 Spring Data JPASpring Data GemfireSpring Data Neo4j 作为后端数据存储,但这些内容不在本指南的范围内。

所需条件

如何完成本指南

与大多数 Spring 入门指南一样,您可以从头开始并完成每个步骤,或者可以跳过您已经熟悉的基本设置步骤。无论哪种方式,最终您都将获得可运行的代码。

从头开始,请继续阅读从 Spring Initializr 开始

跳过基础知识,请执行以下操作:

当您完成后,您可以将您的结果与 gs-accessing-mongodb-data-rest/complete 中的代码进行对比。

从 Spring Initializr 开始

您可以使用这个预先初始化的项目,并点击“生成”以下载一个 ZIP 文件。该项目已配置为适配本教程中的示例。

要手动初始化项目:

  1. 打开 https://start.spring.io。该服务会引入应用程序所需的所有依赖项,并为您完成大部分设置工作。

  2. 选择 Gradle 或 Maven 以及您想要使用的语言。本指南假设您选择了 Java。

  3. 点击 Dependencies,然后选择 Rest RepositoriesSpring Data MongoDB

  4. 点击 Generate

  5. 下载生成的 ZIP 文件,这是一个根据您的选择配置好的 Web 应用程序的压缩包。

如果您的 IDE 集成了 Spring Initializr,您可以直接在 IDE 中完成这一过程。

您也可以从 Github 上 fork 该项目,并在您的 IDE 或其他编辑器中打开它。

安装并启动 MongoDB

为了使本指南能够正常工作,您必须启动一个本地的 MongoDB 服务器。

在安装了 Homebrew 的 Mac OS X 机器上,运行以下命令:

brew install mongodb

您可以在https://docs.mongodb.org/manual/installation/找到更多安装选项。

安装完 MongoDB 后,您需要启动 mongo 守护进程。在 Mac 上,您可以使用以下命令:

$ mongod
all output going to: /usr/local/var/log/mongodb/mongo.log

您可以通过在另一个终端窗口中运行 mongo 命令来启动 MongoDB 客户端。

创建一个领域对象

创建一个新的领域对象来表示一个人,如下示例所示(位于 src/main/java/com/example/accessingmongodbdatarest/Person.java):

package com.example.accessingmongodbdatarest;

import org.springframework.data.annotation.Id;

public class Person {

  @Id private String id;

  private String firstName;
  private String lastName;

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

Person 对象包含名字和姓氏。(还有一个 ID 对象,它被配置为自动生成,因此无需处理它。)

创建 Person 仓库

接下来,您需要创建一个简单的仓库,如下面的代码清单所示(位于 src/main/java/com/example/accessingmongodbdatarest/PersonRepository.java 中):

package com.example.accessingmongodbdatarest;

import java.util.List;

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends MongoRepository<Person, String> {

  List<Person> findByLastName(@Param("name") String name);

}

该仓库是一个接口,允许您执行涉及 Person 对象的各种操作。它通过扩展 MongoRepository 来获取这些操作,而 MongoRepository 又扩展了 Spring Data Commons 中定义的 PagingAndSortingRepository 接口。

在运行时,Spring Data REST 会自动创建该接口的实现。然后它使用 @RepositoryRestResource 注解来引导 Spring MVC 在 /people 路径下创建 RESTful 端点。

@RepositoryRestResource 并不是导出仓库所必需的。它仅用于更改导出细节,例如使用 /people 而不是默认的 /persons

在这里,您还定义了一个自定义查询,用于根据 lastName 值检索 Person 对象列表。您可以在本指南的后续部分看到如何调用它。

默认情况下,Spring Boot 会尝试连接到本地托管的 MongoDB 实例。阅读参考文档了解如何将您的应用程序指向托管在其他地方的 MongoDB 实例。

@SpringBootApplication 是一个便捷的注解,它包含了以下所有内容:

  • @Configuration: 将该类标记为应用程序上下文的 bean 定义来源。

  • @EnableAutoConfiguration: 告诉 Spring Boot 根据类路径设置、其他 bean 和各种属性设置开始添加 bean。例如,如果 spring-webmvc 在类路径上,此注解会将应用程序标记为 Web 应用程序,并激活关键行为,例如设置 DispatcherServlet

  • @ComponentScan: 告诉 Spring 在 com/example 包中查找其他组件、配置和服务,使其能够找到控制器。

main() 方法使用 Spring Boot 的 SpringApplication.run() 方法来启动应用程序。您是否注意到没有一行 XML 代码?也没有 web.xml 文件。这个 Web 应用程序是 100% 纯 Java 的,您无需处理任何配置或基础设施的设置。

构建可执行的 JAR

您可以使用 Gradle 或 Maven 从命令行运行应用程序。您还可以构建一个包含所有必要依赖项、类和资源的单一可执行 JAR 文件并运行它。构建可执行 JAR 文件可以方便地在整个开发生命周期中、跨不同环境等场景下打包、版本化和部署该服务。

如果您使用 Gradle,可以通过 ./gradlew bootRun 来运行应用程序。或者,您可以使用 ./gradlew build 构建 JAR 文件,然后运行该 JAR 文件,如下所示:

java -jar build/libs/gs-accessing-mongodb-data-rest-0.1.0.jar

如果您使用 Maven,您可以通过 ./mvnw spring-boot:run 来运行应用程序。或者,您也可以使用 ./mvnw clean package 构建 JAR 文件,然后运行该 JAR 文件,如下所示:

java -jar target/gs-accessing-mongodb-data-rest-0.1.0.jar

这里描述的步骤创建了一个可运行的 JAR 文件。您也可以构建一个经典的 WAR 文件

日志输出已显示。服务应该会在几秒钟内启动并运行。

测试应用程序

既然应用程序已经在运行,您可以对其进行测试。您可以使用任何您喜欢的 REST 客户端。以下示例使用了 *nix 工具 curl

首先,您需要查看顶层服务,如下例所示:

$ curl http://localhost:8080
{
  "_links" : {
    "people" : {
      "href" : "http://localhost:8080/people{?page,size,sort}",
      "templated" : true
    }
  }
}

前面的示例初步展示了该服务器所提供的功能。在 http://localhost:8080/people 地址下有一个 people 链接。它提供了一些选项,例如 ?page?size?sort

Spring Data REST 使用 HAL 格式 作为 JSON 输出。它非常灵活,并提供了一种便捷的方式来为所提供的数据附加链接。

当您使用人员链接时,您会看到数据库中的 Person 记录(目前没有):

$ curl http://localhost:8080/people
{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "http://localhost:8080/people/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}

当前没有任何元素,因此也没有页面。是时候创建一个新的 Person 了!

如果您多次运行本指南,可能会有残留数据。如果您需要重新开始,请参考 MongoDB shell 快速参考 中查找和删除数据库的命令。

以下命令创建一个名为“Frodo Baggins”的人物:

$ curl -i -X POST -H "Content-Type:application/json" -d "{  \"firstName\" : \"Frodo\",  \"lastName\" : \"Baggins\" }" http://localhost:8080/people
HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
Location: http://localhost:8080/people/53149b8e3004990b1af9f229
Content-Length: 0
Date: Mon, 03 Mar 2014 15:08:46 GMT
  • -i: 确保您可以看到包括头部信息在内的响应消息。新创建的 Person 的 URI 会显示出来。

  • -X POST: 表示这是一个用于创建新条目的 POST 请求。

  • -H "Content-Type:application/json": 设置内容类型,以便应用程序知道有效载荷包含一个 JSON 对象。

  • -d '{ "firstName" : "Frodo", "lastName" : "Baggins" }': 这是发送的数据。

注意前面的 POST 操作包含了一个 Location 头信息。这个头信息包含了新创建资源的 URI。Spring Data REST 还提供了两个方法(RepositoryRestConfiguration.setReturnBodyOnCreate(…)setReturnBodyOnUpdate(…)),您可以使用这些方法来配置框架,使其立即返回刚刚创建或更新的资源的表示。

通过这个,您可以查询所有的人员,如下例所示:

$ curl http://localhost:8080/people
{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "http://localhost:8080/people/search"
    }
  },
  "_embedded" : {
    "persons" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/people/53149b8e3004990b1af9f229"
        }
      }
    } ]
  },
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}

persons 对象包含一个带有 Frodo 的列表。请注意它如何包含一个 self 链接。Spring Data REST 还使用 Evo Inflector 来将实体名称复数化以用于分组。

您可以直接查询单个记录,如下例所示:

$ curl http://localhost:8080/people/53149b8e3004990b1af9f229
{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people/53149b8e3004990b1af9f229"
    }
  }
}

这看似纯粹是基于网页的,但实际上,它正在与您启动的 MongoDB 数据库进行通信。

在本指南中,只有一个领域对象。在一个更复杂的系统中,领域对象之间相互关联,Spring Data REST 会呈现额外的链接,以帮助导航到相关的记录。

查找所有自定义查询,如下例所示:

$ curl http://localhost:8080/people/search
{
  "_links" : {
    "findByLastName" : {
      "href" : "http://localhost:8080/people/search/findByLastName{?name}",
      "templated" : true
    }
  }
}

您可以看到查询的 URL,其中包括 HTTP 查询参数 name。这与接口中嵌入的 @Param("name") 注解相匹配。

要使用 findByLastName 查询,请运行以下命令:

$ curl http://localhost:8080/people/search/findByLastName?name=Baggins
{
  "_embedded" : {
    "persons" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/people/53149b8e3004990b1af9f229"
        }
      }
    } ]
  }
}

因为您在代码中将其定义为返回 List<Person>,所以它会返回所有结果。如果您将其定义为仅返回 Person,则会选择其中一个 Person 对象返回。由于这可能无法预测,因此对于可能返回多个条目的查询,您可能不希望这样做。

您还可以发出 PUTPATCHDELETE REST 调用来分别替换、更新或删除现有记录。以下示例使用 PUT 调用:

$ curl -X PUT -H "Content-Type:application/json" -d "{ \"firstName\": \"Bilbo\", \"lastName\": \"Baggins\" }" http://localhost:8080/people/53149b8e3004990b1af9f229
$ curl http://localhost:8080/people/53149b8e3004990b1af9f229
{
  "firstName" : "Bilbo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people/53149b8e3004990b1af9f229"
    }
  }
}

以下示例使用了 PATCH 调用:

$ curl -X PATCH -H "Content-Type:application/json" -d "{ \"firstName\": \"Bilbo Jr.\" }" http://localhost:8080/people/53149b8e3004990b1af9f229
$ curl http://localhost:8080/people/53149b8e3004990b1af9f229
{
  "firstName" : "Bilbo Jr.",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people/53149b8e3004990b1af9f229"
    }
  }
}

PUT 会替换整个记录。未提供的字段将被替换为 null。您可以使用 PATCH 来更新部分项。

您也可以删除记录,如下例所示:

$ curl -X DELETE http://localhost:8080/people/53149b8e3004990b1af9f229
$ curl http://localhost:8080/people
{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "http://localhost:8080/people/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}

这种超媒体驱动的接口的一个便利之处在于,您可以通过使用 curl(或任何您喜欢的 REST 客户端)来发现所有的 RESTful 端点。无需与客户交换正式的合同或接口文档。

总结

恭喜!您刚刚开发了一个具有基于超媒体的 REST 前端和基于 MongoDB 后端的应用程序。

另请参阅

以下指南可能也会有所帮助:

本页目录