搭建一个Restful Web服务
本文档将引导你用Spring搭建一个“Hello,World!”Restful Web服务。
你要搭建的是什么?
你将搭建一个Web服务,这个服务可以接收一个像http://localhost:8080/greeting 这样的Http Get请求,并返回一个greeting的json数据。这个json数据如下所示:
{"id":1,"content":"Hello,World!"}
你也可以自定义查询字符串中的一个可选name参数,如下所示:
http://localhost:8080/greeting?name=User
传过来name参数的值将覆盖掉name参数的默认值"World",从而我们的response也会变成:
{"id":1,"content":"Hello,User!"}
你需要什么
- 大约15分钟
- 一个最喜欢的编辑器或者IDE
- JDK1.8或者以上
- Gradle 4+ 或者 Maven3.2+
- 你也可以将代码直接import到你的IDE中去
如何完成这个guide
像大多数Spring “Getting Started Guides”一样,你可以从头开始依次完成每一个步骤,也可以跳过那些你已经熟悉的步骤。无论哪种方式都可以。
创建项目
对于所有的Spring应用,你应该使用Spring Initializr来初始化项目。Spring Initializr可以让你快速拉取你需要的依赖和为你做许多配置的工作。在本例中我们只需要Spring Web依赖。(我使用的是Idea,Idea创建项目时也可以使用Spring Inititlizr来初始化项目)具体步骤如下所示:
![](https://wx2.sbimg.cn/2020/06/11/rest-service-1.md.png)
![](https://wx2.sbimg.cn/2020/06/11/rest-service-2.md.png)
![](https://wx2.sbimg.cn/2020/06/11/rest-service-3.md.png)
<?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.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.aihs</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>
创建一个Resource Representation Class(即POJO)
首先我们来思考一下服务的交互过程。这个服务会处理 / g r e e t i n g \color{blue}{/greeting} /greeting的 G e t \color{green}{Get} Get请求,在请求串中还有一个可选的 n a m e \color{blue}{name} name参数。这个请求会带来一个代码为 200 \color{green}{200} 200的响应,这个响应里包含一个代表greeting的JSON数据。这个JSON数据会像如下一样:
{
"id": 1,
"content": "Hello,World!"
}
属性id是
g
r
e
e
t
i
n
g
\color{blue}{greeting}
greeting的一个唯一标识,而属性
c
o
n
t
e
n
t
\color{blue}{content}
content是greeting的具体内容。
想要model greeting,我们就要创建一个POJO。这个POJO中应该有
i
d
\color{blue}{id}
id和
c
o
n
t
e
n
t
\color{blue}{content}
content属性,应该有构造器,应该有getter、setter方法。这个POJO如下所示(src/main/java/com/aihs/model/Greeting.java):
package com.aihs.restservice.model;
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数据。spring-boot-starter-web 中默认包含了Jackson。
创建一个Resource Controller
用Spring的方式来创建Restful Web服务时,Http Requests会被一个Controller处理。这些Controller应该由 @ R e s t C o n t r o l l e r \color{green}{@RestController} @RestController来注解。 G r e e t i n g C o n t r o l l e r \color{blue}{GreetingController} GreetingController(src/main/java/com/aihs/controller/GreetingController.java)能够处理 / g r e e t i n g \color{blue}{/greeting} /greeting的 G E T \color{blue}{GET} GET请求并返回一个新的Greeting类实例,GreetingController的代码如下所示:
package com.aihs.restservice.controller;
import com.aihs.restservice.model.Greeting;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.atomic.AtomicLong;
@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));
}
}
这个Controller是精确简洁的,它的底层完成了大量的工作。我们将逐步分解。
@
G
e
t
M
a
p
p
i
n
g
\color{blue}{@GetMapping}
@GetMapping注解确保向
/
g
r
e
e
t
i
n
g
\color{blue}{/greeting}
/greeting的HTTP GET请求都映射到了$\color{blue}{greeting()}方法中去。
对于其他的HTTP动作也有相应的注解(例如针对POST请求的 @ P o s t M a p p i n g \color{blue}{@PostMapping} @PostMapping)。还有一个叫 @ R e q u e s t M a p p i n g \color{blue}{@RequestMapping} @RequestMapping的注解,用它也可以达到相同的结果(例如 @ R e q u e s t M a p p i n g ( m e t h o d = G E T ) \color{blue}{@RequestMapping(method=GET)} @RequestMapping(method=GET))。
@ R e q u e s t P a r a m \color{blue}{@RequestParam} @RequestParam注解将query string中的 n a m e \color{blue}{name} name与 g r e e t i n g ( ) \color{blue}{greeting()} greeting()中的 n a m e \color{blue}{name} name参数绑定了起来。如果请求中没有 n a m e \color{blue}{name} name属性值,那么 d e f a u l t V a l u e \color{blue}{defaultValue} defaultValue的值将被使用。
g r e e t i n g ( ) \color{blue}{greeting()} greeting()这个方法创建并返回一个新的带有 i d \color{blue}{id} id和 c o n t e n t \color{blue}{content} content属性的 G r e e t i n g \color{blue}{Greeting} Greeting对象,其中 i d \color{blue}{id} id属性的值是由 c o u n t e r \color{blue}{counter} counter而来的, c o n t e n t \color{blue}{content} content是由 n a m e \color{blue}{name} name和 t e m p l a t e \color{blue}{template} template用String format而来的
传统的MVC Controller与Restful Web服务的Controller关键的不同之处表明了HTTP的response体是更容易被创建的。Restful Web服务的Controller是返回一个 G r e e t i n g \color{blue}{Greeting} Greeting对象,而不是依赖于一个视图层的技术将greeting的数据插入进HTML里。这个 G r e e t i n g \color{blue}{Greeting} Greeting对象会以JSON的格式直接写入HTTP的response中去。
代码使用 @ R e s t C o n t r o l l e r \color{blue}{@RestController} @RestController注解把一个类当作为一个Controller。而Controller类中每个方法返回时一个对象而不再是一个视图。 @ R e s t C o n t r o l l e r \color{blue}{@RestController} @RestController是包含了 @ C o n t r o l l e r \color{blue}{@Controller} @Controller和 @ R e s p o n s e B o d y \color{blue}{@ResponseBody} @ResponseBody的简写。
这个 G r e e t i n g \color{blue}{Greeting} Greeting对象必须被转换成JSON数据。由于Spring的HTTP message converter的支持,你不需要手动去完成这个转换。因为Jackson 2被用了进来,Spring的 M a p p i n g J a c k s o n 2 H t t p M e s s a g e C o n v e r t e r \color{blue}{MappingJackson2HttpMessageConverter} MappingJackson2HttpMessageConverter会自动将 G r e e t i n g \color{blue}{Greeting} Greeting实例转换为JSON数据。
@ S p r i n g B o o t A p p l i c a t i o n \color{blue}{@SpringBootApplication} @SpringBootApplication是一个很方便的的注解,它增加了一下内容:
- @ C o n f i g u r a t i o n \color{blue}{@Configuration} @Configuration:把该类标志为作为应用上下文中Bean定义的源
- @ E n a b l e A u t o C o n f i g u r a t i o n \color{blue}{@EnableAutoConfiguration} @EnableAutoConfiguration:告诉Spring Boot根据类路径设置,其他beans和各种属性设置开始添加beans。例如,如果 s p r i n g − w e b m v c \color{blue}{spring-webmvc} spring−webmvc在类路径上,则此注解会将应用程序标记为Web应用程序并激活诸如设置 D i s p a t c h S e r v l e t \color{blue}{DispatchServlet} DispatchServlet这类的关键行为。
- @ C o m p o n e n t S c a n \color{blue}{@ComponentScan} @ComponentScan:告诉Spring去查找 c o m / a i h s \color{blue}{com/aihs} com/aihs包下面的其他组件、配置和服务,让它找到控制器。
m a i n ( ) \color{blue}{main()} main()方法使用Spring Boot的 S p r i n g A p p l i c a t i o n . r u n ( ) \color{blue}{SpringApplication.run()} SpringApplication.run()方法来启动一个应用。你有没有注意到没有一行XML代码。在这里没有 w e b . x m l \color{blue}{web.xml} web.xml等其他XML文件。这个Web应用是100%纯Java的,你不需要处理任何管道和基础设施的配置工作。
编译为一个可执行的JAR
你可以使用Gradle或者Maven命令行来运行这个应用。你也可以将它打包成一个包含必要的依赖、类和资源的可执行JAR包然后执行它。打成可执行的JAR包使得在整个开发生命周期中、跨不同执行环境等等的情况下都可以轻松的将服务作为应用程序进行发布、版本化和部署。
由于我们使用的是maven,所以我们可以使用 . / m v n w \color{blue}{./mvnw} ./mvnw s p i r n g − b o o t : r u n \color{blue}{spirng-boot:run} spirng−boot:run来运行这个应用。
你也可以使用 . / m v n w \color{blue}{./mvnw} ./mvnw c l e a n \color{blue}{clean} clean p a c k a g e \color{blue}{package} package来生成JAR包,然后使用如下的命令来运行这个JAR包:
java -jar target/gs-rest-service-0.1.0.jar
测试这个服务
现在这个服务已经启动起来了,访问 h t t p : / / l o c a l h o s t : 8080 / g r e e t i n g \color{blue}{http://localhost:8080/greeting} http://localhost:8080/greeting,你会看到:
{"id": 1,"content": "Hello,World!"}
当提供一个name值时,如 h t t p : / / l o c a l h o s t : 8080 / g r e e t i n g ? n a m e = U s e r \color{blue}{http://localhost:8080/greeting?name=User} http://localhost:8080/greeting?name=User,注意content的值从 H e l l o , W o r l d ! \color{blue}{Hello,World!} Hello,World!变成了 H e l l o , U s e r ! \color{blue}{Hello,User!} Hello,User!,如下所示:
{"id": 2,"content": "Hello,User!"}
这个改变表明了 G r e e t i n g C o n t r o l l e r \color{blue}{GreetingController} GreetingController中的 @ R e q u e s t P a r a m \color{blue}{@RequestParam} @RequestParam像预期的那样起作用了。 n a m e \color{blue}{name} name属性有一个默认的值 W o r l d \color{blue}{World} World,但是我们可以通过query string去显式的重写默认值。
我们还要注意到 i d \color{blue}{id} id属性值时如何从 1 \color{blue}{1} 1变到 2 \color{blue}{2} 2的。这个证明了不同的请求经过了相同的 G r e e t i n g C o n t r o l l e r \color{blue}{GreetingController} GreetingController实例处理,并且它的 c o u n t e r \color{blue}{counter} counter属性如我们预期的那样被增加了。
总结
祝贺你!你已经成功得使用Spring开发了一个Restful Web服务!