其它篇章:
一:SpringBoot3-日志——日志原理&日志格式&日志級別&日志分組&文件輸出&文件歸檔&滾動切割
二:SpringBoot3-Web開發-靜態資源——WebMvcAutoConfiguration原理&資源映射&資源緩存&歡迎頁&Favicon&自定義
一、多端內容適配
一套系統適配多端數據返回。
假設:
- 主體:寫好一個接口
GET /person
。 - 部署:部署到服務器,暴露給外界訪問。
- 外界:
1. 一個移動端應用:希望接口返回一個JSON文件;
2. 一個第三方應用:希望接口返回一個XML文件;
3. 一個IoT(物聯網設備):希望接口返回一個自定義協議數據文件。
提問:
- 以以上假設為例,如何讓一個接口做到多端的數據反饋?
思考:
- 一,可以寫三個請求路徑,分別為
/person.json
、/person.xml
、/person.diy(應用指定后綴)
,這三個不同應用分別訪問指定的請求路徑:- 缺點:這種方法只能用在內部接口少的情況下。如果,內部接口很多,那么每個接口都得拆分成三個接口,對應三個外部應用。若需要返回更多種類型數據文件,這個數量還得上升。
- 二,
SpringBoot
可以解決這個問題,詳細看接下來的內容。
默認規則
a.主要內容
內容協商功能是SpringMVC自帶的功能,SpringBoot對其進行了整合,快速實現一套系統適配多端數據返回。
SpringBoot多端內容適配
- 基于請求頭內容協商(默認開啟)
- 客戶端 向 服務端 發送請求時,攜帶HTTP標準的Accept請求頭。
- 客戶端:外界應用
- 服務端:接口
accept
:是http協議里規定的一個標準
- 在 客戶端 向 服務端 發送請求時,帶上Accept,讓 服務端 知道該返回什么類型的數據,SpringBoot 就把數據自動格式化成對應類型,再返回。
- 舉例:
- Accept:
application/json
- Accept:
text\xml
- Accept:
text\yaml
- Accept:
- 舉例:
- 基于請求參數內容協商(需要手動開啟)
- 舉例:
?format=json
:- 發送請求:
GET /projects/spring-boot?format=json
- 匹配:匹配到
GetMapping("/projects/spring-boot")
- 返回:根據參數協商,優先返回
json
類型數據
- 發送請求:
- 同理,發送請求
GET /projects/spring-boot?format=xml
,則優先返回xml
類型數據
b.演示驗證
- 創建一個接口,其內容如下:
- 先綁定地址:
@GetMapping("/person")
- 再定義一個 Person類。
- 創建的接口代碼如下:
package com.atwyb.web.controller;import com.atwyb.web.bean.Person; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;@RestController public class HelloController {@GetMapping("/person")public Person person(){Person person = new Person();person.setId("1");person.setUserName("zhangsan");return person;} }
- person的javabean如下:
package com.atwyb.web.bean;public class Person {private String id;private String userName;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;} }
- 先綁定地址:
點進
@RestController
,發現標注了@ResponseBody
。說明@RestController
返回的數據都標注了@ResponseBody
,而所有標注了@ResponseBody
的對象,都會以json傳輸。
-
啟動項目,在瀏覽器中驗證:
- 瀏覽器中訪問:
http://localhost:8080/person
- 發現數據的類型為
json
,如下圖所示:
- 瀏覽器中訪問:
-
思考:要想讓數據適配xml,該怎么做?
- SpringBoot默認支持把對象寫為json。因為默認web場景導入了jackson處理json的包:jackson-core。
- jackson是一個庫,也支持把數據寫為xml,但要導入xml相關依賴:
- 引入支持xml內容依賴:
<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId> </dependency>
- 標注xml注解:在Person這個JavaBean下標注xml注解
@JacksonXmlRootElement public class person{(省略……)}
- 引入支持xml內容依賴:
- 開啟基于請求參數的內容協商(默認關閉)
- 開啟基于請求參數的內容協商功能,默認參數名為format
spring.mvc.contentnegotiation.favor-parameter=true
- 指定內容協商時使用的參數名,默認為format
- 在application.propertise中添加以下內容:
- 開啟基于請求參數的內容協商功能,默認參數名為format
-
驗證:
- 在瀏覽器中訪問
http://localhost:8080/person?format=xml
- 在瀏覽器中訪問
http://localhost:8080/person?type=json
- 在瀏覽器中訪問
二、內容協商原理-HttpMessageConverter
學習邏輯鏈:
- 目的:想要自定義接口的返回內容
- 分析:要想自定義接口的返回內容,就要先理解內容協商的底層原理;而要理解內容協商的底層原理,只要知道
HttpMessageConverter
怎么工作,什么時候工作就行了;知道HttpMessageConverter
工作原理后,就能通過定制HttpMessageConverter
來實現多端內容協商,以達到自定義接口返回內容的目的。- 步驟:
a. 了解HttpMessageConverter
怎么工作,什么時候工作。
b. 這樣就能,理解內容協商的底層原理。
c. 接下來就能,通過定制HttpMessageConverter
來實現多端內容協商。
d. 這樣就,達到了想要自定義接口的返回內容的目的。- 總結:要想知道怎么自定義接口的返回內容,其實就是要知道怎么通過定制
HttpMessageConverter
來實現多端內容協商。
在WebMvcConfigurer
接口里,能夠配置很多底層的東西,其中就包含了一個configureMessageConverters
,如下圖。
只需要編寫WebMvcConfigurer
接口提供的configureMessageConverters
底層,修改底層的MessageConverters
就可以了。
思考:只要修改以上的MessageConverters
就可以了,但為什么這樣做就可以了?這就是接下來要說的內容了。
1、@ResponseBody由HttpMessageConverter處理
a.主要內容
在向瀏覽器返回內容的controller里,有一個
@RestController
注解,而這個注解里又包含一個@ResponseBody
,而注解標注在類上面,就表示標注在每一個方法上。
- 如果controller方法的返回值標注了
@ResponseBody
注解,
按照SpringMVC
的原理,我們從DispatcherServlet.class
開始,所有請求來到DispatcherServlet
,都是從doDispatch()
開始的。
ctlr+n搜索DispatcherServlet.class
,進入后繼續ctlr+n搜索doDispatch()
,在其內容第一行設置一個斷點,找到以下界面:
往下翻找到HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
。
- 如果根據/person這個請求路徑找到了某個方法要處理,那就要找到這個適配器,最終利用這個適配器處理方法。往下翻找到
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
,如圖。這是正真執行handler
的語句。
- 給person()第一行打個斷點,并以debug啟動,看看是怎么處理的。
瀏覽器訪問http://localhost:8080/person?format=json
,先來到doDispatch
,它來接第一個請求。
放行。
再放行,來到正真執行handler
的語句的位置。
- 這個時候點擊
step into
,進到這個目標執行方法,來到以下界面:
再點進handleInternal
,往下翻,看到invokeHandlerMethod
方法。
點進去,在第一行設置一個斷點:
一直放行到這,目標方法還沒進行到,只進行到invokeHandlerMethod
。
在RequestMappingHandlerAdapter.class
中往下找到invocableMethod.invokeAndHandle
,意思是執行并處理,把這一放行,就會去到person()
并執行。
這樣就會得到結論:
RequestMappingHandlerAdapter.class
里面的invokeAndHandle()才是正真執行目標方法的。
b. 總結:
- 如果
controller
方法的返回值標注了@ResponseBody
注解:- 請求進來先來到
DispatcherServlet
的doDispatch()
進行處理 - 找到一個
HandlerAdapter
適配器,利用適配器執行目標方法 RequestMappingHandlerAdapter
來執行,調用invokeHandlerMethod()
來執行目標方法- 目標方法執行之前,準備好兩個東西:
HandlerMethodArgumentResolver
:參數解析器,確定目標方法每個參數值HandlerMethodReturnValueHandler
:返回值處理器,確定目標方法的返回值該怎么處理。
- 目標方法執行完成,會返回返回值對象
- 找到一個合適的返回值處理器
- 最終找到
RequestResponseBodyMethodProcessor
中能處理標注了@ResponseBody注解
的方法 RequestResponseBodyMethodReturnValueHandler
調用writeWithMessageConverters
,利用MessageConverter把返回值寫出去。
- 請求進來先來到
看到
@GetMapping
,要知道這個注解都是由invokeHandlerMethod
所在的RequestMappingHandlerAdapter.class
其它篇章:
一:SpringBoot3-日志——日志原理&日志格式&日志級別&日志分組&文件輸出&文件歸檔&滾動切割
二:SpringBoot3-Web開發-靜態資源——WebMvcAutoConfiguration原理&資源映射&資源緩存&歡迎頁&Favicon&自定義