<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
web專案引入的啟動器spring-boot-starter-web中含有
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> <version>2.7.0</version> <scope>compile</scope> </dependency>
這個依賴下面又有jackson的相關依賴,用於json的轉換
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.3</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jdk8</artifactId> <version>2.13.3</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.13.3</version> <scope>compile</scope> </dependency>
之前我們分析了引數解析器argumentResolvers的相關原始碼,瞭解了請求中的引數是如何找到合適的引數解析器,並與方法的入參進行繫結的,那麼問題來了,響應值的返回值處理器是如何找到的呢?
要分析原始碼,我們還是得進入DispatcherServlet這個類下的doDiapatch()方法
與之前分析引數解析器一樣的流程,我們跟到了RequestMappingHandlerAdapter這個類下的invokeHandlerMethod()方法
通過斷點可以看到我們預設支援15種返回值解析器
那麼他是怎麼從這麼多返回值解析器中選出自己支援的那一個呢?
我們可以發現,這些返回值解析器都是實現了HandlerMethodReturnValueHandler介面
package org.springframework.web.method.support; import org.springframework.core.MethodParameter; import org.springframework.lang.Nullable; import org.springframework.web.context.request.NativeWebRequest; public interface HandlerMethodReturnValueHandler { boolean supportsReturnType(MethodParameter returnType); void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception; }
此介面下只有2個方法,一個是判斷是否支援此型別返回值,另一個是處理返回值的方法
是不是和之前的引數解析器的介面類有異曲同工之妙?
沒錯,我們正是使用這兩個方法來尋找適合自己的返回值解析器以及處理我們的返回值
跟著斷點一直向下,我們跟到了HandlerMethodReturnValueHandlerComposite類下的handleReturnValue()方法,在這裡,我們就找到了對應的解析器並執行了相關方法
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { // 獲取自己適合的返回值解析器 HandlerMethodReturnValueHandler handler = this.selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } else { // 呼叫該返回值解析器中的處理返回值的方法 handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); } } @Nullable private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) { // 判斷是否是非同步的返回值 boolean isAsyncValue = this.isAsyncReturnValue(value, returnType); Iterator var4 = this.returnValueHandlers.iterator(); HandlerMethodReturnValueHandler handler; // 使用do-while進行遍歷,找出支援當前返回值型別的解析器 do { do { if (!var4.hasNext()) { return null; } handler = (HandlerMethodReturnValueHandler)var4.next(); } while(isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)); } while(!handler.supportsReturnType(returnType)); return handler; }
最終,我們確定使用解析器RequestResponseBodyMethodProcessor可以處理標註了@ResponseBody的返回值
除了尋找合適的返回值解析器之外,我們還有一個問題要思考
為什麼我們在控制器類的方法上加一個@ResponseBody註解就能將響應資訊轉換成json格式呢?
這個轉換是怎樣實現的呢?
我們接著上面的程式碼繼續往下debug
之前我們已經定位到了使用RequestResponseBodyMethodProcessor來處理@ResponseBody的返回值,所以我們繼續深入到這個類下的handleReturnValue()
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { mavContainer.setRequestHandled(true); // 建立輸入和輸出資訊 ServletServerHttpRequest inputMessage = this.createInputMessage(webRequest); ServletServerHttpResponse outputMessage = this.createOutputMessage(webRequest); // 使用訊息轉換器進行寫出操作 this.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); }
然後我們來分析解析器父類別AbstractMessageConverterMethodProcessor下的writeWithMessageConverters()方法
首先我們瞭解一個概念:什麼是內容協商?
瀏覽器預設會以請求頭的方式告訴伺服器他能接受什麼樣的內容型別,這裡的q代表優先順序
然後伺服器最終根據自己自身的能力,決定伺服器能生產出什麼樣內容型別的資料
所以在writeWithMessageConverters()方法中我們獲取到瀏覽器能接受什麼內容
以及伺服器能產生什麼內容
然後我們經過協商,決定返回application/json格式的資料
重點來了:這裡有一系列的訊息轉換器,我們到底使用哪個呢?
隨便點進一個訊息轉換器,我們發現他都是實現了HttpMessageConverter這個介面
這裡面有2個方法,canRead()和canWrite()來幫助我們判斷能否支援源型別和目標型別的訊息讀寫
最終我們確定了,使用MappingJackson2HttpMessageConverter這個訊息轉換器來進行json轉換
package org.springframework.http.converter; import java.io.IOException; import java.util.Collections; import java.util.List; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.lang.Nullable; public interface HttpMessageConverter<T> { // 讀取,將我們當前訊息轉換器支援的物件以某種格式讀取進來,例如將json資料讀取成Person物件 boolean canRead(Class<?> clazz, @Nullable MediaType mediaType); // 寫出,我們當前訊息轉換器支援的物件以某種格式寫出去,例如將Person物件以json格式寫出去 boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType); List<MediaType> getSupportedMediaTypes(); default List<MediaType> getSupportedMediaTypes(Class<?> clazz) { return !this.canRead(clazz, (MediaType)null) && !this.canWrite(clazz, (MediaType)null) ? Collections.emptyList() : this.getSupportedMediaTypes(); } T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException; void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException; }
然後我們在此處呼叫MappingJackson2HttpMessageConverter父類別AbstractGenericHttpMessageConverter中的write()方法
走到AbstractJackson2HttpMessageConverter類下的writeInternal()方法
可以看出,它利用了底層的Jackson的objectMapper進行轉換
這樣,我們就完成了Person資料到json格式的轉換
大概流程如下
如果需要返回xml格式的資料,那麼需要額外匯入相關依賴
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency>
我們可以看到,瀏覽器是預設支援如下返回格式的,一般情況下,我們無法指定自己需要的返回格式
但是我們可以通過修改設定+新增引數的方式指定我們需要的格式
首先,我們在yaml檔案中,開啟基於瀏覽器引數方式內容協商功能
spring:
mvc:
contentnegotiation:
favor-parameter: true
一旦此引數設定為true,那麼我們的內容協商管理器contentNegotiationManager中,除了原有的從請求頭獲取媒體型別的策略之外,還多了一個從請求引數中獲取媒體型別的策略,它支援xml和json兩種媒體型別
然後我們使用http://localhost:8080/testPathVariable/001/split/decade?format=xml或者http://localhost:8080/testPathVariable/001/split/decade?format=json指定我們需要的返回格式
到此這篇關於SpringBoot響應處理實現流程詳解的文章就介紹到這了,更多相關SpringBoot響應處理內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45