本章是《 Spring Boot 快速入門 》系列教程的第二章,若要查看本系列的所有章節,請點擊 這裏 。前端
在上一章《Hello Spring Boot》中,咱們從一個最簡單的應用程序入手,體驗了 Spring Boot 的開發流程。 本章咱們會帶着你們繼續 Spring Boot 體驗之旅,將 Hello Spring Boot 程序升級成 Web 版。 如今WEB開發比較流行的開發模式是先後端分離,也就是前端工程師專一於寫前端的代碼,用 ajax 調用後端的 Http Restful API 獲取數據,然後臺工程師則專一於實現 Http Restful API 便可,先後端在代碼工程和部署上都是徹底分離的,這樣只要接口定義好了就能夠各自開發相互不干擾,協做效率更高。 所以,本文的重點是描述如何用 Spring Boot 開發 Http Restful API 服務。java
本章的示例代碼放在「碼雲」上,你們能夠免費下載或瀏覽:git
https://git.oschina.net/terran4j/springboot/tree/master/springboot-web程序員
相關軟件使用的版本:web
程序在以上版本均調試過,能夠正常運行,其它版本僅做參考。ajax
Spring Boot 默認使用 Spring MVC 做爲WEB框架,處理HTTP請求須要編寫 Controller 類,以下代碼所示:spring
package com.terran4j.springboot.web; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class HelloController { @Autowired private HelloService helloService; @RequestMapping(value = "/hello", method = RequestMethod.GET) public void sayHello(HttpServletRequest request, HttpServletResponse response) throws IOException { String name = request.getParameter("name"); String msg = helloService.hello(name); response.getWriter().println(msg); } }
首先, 要在類上面加 @Controller 註解,而後處理 HTTP 請求的方法上要加 @RequestMapping 註解,如:數據庫
@RequestMapping(value = "/hello", method = RequestMethod.GET)
其中 value 是匹配的路徑,method 是匹配的方法,主要有GET, POST, PUT, DELETE這麼幾種,上面這行的意思是路徑爲 /hello 的 GET 請求,都會讓這個方法來處理,如:編程
http://localhost:8080/hello?name=terran4j
這裏方法參數咱們先用原始的 HttpServletRequest 和 HttpServletResponse 做爲入參,如:json
sayHello(HttpServletRequest request, HttpServletResponse response)
有的場景下咱們直接使用 HttpServletRequest 和 HttpServletResponse 對象會更靈活,但大多數實際場景下咱們用 @RequestParam
直接自動裝載參數會更簡單一些(這點後面的例子會講到)。
而後咱們寫一個 main 函數並運行它:
package com.terran4j.springboot.web; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class HelloWebApp { public static void main(String[] args) { SpringApplication.run(HelloWebApp.class, args); } }
運行起來後,咱們在瀏覽器輸入URL:
http://localhost:8080/hello?name=terran4j
訪問結果爲:
上面的示例中咱們直接從 HttpServletRequest 對象中讀取參數,然而Spring MVC提供了更簡單的方式,就是經過註解 @RequestParam 自動裝載請求參數,以下代碼所示:
// URL示例: http://localhost:8080/hello2/param?name=terran4j @RequestMapping(value = "/hello2/param", method = RequestMethod.GET) @ResponseBody public String sayHelloByParam(@RequestParam("name") String name) { return helloService.hello(name); }
代碼 @RequestParam("name") String name
的意思是自動讀取key爲 name 的 HTTP 請求參數,值裝載到方法入參 name 中。 但這樣寫的話,若是HTTP請求中沒有 name 參數時Spring MVC就會報錯,解決的方法是這樣寫 @RequestParam(value = "name", defaultValue = "")
意思是給它設置一個默認值,當沒有請求參數時就用默認值,默認值爲空串。
這種自動裝載的方式很智能,它還會根據參數對象的類型自動解析,好比還要一個 count 參數是數字類型的,能夠直接定義成Integer 或 Long 類型的,如:
sayHello(@RequestParam("count") Integer count, @RequestParam("name") String name)
Spring MVC 會根據方法入參的類型自動解析成 Integer 類型,固然若是對應的 HTTP 參數不是數字就會報錯。
另外,還能夠用 @PathVariable 來裝載URL路徑中的值做爲參數,如:
// URL示例: http://localhost:8080/hello2/path/terran4j @RequestMapping(value = "/hello2/path/{name}", method = RequestMethod.GET) @ResponseBody public String sayHelloByPath(@PathVariable("name") String name) { return helloService.hello(name); }
它會自動從URL路徑中 /hello2/path/ 後面的一節做爲 name 參數的值。 若是讀者比較瞭解 Restful 風格的 API 就知道這種方式很是適合 Restful API 。
注意,方法上必定不能少了 @ResponseBody 註解,它的意思是該方法的返回結果直接寫入 HTTP Response Body 中。 至於寫入的內容具體是什麼,則由 HttpMessageConverter 來決定,Spring MVC中默認的 HttpMessageConverter 是將返回對象自動轉成的json串,關於這一點下一節會講到。
目前爲止,咱們的返回值只是簡單的 String 類型,若是是一個本身定義的複雜的 java bean 會怎麼樣呢? 咱們先本身定義一個名爲 HelloBean 的java bean類,代碼以下:
package com.terran4j.springboot.web; import java.util.Date; public class HelloBean { private String name; private String message; private Date time; public final String getName() { return name; } public final void setName(String name) { this.name = name; } public final String getMessage() { return message; } public final void setMessage(String message) { this.message = message; } public final Date getTime() { return time; } public final void setTime(Date time) { this.time = time; } @Override public String toString() { return "Hello [name=" + name + ", message=" + message + ", time=" + time + "]"; } }
這個 Bean 中有 name, msg 兩個 String 類型的屬性,還有 time 這個 Date 類型的屬性,以及它們的 getter , setter 方法。
而後咱們將以前的Controller類改形成這樣:
package com.terran4j.springboot.web; import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController3 { private static final Logger log = LoggerFactory.getLogger(HelloController3.class); @Autowired private HelloService helloService; // URL示例: http://localhost:8080/hello3?name=terran4j @RequestMapping(value = "/hello3", method = RequestMethod.GET) public HelloBean sayHello(@RequestParam("name") String name) { HelloBean hello = new HelloBean(); hello.setName(name); hello.setMessage(helloService.hello(name)); hello.setTime(new Date()); if (log.isInfoEnabled()) { log.info("hello bean is: {}", hello); } return hello; } }
注意: 上面代碼中,類上的註解從 @Controller 變成 @RestController,而方法的註解少了個 @ResponseBody 。 其實 @RestController = @Controller + @ResponseBody,也就是說有了@RestController以後,至關於每一個方法(有@RequestMapping的)自動加上了 @ResponseBody 註解。
上面代碼中,方法返回一個 HelloBean 對象,咱們運行main程序,而後訪問URL:
http://localhost:8080/hello3?name=terran4j
結果以下:
能夠看到Spring MVC 自動將 java bean 對象轉成 json 串返回了。
但這仍是有一個問題, 屬性 Date time
顯示成了 long 類型的時間戳格式,而不是人類容易理解的格式(如「2017-03-09 18:42:00」),還有這個 json 串被壓縮成一行很很差看,一般正式的項目返回的數據是比較多的,若是也像這樣擠成一坨,那調試程序時看着也費勁啊。
那有沒有辦法對返回的 json 串結果優化一下呢? 答案顯然是有的,下一節咱們將講解如何經過注入自定義的 HttpMessageConverter 將返回值轉化成咱們但願的json串格式。
Spring MVC 在調用 Controller 的方法後,是用 HttpMessageConverter 類來將返回值轉化成最終結果並寫入到 Http Response Body 中的。 咱們能夠本身定義一個 HttpMessageConverter 的對象,來代替 Spring MVC 默認提供的 HttpMessageConverter 對象。
以下代碼所示:
package com.terran4j.springboot.web; import java.text.SimpleDateFormat; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.SerializationFeature; @EnableWebMvc public class HelloWebMvcConfigurer extends WebMvcConfigurerAdapter { private static final Logger log = LoggerFactory.getLogger(HelloWebMvcConfigurer.class); public static final ObjectMapper createObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE); // 屬性爲空時(包括 null, 空串,空集合,空對象),不參與序列化。 objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); // Date 對象在序列化時,格式爲 yyyy年MM月dd日 HH時mm分ss秒 。 objectMapper.setDateFormat(new SimpleDateFormat("yyyy年MM月dd日 HH時mm分ss秒")); objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true); objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); // json串以良好的格式輸出。 objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true); // 當屬性爲空或有問題時不參與序列化。 objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); // 未知的屬性不參與反序列化。 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); if (log.isInfoEnabled()) { log.info("create objectMapper done."); } return objectMapper; } @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { ObjectMapper objectMapper = createObjectMapper(); MappingJackson2HttpMessageConverter convertor = new MappingJackson2HttpMessageConverter(objectMapper); converters.add(0, convertor); } }
首先咱們用createObjectMapper()
方法本身建立一個 ObjectMapper 對象,ObjectMapper 是由開源項目 Jackson 提供的一個高性能 json 處理工具,它提供了 json 與 java 對象互相轉化能力,而且有很是強大的可配置性,好比下面這行代碼:
// Date 對象在序列化時,格式爲 yyyy年MM月dd日 HH時mm分ss秒 。 objectMapper.setDateFormat(new SimpleDateFormat("yyyy年MM月dd日 HH時mm分ss秒"));
它的意思是說,能夠把 Date 類型的對象,轉成本身但願的日期格式。 固然 ObjectMapper 還有不少配置項,就不一一詳述了,有興趣的朋友們能夠自行在網上查找相關資料瞭解。
而後咱們將自定義的 HttpMessageConverter 注入到 Spring MVC中,以下代碼所示:
@Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { ObjectMapper objectMapper = createObjectMapper(); MappingJackson2HttpMessageConverter convertor = new MappingJackson2HttpMessageConverter(objectMapper); converters.add(0, convertor); }
Spring MVC 中可能有不少 HttpMessageConverter 對象,運行時是從 converters 列表中遍歷,直到找到了一個 HttpMessageConverter 對象能處理當前的HTTP請求爲止。 咱們要把本身的 HttpMessageConverter 對象加到 converters 列表的第1位,以便得到最高優先權,如代碼: converters.add(0, convertor);
咱們使用了 jackson 項目提供的現成的 MappingJackson2HttpMessageConverter 類(它實現了 HttpMessageConverter 接口),咱們只要把本身建立的 ObjectMapper 對象傳進去就能夠了。
最後,咱們用 @Import 註解在主程序中引入這個類,代碼以下:
package com.terran4j.springboot.web; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Import; @Import(HelloWebMvcConfigurer.class) @SpringBootApplication public class HelloWebApp { public static void main(String[] args) { SpringApplication.run(HelloWebApp.class, args); } }
上面的 @Import(HelloWebMvcConfigurer.class)
就引入了咱們定義的 HelloWebMvcConfigurer 類到主程序中。
最後,咱們運行主程序,在瀏覽器輸入URL:
http://localhost:8080/hello3?name=terran4j
結果以下:
與以前的 json 串結果相比,格式變優雅了,Date 的顯示格式也易讀了:
本章是《 Spring Boot 快速入門 》系列的第二章,本章咱們講解了如何使用 Spring Boot 開發 Http Restful 服務,下一章咱們會講解在 Spring Boot 中如何訪問數據庫,歡迎你們繼續學習下一章《 Spring Boot JPA 》。
點擊 這裏 能夠查看本系列的所有章節。 (本系列的目標是幫助有 Java 開發經驗的程序員們快速掌握使用 Spring Boot 開發的基本技巧,感覺到 Spring Boot 的極簡開發風格及超爽編程體驗。)
另外,咱們有一個名爲 SpringBoot及微服務 的微信公衆號,感興趣的同窗請掃描下面的二維碼關注下吧,關注後就能夠收到咱們按期分享的技術乾貨哦!