構建一個基於 Spring 的 RESTful Web Service

__kingzone__的專欄發表於2016-01-17

本文詳細介紹了基於Spring建立一個“Hello World” RESTful web service工程的步驟。

目標

構建一個service,接收如下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
  • JDK1.6或更高
  • Gradle 1.8+或Maven 3.0+
  • 你也可以使用STS(Spring Tool Suite)直接import該工程

如何完成

如同所有的Spring入門教程,你可以選擇一步一步的自己實現,也可以跳過基本的設定步驟。最終,你都將得到一份可以正常執行的程式碼。

如果選擇按步實現,繼續下一節。

如果選擇跳過基本的安裝部分,則執行以下命令從github獲取程式碼:

git clone https://github.com/spring-guides/gs-rest-service.git

切換當前目錄到gs-rest-service/initial,跳到 Create a resource representation class步驟 。

完成後,可以與gs-rest-service/complete中的程式碼對比一下,確保正確。

建立工程

首先建立一個基本的構建指令碼。基於Spring構建應用時,可以用使用任何的構建系統。這裡我們以Gradle和Maven為例。如果不熟悉它們,請參考Building Java Projects with Gradle或者Building Java Projects with Maven

建立目錄結構

在你選定的工程目錄下,建立如下子目錄結構;例如,在*nix系統中使用mkdir -p src/main/java/hello命令:

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

建立Gradle構建檔案

下面是初始的gradle構建檔案。你也可以使用Maven,Maven的配置檔案pom.xml可以參考這裡。如果你使用STS(Spring Tool Suite),可以直接匯入該工程。

build.gradle

buildscript {
    repositories {
        maven { url "http://repo.spring.io/libs-snapshot" }
        mavenLocal()
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'

jar {
    baseName = 'gs-rest-service'
    version =  '0.1.0'
}

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

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web:1.0.0.RC1")
    compile("com.fasterxml.jackson.core:jackson-databind")
    testCompile("junit:junit:4.11")
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.8'
}

注意:本文使用了Spring Boot

建立資源描述類(Create a resource representation class)

現在已經建立了工程和構建系統,下面建立你的web service。

首先考慮服務間的互動(service interactions)。

這個服務要處理/greeting的GET請求,其查詢字串包含一個可選的name引數。這個GET請求應該一個200 OK的響應,以及JSON結構的描述問候語的內容。格式如下:

{
    "id": 1,
    "content": "Hello, World!"
}

id域是問候語的唯一標識,content域是問候語的文字描述。

為了對問候語的描述進行建模,建立了一個資源描述類。提供了一個包含域(id和content)、構造方法和訪問器(getter和setter)的pojo(pain old java object)類:

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格式。

下面建立資源控制器(resource controller)來傳送這些問候語。

建立資源控制器(Create a resource controller)

採用Spring構建RESTful web services時,採用控制器處理HTTP請求。控制器元件通過@Controller註解來標識,下面的GreetingController類處理/greeting的GET請求,並返回一個Greeting類的新的例項:

src/main/java/hello/GreetingController.java

package hello;

import java.util.concurrent.atomic.AtomicLong;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @RequestMapping("/greeting")
    public @ResponseBody Greeting greeting(
            @RequestParam(value="name", required=false, defaultValue="World") String name) {
        return new Greeting(counter.incrementAndGet(),
                            String.format(template, name));
    }
}

這個controller很簡單,但是其內部做了大量工作,麻雀雖小,五臟俱全。我們一步一步的解釋。

@RequestMapping註解確保對/greeting的HTTP請求對映到greeting()方法。

注意:上述例子中沒有寫明GET、PUT、POST等等。這是因為@RequestMapping註解預設情況下對映所有的HTTP操作。使用@RequestMapping(method=GET)指定只對映GET請求。

@RequestParam把查詢字串中name引數的值繫結到greeting()方法的name引數上。該查詢引數不是必須的(required=false);如果請求時沒有指定該引數,則使用其預設值“World”(defaultValue)。

方法體的實現建立並返回了一個新的Greeting物件,該物件的id屬性每次自增1,content屬性採用template和name組合而來。

傳統的MVC控制器和上述RESTful web service控制器的一個關鍵區別在於:HTTP響應體的建立方式。前者採用檢視層技術(view technology)實現把伺服器端的資料渲染為HTML,後者則返回一個Greeting物件。物件資料將會直接以JSON格式寫到HTTP響應中。

通過以下方式實現上述功能,greeting()方法的@ResponseBody註解告訴Spring MVC不需要使用伺服器端檢視層渲染問候語物件(the greeting object),取而代之的是,返回的問候語物件時一個response body,而且應該直接寫出。

Greeting物件必須轉換成JSON格式。由於Spring支援HTTP報文轉換,你不需要手工進行轉換。由於Jackson 2在classpath中,因而Spring的MappingJackson2HttpMessageConverter會自動將Greeting例項轉換為JSON格式。

Make the application executable

雖然可以將這個service打包成傳統的WAR檔案,並部署到一個外部的應用伺服器上,但是我們採用了一個更簡單的方式:建立一個獨立的(standalone)應用程式。把所有檔案打包到一個可執行的JAR檔案中,由古老的main()方法驅動。採用Spring提供的嵌入的Tomcat Servlet容器作為HTTP執行時環境,不需要部署一個外部的執行時環境例項。

src/main/java/hello/Application.java

package hello;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan
@EnableAutoConfiguration
public class Application {

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

main()方法呼叫了SpringApplication幫助類,把Application.class傳遞給它的run()方法作為引數。這樣Spring就會去從Application讀取註解,並作為Spring application context的一個元件進行管理。

@ComponentScan註解告訴Spring遞迴地搜尋hello包和其子包中直接或間接標記為@Component的類。這確保Spring發現並註冊GreetingController,由於它被標記為@Controller,而@Controller是一類@Component註解。

@EnableAutoConfiguration註解基於你的classpath的內容開啟合理的預設行為。例如,由於應用程式依賴於嵌入版的Tomcat(tomcat-embed-core.jar),一個Tomcat伺服器會自動建立並進行合理的預設配置。應用程式也依賴於Spring MVC(spring-webmvc.jar),一個Spring MVC DispatcherServlet會為你配置和註冊–不需要web.xml!自動配置(Auto-configuration)是一種強大的靈活的機制。詳請參考API文件

構建可執行JAR(Build an executable JAR)

目前位置,Application類已經寫完,下面通過構建系統把所有檔案打包為一個可執行jar檔案。這將便於對這個service在多種不同環境中進行釋出、版本控制和部署。

下面是採用Gradle的步驟,如果採用Maven,可以在這裡找到pom.xml檔案,執行mvn clean package構建工程。

更新build.gradle檔案的buildscript部分,如下:

buildscript {
    repositories {
        maven { url "http://repo.spring.io/libs-snapshot" }
        mavenLocal()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.0.0.RC1")
    }
}

再在build.gradle中新增如下語句:

apply plugin: 'spring-boot'

可以在這裡看到最終版本的build.gradle檔案。

Spring Boot gradle plugin收集classpath中的所有jar包,並構建一個單獨的uber-jar,這使得更加便於執行和傳輸你的service。它也搜尋public static void main()方法標誌為一個可執行類。

下面執行如下命令生成一個單獨的可執行JAR檔案,該JAR檔案包含所有必需的依賴的類和資源:

./gradlew build

如果你使用Gradle,可以使用如下語句執行生成的JAR檔案:

java -jar build/libs/gs-rest-service-0.1.0.jar

如果使用Maven,使用如下語句:

java -jar target/gs-rest-service-0.1.0.jar

注意:上述過程將生成一個可執行JAR。你也可以選擇構建一個WAR檔案

執行(Run the service)

如果採用Gradle,可以在命令列中執行如下命令來執行你的service:

./gradlew clean build && java -jar build/libs/gs-rest-service-0.1.0.jar

注意:如果採用Maven,可以執行如下語句mvn clean package && java -jar target/gs-rest-service-0.1.0.jar

日誌輸出。service將會啟動並在數秒鐘內執行。

測試(Test the service)

現在service已經啟動,訪問http://localhost:8080/greeting,你會看到:

{"id":1,"content":"Hello, World!"}

查詢字串中指定一個name引數,如http://localhost:8080/greeting?name=User。content的值從“Hello, World!”變為“Hello, User!”:

{"id":2,"content":"Hello, User!"}

這說明GreetingController類中的@RequestParam註解起作用了。name引數給定的預設值為“World”,但是可以通過在查詢字串中設定值覆蓋它。

注意id屬性從1變為2。這表明你在多次請求中訪問了同一個GreetingController例項,它的counter域每次訪問時會自增1。

小結(Summary)

恭喜!你已經開發出了一個基於Spring的RESTful web service。

相關文章