构建的rest的Web服务所要实现的功能
构建一个接受HTTP GET请求的服务,如下:
http://localhost:8080/greeting
greeting
响应的json数据格式如下:
{"id":1,"content":"Hello, World!"}
在greeting
中,我们可以自定义一个名为name
的请求参数:
http://localhost:8080/greeting?name=User
当我们加入name
的请求参数后,默认的“World”值将会被重写,因而响应的结果变更为:
{"id":1,"content":"Hello, User!"}
构建rest的Web服务器
构建rest的Web服务器需要准备的前提条件
JDK 1.8 及以上版本
Gradle 2.3+ 或者 Maven 3.0+
而我这里主要是以Maven 3.0+为基础来进行演示。
构建Maven格式的rest的Web服务
构建Maven格式的rest的Web服务器基础框架
我们需要创建的目录结构如下:
└── src
└── main
└── java
└── hello
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-rest-service</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<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>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
其实我们在搭建这个rest的Web服务的基础框架时有一个更好的办法,那就是在Maven官网的GitHub账号上有一个官方的基础框架可以直接拿来使用,该框架支持Maven、Gradle,能够支持在包括windows和linux等的操作系统在内的命令行运行。其链接如下:
这个 Spring Boot Maven插件提供了许多方便的特性
它收集类路径上的所有jar,并构建一个单一的、可运行的“über-jar”,这使得执行和传输您的服务更加方便。
它搜索
public static void main()
方法并将其标记为可运行的类。它提供了一个内置的依赖解析器,它可以设置版本号来匹配
Spring Boot dependencies
。您可以覆盖任何您希望的版本,但是它将引导默认所选择的版本集。
添加java代码
我们通过考虑服务交互来开始这个过程。
该服务将以GET
请求的方式来请求/greeting
,并可选地使用查询字符串中的name
参数。GET
请求应该返回一个200 OK
的JSON格式的问候响应,其应该是这样的:
{
"id": 1,
"content": "Hello, World!"
}
id
字段是问候的唯一标识符,content
是问候的文本表示。
要对问候表示建模,您需要创建一个资源表示类。为id
和 content
数据提供一个带有字段、构造器和存取器的普通java对象。
src/main/java/hello/Greeting.java
package hello;
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;
}
}
正如您在下面的步骤中看到的,Spring将会调用
Jackson
JSON的运行库将Greeting
的返回数据转换成JSON格式。
接下来,您将创建资源控制器来提供这些问候。
创建一个资源controller
在Spring
构建RESTful web
服务的方法中,HTTP
请求由控制器处理。这些组件很容易通过@restcontroller
注解来识别,下面的GreetingController
通过处理访问/greeting
的GET
请求来返回并装载一个新的Greeting
类。
src/main/java/hello/GreetingController.java
package hello;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
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();
@RequestMapping("/greeting")
public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}
这个控制器简洁而简单,但是在底层有很多东西。让我们一步一步地把它分解。
@RequestMapping
注解确保将/greeting
的HTTP
请求映射到greeting()
方法。
上面的例子并没有指定
GET
和PUT
、POST
等等,因为@RequestMapping
在默认情况下映射所有的HTTP
操作。使用@RequestMapping(method=GET)
来缩小这个映射。
@RequestParam
将查询的字符串参数name
的值绑定到greeting()
方法的name
参数中。这个查询字符串参数被显式地标记为可选的(默认情况下required=true
):如果在请求中没有,则使用defaultValue
中的“World”。
方法主体的实现创建并返回一个带有id
和content
属性的新Greeting
对象,该对象基于counter
的下一个值,并通过使用问候template
来格式化给定的name
。
传统的MVC控制器和上面的RESTful Web
服务控制器之间的一个关键区别是HTTP响应主体的创建方式。这个rest式的web服务控制器并不依赖于视图技术来执行向HTML的问候数据的服务器端呈现,而是简单地填充并返回一个Greeting
对象。对象数据将以JSON形式直接写入HTTP响应。
这段代码使用了Spring 4的新@RestController
注释,它将类标记为一个控制器,其中每个方法都返回一个域对象而不是一个视图。它是@Controller
和@ResponseBody
的缩写。
Greeting
对象必须转换为JSON。由于Spring的HTTP消息转换器支持,您不需要手工进行这种转换。因为 Jackson 2
类路径中,Spring的 MappingJackson2HttpMessageConverter
自动选择Greeting
实例转换为JSON。
使应用程序成为可执行文件
尽管可以将此服务打包为用于部署到外部应用服务器的传统WAR文件,但下面演示的更简单的方法创建了一个独立的应用程序。您可以将所有内容打包到一个单独的、可执行的JAR文件中,这个JAR文件由一个好的旧Java main()
方法驱动。在此过程中,您使用Spring的支持来将Tomcat servlet容器嵌入到HTTP运行时中,而不是将其部署到外部实例中。
src/main/java/hello/Application.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication
是一个方便的注释,它添加了以下所有内容:
@Configuration
将类标记为应用程序上下文中bean定义的来源。@EnableAutoConfiguration
告诉Spring引导开始添加基于类路径设置、其他bean以及各种属性设置。通常情况下,你会为一个Spring MVC应用添加
@EnableWebMvc
,但是当Spring引导在类路径上看到spring-webmvc
时,它会自动添加它。这将应用程序标记为web应用程序,并激活诸如设置DispatcherServlet
之类的关键行为。@ComponentScan告诉Spring在
hello
包中寻找其他组件、配置和服务,以便找到控制器。
main()
方法使用Spring Boot
的SpringApplication.run()
方法来启动一个应用程序。您注意到没有一行XML吗?没有web.xml
文件。这个web应用程序是100%纯Java的,您不需要处理任何管道或基础设施的配置。
构建一个可执行JAR
您可以在命令行中使用Gradle或Maven来运行该应用程序。或者您可以构建一个单一的可执行JAR文件,该文件包含所有必需的依赖项、类和资源,并运行该文件。这使得在整个开发生命周期中,跨不同的环境,以及在不同的环境中,将服务作为一个独立的应用程序进行发布、版本和部署服务变得更加容易。
在Maven中,如果是Linux操作系统,我们需要在应用程序下使用./mvnw spring-boot:run
,如果是windows操作系统,我们需要在应用程序下使用mvnw spring-boot:run
或者你可以在Linux下使用./mvnw clean package
如果在windows下使用mvnw clean package
。然后你就可以运行这个JAR
文件了,运行指令如下:
java -jar target/gs-rest-service-0.1.0.jar
上面的过程将创建一个可运行的JAR。您也可以选择构建一个典型的WAR文件。
测试服务
现在服务已经运行,访问http://localhost:8080/greeting
,你将会看到:
{"id":1,"content":"Hello, World!"}
添加查询参数name
访问http://localhost:8080/greeting?name=User
。注意,内容属性的值从“Hello,World!”变成“Hello User!”:
{"id":2,"content":"Hello, User!"}
这一变化表明,GreetingController
中的@RequestParam
是按预期工作的。name
参数被赋予了“World”的默认值,但是总是可以通过查询字符串显式地覆盖它。
还要注意id
属性是如何从1变更为2的。这就证明了您在多次请求中对同一个GreetingController
实例进行了操作,并且它的计数器字段在每次调用时都是按照预期的那样递增的。