该指南将引导你完成使用 Spring 创建 “Hello, World” RESTful web 服务的过程。
你将会创建
你将创建一个能在以下地址接收 HTTP GET 请求的一个服务:
http://localhost:8080/greeting
它将以 JSON 表现形式的问候语进行响应,如下清单所示:
{"id":1,"content":"Hello, World!"}
你可以在查询字符串中使用可选的 name
参数来自定义问候语,如下清单所示:
http://localhost:8080/greeting?name=User
name
参数值将覆盖 World
的默认值,并反映在响应中,如下清单所示:
{"id":1,"content":"Hello, User!"}
你将需要准备
- 大概 15 分钟左右;
- 你最喜欢的文本编辑器或集成开发环境(IDE)
- JDK 1.8 或更高版本;
- Gradle 4+ 或 Maven 3.2+
- 你还可以将代码直接导入到 IDE 中:
如何完成本指南
像大多数的 Spring 入门指南一样,你可以从头开始并完成每个步骤,也可以绕过你已经熟悉的基本设置步骤。如论哪种方式,你最终都有可以工作的代码。
- 要从头开始,移步至从 Spring Initializr 开始;
- 要跳过基础,执行以下操作:
待一切就绪后,可以检查一下 gs-rest-service/complete
目录中的代码。
从 Spring Initializr 开始
对于所有的 Spring 应用来说,你应该从 Spring Initializr 开始。Initializr 提供了一种快速的方法来提取应用程序所需的依赖,并为你完成许多设置。该示例仅需要 Spring Web 依赖项。下图显示了此示例项目的 Initializr 设置:
上图显示了选择 Maven 作为构建工具的 Initializr。你也可以使用 Gradle。它还将
com.example
和rest-service
的值分别显示为 Group 和 Artifact。在本示例的其余部分,将用到这些值。
以下清单显示了选择 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.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>rest-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rest-service</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
以下清单显示了在选择 Gradle 时创建的 build.gradle
文件:
plugins {
id 'org.springframework.boot' version '2.2.0.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
添加一个 JSON 库
因为我们将使用 JSON 发送和接收信息,所以需要一个 JSON 库。在本指南中,将会用到 Jayway JsonPath 库。
要将库包含在 Maven 构建中,请将以下依赖项添加到 pom.xml
文件。
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
以下清单显示了最终的 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.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>rest-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rest-service</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
要将库包含在 Gradle 构建中,请将以下依赖项添加到 build.gradle
文件中:
testCompile 'com.jayway.jsonpath:json-path'
以下清单显示了最终的 build.gradle
文件:
plugins {
id 'org.springframework.boot' version '2.2.0.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testCompile 'com.jayway.jsonpath:json-path'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
创建一个资源展示类
现在你已经设置了项目和构建系统,可以开始创建 Web 服务了。
在开始之前来考虑一下服务的交互。
该服务将处理 /greeting
地址的 GET
请求,查询字符串中的 name
参数为可选。GET
请求应返回 200 OK
响应,并在 HTTP 正文中显示 JSON 格式的问候语。它应该类似于以下输出:
{
"id": 1,
"content": "Hello, World!"
}
id
字段是问候语的唯一标识符,content
是问候语的文本表现。
要建模问候展示,创建资源展示类。为此,给经典的 Java 对象提供字段、构造函数还有 id
和 content
的存取方法,如下清单(来自 src/main/java/com/example/restservice/Greeting.java
)所示:
package com.example.restservice;
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
该应用使用 Jackson JSON 库自动将类型
Greeting
的实例转成 JSON。Web 启动器默认情况下将该库载入。
创建一个资源控制器
在 Spring 建立 RESTful Web 服务的方法中,HTTP 请求由控制器处理。这些组件由 @RestController
注解来标识,下面清单中所示的 GreetingController
(来自 src/main/java/com/example/restservice/GreetingController.java
)通过返回 Greeting
类的新实例来处理 /greeting
地址的 GET
请求:
package com.example.restservice;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@GetMapping("/greeting")
public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
该控制器简洁明了,但是内部却有很多事情要做。我们将其逐步分解。
@GetMapping
注解确保将访问 /greeting
地址的 HTTP GET 请求映射至 greeting()
方法。
其他 HTTP 动词也伴有注解(例如 POST 的
@PostMapping
)。这些注解都是@RequestMapping
注解的衍生物,而其可以用作同义词(例如@RequestMapping(method=GET)
)。
@RequestParam
将查询字符串参数 name
的值绑定至 greeting()
方法的 name
参数中。如果请求中不存在 name
参数,则使用 World
的 defaultValue
。
方法主体的实现是创建并返回一个新的 Greeting
对象,持有 id
和 content
属性,id 是 counter
的下一个值,而 content 则是将 name
格式化入 template
变量的结果。
传统 MVC 控制器和 RESTful Web 服务控制器之间的主要区别在于创建 HTTP 响应主体的方式。此 RESTful Web 服务控制器填充并返回 Greeting
对象,而不是依赖于视图技术来通过服务器端渲染将问候数据展现到 HTML。对象数据将以 JSON 格式直接写入 HTTP 响应。
该代码使用 Spring 的 @RestController
注解,该注解将类标记为控制器,其中每个方法均返回域对象而不是视图。这是同时包含 @Controller
以及 @ResponseBody
的简写。
Greeting
对象必须转换为 JSON。借助 Spring 的 HTTP 消息转换器支持,我们无需手动进行该操作。由于 Jackson 2 位于类路径中,因此自动选择 Spring 的 MappingJackson2HttpMessageConverter
来将 Greeting
实例转换为 JSON。
@SpringBootApplication
是一个便利的注解,它添加了以下所有内容:
@Configuration
:将类标记为应用上下文 Bean 定义的源;@EnableAutoConfiguration
:告诉 Spring Boot 根据类路径配置、其他 bean 以及各种属性的配置来添加 bean。例如,如果spring-webmvc
在类路径上,则此注释将应用标记为 Web 应用并激活关键行为,例如配置DispatcherServlet
;@ComponentScan
:告知 Spring 在com/example
包中寻找他组件、配置以及服务,让它找到控制器。
main()
方法使用 Spring Boot 的 SpringApplication.run()
方法启动应用。你是否注意到这里一行 XML 都没有?也没有 web.xml
文件。这是个百分百纯 Java 的应用,因此我们不需要处理任何管道配置或基础架构。
构建一个可执行 JAR
我们可以结合 Gradle 或 Maven 来从命令行运行该应用。我们还可以构建一个包含所有必须依赖项、类以及资源的可执行 JAR 文件,然后运行该文件。在整个开发生命周期中,跨环境等等情况下,构建可执行 JAR 可以轻松地将服务作为应用进行发布、版本化以及部署。
如果使用 Gradle,则可以借助 ./gradlew bootRun
来运行应用。或通过借助 ./gradlew build
来构建 JAR 文件,然后运行 JAR 文件,如下所示:
java -jar build/libs/gs-rest-service-0.1.0.jar
如果使用 Maven,则可以借助 ./mvnw spring-boot:run
来运行该用。或可以借助 ./mvnw clean package
来构建 JAR 文件,然后运行 JAR 文件,如下所示:
java -jar target/gs-rest-service-0.1.0.jar
我们还可以构建一个经典的 WAR 文件。
日志输出已显示,该服务应在几秒内启动并运行。
测试服务
现在服务启动了,访问 http://localhost:8080/greeting
,你将看到:
{"id":1,"content":"Hello, World!"}
通过访问 http://localhost:8080/greeting?name=User
来提供一个查询字符串参数 name。观察 content 属性的值是怎样从 Hello, World! 变成 Hello, User! 的,如下清单所示:
{"id":2,"content":"Hello, User!"}
这种变更表明 GreetingController
中安插的 @RequestParam
注解按预期工作了。name
参数的默认值为 World
,但可以通过查询字符串来显式地覆盖。
还需要观察的是 id
属性怎样从 1
变成 2
。这证明多个请求访问的的是同一个 GreetingController
实例,并且其 counter
字段正按预期在每次调用时递增。
概述
恭喜你!我们刚刚借助 Spring 开发了一款 RESTful Web 服务。
参见
以下指南也可能会有所帮助:
- 使用 REST 访问 GemFire 数据(尽请期待~)
- 使用 REST 访问 MongoDB 数据(尽请期待~)
- 使用 MySQL 访问数据(尽请期待~)
- 使用 REST 访问 JPA 数据(尽请期待~)
- 使用 REST 访问 Neo4j 数据(尽请期待~)
- 消费 RESTful Web 服务(尽请期待~)
- 使用 AngularJS 消费 RESTful Web 服务(尽请期待~)
- 使用 jQuery 消费 RESTful Web 服务(尽请期待~)
- 使用 rest.js 消费 RESTful Web 服务(尽请期待~)
- 保护 Web 应用程序(尽请期待~)
- 使用 Spring 构建 REST 服务(尽请期待~)
- React.js 和 Spring Data REST(尽请期待~)
- 使用 Spring Boot 构建应用程序(尽请期待~)
- 使用 Restdocs 创建 API 文档(尽请期待~)
- 为 RESTful Web 服务启用跨源请求(尽请期待~)
- 构建超媒体驱动的 RESTful Web 服务(尽请期待~)
- 断路器(尽请期待~)