当前位置: 首页 > 工具软件 > GemFire > 使用案例 >

SpringBoot 2 REST 访问 Pivotal GemFire

司空胤
2023-12-01

开篇词

该指南将引导你创建通过基于超媒体的 RESTful 前端访问 Pivotal GemFire 的数据。
 

你将创建的应用

我们将构建一个 Spring 应用,该应用允许我们使用 Spring Data REST 创建和检索存储在 Pivotal GemFire 内存数据网格中的 Person 对象。Spring Data REST 具有 Spring HATEOASSpring Data for Pivotal GemFire 的功能,并将它们自动结合在一起。

Spring Data REST 还支持将 Spring Data JPASpring Data MongoDBSpring Data Neo4j 作为后端数据存储,但这些都不属于该指南的一部分。

有关 Pivotal GemFire 概念和从 Pivotal GemFire 访问数据的更多常规知识,请通读使用 Pivotal GemFire 访问数据的指南。

 

你将需要的工具

如何完成这个指南

像大多数的 Spring 入门指南一样,你可以从头开始并完成每个步骤,也可以绕过你已经熟悉的基本设置步骤。如论哪种方式,你最终都有可以工作的代码。

  • 要从头开始,移步至用 Gradle 来构建
  • 要跳过基础,执行以下操作:
    • 下载并解压缩该指南将用到的源代码,或借助 Git 来对其进行克隆操作:git clone https://github.com/spring-guides/gs-accessing-gemfire-data-rest.git
    • 切换至 gs-accessing-gemfire-data-rest/initial 目录;
    • 跳转至该指南的创建域对象

待一切就绪后,可以检查一下 gs-accessing-gemfire-data-rest/complete 目录中的代码。
 

用 Gradle 来构建

首先,我们设置一个基本的构建脚本。在使用 Spring 构建应用时可以使用任何喜欢的构建系统,但此处包含使用 GradleMaven 所需的代码。如果你都不熟悉,请参阅使用 Gradle 构建 Java 项目使用 Maven 构建 Java 项目

创建目录结构

在我们选择的项目目录中,创建以下自目录结构;例如,在 *nix 系统上使用 mkdir -p src/main/java/hello

└── src
    └── main
        └── java
            └── hello

创建 Gradle 构建文件

以下是初始 Gradle 构建文件。
build.gradle

buildscript {
    repositories {
        mavenCentral()
        maven { url "https://repo.spring.io/libs-release" }
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:2.2.1.RELEASE")
    }
}

plugins {
    id "io.spring.dependency-management" version "1.0.5.RELEASE"
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'

sourceCompatibility = 1.8
targetCompatibility = 1.8

bootJar {
    baseName = 'gs-accessing-gemfire-data-rest'
    version =  '0.1.0'
}

repositories {
    mavenCentral()
    maven { url "https://repo.spring.io/libs-release" }
}

dependencies {

    compile("org.springframework.boot:spring-boot-starter-data-rest") {
        exclude group: "org.springframework.boot", module: "spring-boot-starter-logging"
    }
    compile("org.springframework.data:spring-data-gemfire")
    compile("org.projectlombok:lombok")

    runtime("org.springframework.shell:spring-shell:1.2.0.RELEASE")

    testCompile("org.springframework.boot:spring-boot-starter-test") {
        exclude group: "org.springframework.boot", module: "spring-boot-starter-logging"
    }
}

Spring Boot gradle 插件提供了许多方便的功能:

  • 它收集类路径上的所有 jar,并构建一个可运行的单个超级 jar,这使执行和传输服务更加方便;
  • 它搜索 public static void main() 方法并将其标记为可运行类;
  • 它提供了一个内置的依赖解析器,用于设置版本号以及匹配 Spring Boot 依赖。我们可以覆盖所需的任何版本,但默认为 Boot 选择的一组版本。
     

用 Maven 来构建

首先,我们搭建一个基本的构建脚本。使用 Spring 构建应用时,可以使用任何喜欢的构建系统,但是此处包含了使用 Maven 所需的弟阿玛。如果你不熟悉 Maven,请参阅使用 Maven 构建 Java 项目

创建目录结构

在我们选择的项目目录中,创建以下自目录结构;例如,在 *nix 系统上使用 mkdir -p src/main/java/hello

└── src
    └── main
        └── java
            └── hello

创建 Maven 构建文件

以下是初始 Maven 构建文件。
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
    </parent>

    <groupId>org.springframework</groupId>
    <artifactId>gs-accessing-gemfire-data-rest</artifactId>
    <version>0.1.0</version>

    <properties>
        <spring-shell.version>1.2.0.RELEASE</spring-shell.version>
    </properties>

    <repositories>
        <repository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-gemfire</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.shell</groupId>
            <artifactId>spring-shell</artifactId>
            <version>${spring-shell.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Spring Boot Maven 插件提供了许多方便的功能:

  • 它收集类路径上的所有 jar,并构建一个可运行的单个超级 jar,这使执行和传输服务更加方便;
  • 它搜索 public static void main() 方法并将其标记为可运行类;
  • 它提供了一个内置的依赖解析器,用于设置版本号以及匹配 Spring Boot 依赖。我们可以覆盖所需的任何版本,但默认为 Boot 选择的一组版本。
     

用 IDE 来构建

使用 Maven 构建 Java 项目

创建一个新的域对象来展示一个人。

src/main/java/hello/Person.java

package hello;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.gemfire.mapping.annotation.Region;

import lombok.Data;

@Data
@Region("People")
public class Person {

  private static AtomicLong COUNTER = new AtomicLong(0L);

  @Id
  private Long id;

  private String firstName;
  private String lastName;

  @PersistenceConstructor
  public Person() {
    this.id = COUNTER.incrementAndGet();
  }
}

Person 对象有一个名字和一个姓氏。Pivotal GemFire 域对象需要一个 ID,因此每次创建 Person 对象时都会使用 AtomicLong 进行递增。
 

创建一个 Person 存储库

接下来,我们需要创建一个简单的存储库,如以下示例所示(在 src/main/java/hello/PersonRepository.java 中):

package hello;

import java.util.List;

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

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

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

}

该存储库是一个接口,将允许我们执行涉及 Person 对象的各种数据访问操作(例如基本 CRUD 和简单查询)。它通过扩展 CrudRepository 获得这些操作。

在运行时,用于 Pivotal GemFire 的 Spring Data 将自动创建该接口的实现。然后,Spring Data REST 将使用 @RepositoryRestResource 注解来指示 Spring MVC 在 /people 处创建基于 REST 的端点。

导出存储库不需要 @RepositoryRestResource。它仅用于更改导出详细信息,例如使用 /people 代替 /persons 默认值。

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

使应用可执行

尽管可以将服务打包为传统的 WAR 文件以部署到外部应用服务器,但是下面演示的更简单的方法创建了一个独立的应用。我们将所有内容打包在一个可执行的 JAR 文件中,由一个经典的 Java main() 方法驱动。在该过程中,我们将使用 Spring 的支持将 Tomcat servlet 容器作为 HTTP 运行时嵌入,而不是部署到外部 servlet 容器。

src/main/java/hello/Application.java

package hello;

import org.apache.geode.cache.client.ClientRegionShortcut;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions;
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;

@SpringBootApplication
@ClientCacheApplication(name = "AccessingGemFireDataRestApplication", logLevel = "error")
@EnableEntityDefinedRegions(basePackageClasses = Person.class,
  clientRegionShortcut = ClientRegionShortcut.LOCAL)
@EnableGemfireRepositories
@SuppressWarnings("unused")
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

@SpringBootApplication 是一个便利的注解,它添加了以下所有内容:

  • @Configuration:将类标记为应用上下文 Bean 定义的源;
  • @EnableAutoConfiguration:告诉 Spring Boot 根据类路径配置、其他 bean 以及各种属性的配置来添加 bean。
  • @ComponentScan:告知 Spring 在 hello 包中寻找他组件、配置以及服务。

main() 方法使用 Spring Boot 的 SpringApplication.run() 方法启动应用。

@EnableGemfireRepositories 注解可激活 Spring Data for Pivotal GemFire 存储库。Pivotal GemFire 的 Spring 数据将创建 PersonRepository 接口的具体实现,并将其配置为与 Pivotal GemFire 嵌入式实例进行通信。

构建可执行 JAR

我们可以结合 Gradle 或 Maven 来从命令行运行该应用。我们还可以构建一个包含所有必须依赖项、类以及资源的可执行 JAR 文件,然后运行该文件。在整个开发生命周期中,跨环境等等情况下,构建可执行 JAR 可以轻松地将服务作为应用进行发布、版本化以及部署。

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

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

如果使用 Maven,则可以借助 ./mvnw spring-boot:run 来运行该用。或可以借助 ./mvnw clean package 来构建 JAR 文件,然后运行 JAR 文件,如下所示:

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

我们还可以将 JAR 应用转换成 WAR 应用

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

测试应用

现在该应用正在运行,我们可以对其进行测试。我们可以使用任何喜欢的 REST 客户端。以下示例使用 curl

首先,我们要查看顶级服务。以下示例(带有输出)显示了如何执行该操作:

curl http://localhost:8080
{
  "_links" : {
    "people" : {
      "href" : "http://localhost:8080/people"
    }
  }
}

这里,我们可以初步了解该服务器所提供的功能。在 http://localhost:8080/people 上有一个 people 链接。Spring Data for Pivotal GemFire 不像其他 Spring Data REST 指南那样支持分页,因此没有多于的导航连接。

Spring Data REST 使用 HAL 格式进行 JSON 输出。它非常灵活,并提供了一种便捷的方式来提供与所提供数据相邻的链接。

使用 people 链接时,我们会在数据库中看到 Person 的记录(目前没有):

curl http://localhost:8080/people
{
  "_links" : {
    "search" : {
      "href" : "http://localhost:8080/people/search"
    }
  }
}

当前没有任何元素,所以是时候创建一个新的 Person 了!

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/1
Content-Length: 0
Date: Wed, 05 Mar 2014 20:16:11 GMT
  • -i:确保我们可以看到包括标题的响应消息。显示新创建的 Person 的 URI;
  • -X POST 表示这是用于创建新条目的 POST
  • -H "Content-Type:application/json":设置内容类型,以便应用知道有效负载包含 JSON 对象;
  • -d'{"firstName: "Frodo", "lastName": "Barggins""}':被发送的数据;

请注意,对 POST 操作的响应如何包含 Location 标头。它包含新创建资源的 URI。Spring Data REST 还具有两个方法(RepositoryRestConfiguration.setReturnBodyOnCreate(…)setReturnBodyOnUpdate(…)),我们可以使用它们来配置框架以立即返回刚刚创建的资源的表示形式。
RepositoryRestConfiguration.setReturnBodyForPutAndPost(…) 是一种启用创建和更新操作的表示形式响应的快捷方式。

我们可以查询所有人,如以下示例所示:

curl http://localhost:8080/people
{
  "_links" : {
    "search" : {
      "href" : "http://localhost:8080/people/search"
    }
  },
  "_embedded" : {
    "persons" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/people/1"
        }
      }
    } ]
  }
}

people 对象包含一个包含 Frodo 的列表。注意它是如何包含一个 self 链接的。Spring Data REST 还使用 Evo Inflector 来对实体名称进行复数以进行分组。

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

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

这似乎完全是基于 Web 的,但是后台有一个嵌入式的 Pivotal GemFire 数据库。

在该指南中,只有一个域对象。在域对象相互关联的更复杂的系统中,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/1"
        }
      }
    } ]
  }
}

因为我们已将其定义为在代码中返回 List<Person>,所以它将返回所有结果。如果已将其定义为仅返回 Person,则它将选择要返回的 Person 对象之一。由于这可能是不可预测的,因此对于可能返回多个条目的查询,我们可能不想这样做。

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

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

下面示例使用 PATCH 调用:

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

PUT 替换整个记录。未提供的字段将替换为 null。我们可以使用 PATCH 更新项的子集。

curl -X DELETE http://localhost:8080/people/1
curl http://localhost:8080/people
{
  "_links" : {
    "search" : {
      "href" : "http://localhost:8080/people/search"
    }
  }
}

超媒体驱动接口的一个方便方面是,我们可以使用 curl(或您喜欢的任何REST客户端)来发现所有 RESTful 端点。我们无需与客户交换正式契约或接口文件。
 

概述

恭喜你!我们已经开发了具有基于超媒体的 RESTful 前端和基于 Pivotal GemFire 后端的应用。
 

参见

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

想看指南的其他内容?请访问该指南的所属专栏:《Spring 官方指南

 类似资料: