在项目初始化的部分有删减
本文将带你使用 Spring 开发一个 “Hello, World” RESTful Web 服务。
最终目标
本文最终的目标,是带你开发一个能够在 http://localhost:8080/greeting
接收 HTTP GET 请求的服务,该接口将以 JSON 格式返回一个问候信息,如:
{"id":1,"content":"Hello, World!"}
该接口还能够通过 url 中的查询字符串( query string )接收一个可选的 name
参数,请求如:
http://localhost:8080/greeting?name=User
这里的 name
参数值将覆盖默认的 World
,此时的接口响应如下:
{"id":1,"content":"Hello, User!"}
如何完成该教程
和其他的 Spring 教程一样,你可以从零开始建立项目,也可以直接使用已有的代码模板。
-
若要从零开始,请阅读从零开始建立项目
-
若要跳过创建项目的部分,请进行如下步骤:
- 下载本教程的源代码:github.com/spring-guid…
- 进入
gs-rest-service/initial
目录 - 直接阅读本文下面的【创建资源表示类】部分
-
当完成代码后,可以将自己的代码和
gs-rest-service/complete
中的代码进行比较,检查自己的成果。
创建资源表示类(Resource Representation Class)
首先,我们来思考一下服务交互的过程:
服务首先应该接收 /greeting
url 上面的 GET
请求,并处理可选的 name
参数。GET
请求应该返回一个 200 OK
的响应并在响应体( body )部分加上最终的 JSON ,样子如下:
{
"id":1,
"content":"Hello, World!"
}
id
字段是每个问候响应的唯一标识符,content
是问候文本。
我们需要建立一个资源表示类( resource representation class )作为问候响应的数据模型。这个类是一个包含数据(表示 id
、content
的变量其访问他们的方法)、构造器的普通 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。Jackson 默认包含在 Web 初始模板中
创建资源控制器(Resource Controller)
在 Spring 的 RESTful Web 服务中,HTTP 请求都是由控制器来管理的。这些控制器部件都被 @RestController
注解修饰,下面代码中的 GreetingController
会在 /greeting
接收 GET
请求,并返回一个新创建的 Greeting
类实例:
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
的请求映射到 greeting()
方法。
此外还有几个处理其他 HTTP 方法的注解(例如给 POST 用的
@PostMapping
),它们都派生自@RequestMapping
注解。也可以通过@RequestMapping(method=GET)
的方法来直接使用 RequestMapping
@RequestParam
能够将请求中的查询字符串参数 name
转为 greeting()
方法的 name
参数。如果请求中不含 name
参数,则将使用defaultValue
的值 World
作为默认参数。
方法体中创建并返回一个 Greeting
实例,并设置其成员变量 counter
(通过 incrementAndGet()
方法) 和 content
(通过对 template
用 name
参数来格式化)的值。
传统 MVC 控制器和 RESTful web 服务控制器的关键差异在于他们创建 HTTP 响应体的方式:传统 MVC 会使用服务端渲染(SSR)的方式,将问候数据直接转为 HTML 。而这里的 RESTful web 服务控制器会生成并返回一个 Greeting
对象。该对象的数据会被直接以 JSON 的方式返回给请求者。
这段代码最外面的 @RestController
注解将该类标记为一个控制器,其中的所有方法都会返回一个域对象(domain object),而不会返回一个视图( view )。@RestController
是包含了 @Controller
和 @ResponseBody
的简写。
Greeting
对象需要被转为 JSON 才能返回给请求者,该工作由 Spring HTTP 消息转换支持来实现,因此不需要我们对其进行手动转换。因为 classpath 中包含 Jackson 2
,因此 Spring 会自动选择 MappingJackson2HttpMessageConverter
来将 Greeting
实例转为 JSON 文本。
@SpringBootApplication
是下面三个注解的简写:
@Configuration
: 将类标记为应用上下文( application context )bean 的定义;@EnableAutoConfiguration
:让 Spring Boot 根据 classpath 设置、其他的属性设置( various property settings )等来添加 bean 。例如:如果 classpath 中有一个spring-webmvc
,那么该标记就会将应用程序标记为一个 web 应用,并启用相关的行为——比如启动一个DispatcherServlet
;@ComponentScan
:让 Spring 寻找在com/example
包下面的其他组件、配置、服务等,并找到所有的控制器。
整个程序的 main()
方法使用 Spring Boot 的 SpringApplication.run()
方法来启动应用程序。在上面的全过程中都不需要我们处理 XML ,也没有 web.xml
文件。该应用是 100% 的纯 Java 程序,不需要配置任何的管道和基础设施( plumbing or infrastructure )。
创建可执行 JAR
写好的程序可以通过命令行或使用 Gradle / Maven 来运行。此外我们也可以将应用构建为一个可执行的 JAR 文件,该文件会包含所有必须的依赖、类和运行所需的资源。构建可执行 JAR 能够方便发布、管理版本以及在不同的环境中部署等。
如果你用 Gradle 管理项目,那么可以使用 ./gradlew bootRun
来运行应用程序。如果想将程序构建为 JAR ,则可以使用 ./gradlew build
构建然后运行,运行方法如下:
java -jar build/libs/gs-rest-service-0.1.0.jar
如果你用 Maven 管理项目,可以通过 ./mvnw spring-boot:run
运行程序。如果要构建为 JAR ,使用命令 ./mvnw clean package
并通过和上面一样的方式运行 jar 文件。
测试服务
程序运行起来之后,使用浏览器访问 http://localhost:8080/greeting
,可以看到
{"id":1,"content":"Hello, World!"}
也可以在 url 中加上查询参数:http://localhost:8080/greeting?name=User
,显示如下:
{"id":2,"content":"Hello, User!"}
这说明 GreetingController
中的 @RequestParam
如预期正常工作,原来的默认参数 World
已经变为了 User
。此外,id
值也从 1
变为了 2
。这说明多个请求都会被同一个 GreetingController
实例处理,counter
的值也如预期增长。