日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

SpringMVC注解之@ResponseBody注解原理

瀏覽:236日期:2022-08-11 17:04:20
目錄一、介紹二、作用范圍三、源碼分析四、總結一、介紹 @ResponseBody 注解的作用是將方法的返回值通過適當的轉換器轉換為指定的格式之后,寫入到 response 對象的 body 區,通常用來返回 JSON、XML 數據。 使用了 @ResponseBody 注解標記的方法不再做視圖解析二、作用范圍 標記在方法上 標記在類上

通過 @RestController 注解實現,此時所有的方法都將會被添加 @ResponseBody 注解

三、源碼分析

具體為何調用了以下方法可以看我的另一篇文章。SpringMVC 執行流程解析

ServletInvocableHandlerMethod # invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);setResponseStatus(webRequest);if (returnValue == null) {if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}}else if (StringUtils.hasText(getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, 'No return value handlers');try {// 處理返回值this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);}throw ex;}}

該方法中調用了 handleReturnValue() 方法去處理返回值。SpringMVC 中使用 RequestResponseBodyMethodProcessor 類來處理 @ResponseBody 標記的方法

RequestResponseBodyMethodProcessor # handleReturnValue

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {// 設置請求已經被完全處理了,則后面不再做視圖解析// 后面我還會提到的mavContainer.setRequestHandled(true);ServletServerHttpRequest inputMessage = createInputMessage(webRequest);ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);}

該方法中通過 mavContainer.setRequestHandled(true); 設置請求已經被完全處理了,則后面不再做視圖解析。然后調用了 writeWithMessageConverters() 方法。

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {// 存儲響應體的信息Object body;// 返回值類型Class<?> valueType;// 目標類型Type targetType;// 返回值類型是否是 CharSequence // 是則將 返回值類型和目標類型設置為 String.classif (value instanceof CharSequence) {body = value.toString();valueType = String.class;targetType = String.class;}// 不是 CharSequence 類型,一般是我們的自定義類else {body = value;valueType = getReturnValueType(body, returnType);targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());}// 返回值類型是否是實現了 Resource 接口的資源// 這里我就不分析了if (isResourceType(value, returnType)) {outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, 'bytes');if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&outputMessage.getServletResponse().getStatus() == 200) {Resource resource = (Resource) value;try {List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());body = HttpRange.toResourceRegions(httpRanges, resource);valueType = body.getClass();targetType = RESOURCE_REGION_LIST_TYPE;}catch (IllegalArgumentException ex) {outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, 'bytes */' + resource.contentLength());outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());}}}// 選中的媒體類型MediaType selectedMediaType = null;MediaType contentType = outputMessage.getHeaders().getContentType();boolean isContentTypePreset = contentType != null && contentType.isConcrete();if (isContentTypePreset) {if (logger.isDebugEnabled()) {logger.debug('Found ’Content-Type:' + contentType + '’ in response');}selectedMediaType = contentType;}else {HttpServletRequest request = inputMessage.getServletRequest();// 可接受的媒體類型List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);// 可產生的媒體類型List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);if (body != null && producibleTypes.isEmpty()) {throw new HttpMessageNotWritableException('No converter found for return value of type: ' + valueType);}// 將要被使用的媒體類型List<MediaType> mediaTypesToUse = new ArrayList<>();for (MediaType requestedType : acceptableTypes) {for (MediaType producibleType : producibleTypes) {if (requestedType.isCompatibleWith(producibleType)) {mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));}}}if (mediaTypesToUse.isEmpty()) {if (body != null) {throw new HttpMediaTypeNotAcceptableException(producibleTypes);}if (logger.isDebugEnabled()) {logger.debug('No match for ' + acceptableTypes + ', supported: ' + producibleTypes);}return;}MediaType.sortBySpecificityAndQuality(mediaTypesToUse);for (MediaType mediaType : mediaTypesToUse) {// 該媒體類型是否是具體的// 也就是不包含類似于 * 這樣的通配符if (mediaType.isConcrete()) {// 選中要使用的媒體類型selectedMediaType = mediaType;break;}else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;break;}}if (logger.isDebugEnabled()) {logger.debug('Using ’' + selectedMediaType + '’, given ' +acceptableTypes + ' and supported ' + producibleTypes);}}if (selectedMediaType != null) {selectedMediaType = selectedMediaType.removeQualityValue();for (HttpMessageConverter<?> converter : this.messageConverters) {GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?(GenericHttpMessageConverter<?>) converter : null);if (genericConverter != null ?((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :converter.canWrite(valueType, selectedMediaType)) {body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,(Class<? extends HttpMessageConverter<?>>) converter.getClass(),inputMessage, outputMessage);if (body != null) {Object theBody = body;LogFormatUtils.traceDebug(logger, traceOn ->'Writing [' + LogFormatUtils.formatValue(theBody, !traceOn) + ']');addContentDispositionHeader(inputMessage, outputMessage);if (genericConverter != null) {// 使用類型轉換器將請求寫入到 response body 中genericConverter.write(body, targetType, selectedMediaType, outputMessage);}else {((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);}}else {if (logger.isDebugEnabled()) {logger.debug('Nothing to write: null body');}}return;}}}if (body != null) {Set<MediaType> producibleMediaTypes =(Set<MediaType>) inputMessage.getServletRequest().getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {throw new HttpMessageNotWritableException('No converter for [' + valueType + '] with preset Content-Type ’' + contentType + '’');}throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);}}

該方法中通過調用 getAcceptableMediaTypes() 方法獲取到 acceptableTypes,getProducibleMediaTypes() 方法獲取到 producibleTypes,然后調用 isCompatibleWith() 方法比較 acceptableTypes 和 producibleTypes,獲取到兩者都兼容的類型。最后通過調用 isConcrete() 獲取到一個具體使用的媒體類型。

AbstractMessageConverterMethodProcessor # getProducibleMediaTypes

protected List<MediaType> getProducibleMediaTypes(HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {Set<MediaType> mediaTypes =(Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);if (!CollectionUtils.isEmpty(mediaTypes)) {return new ArrayList<>(mediaTypes);}else if (!this.allSupportedMediaTypes.isEmpty()) {List<MediaType> result = new ArrayList<>();// 遍歷類型轉化器,獲取支持的媒體類型for (HttpMessageConverter<?> converter : this.messageConverters) {if (converter instanceof GenericHttpMessageConverter && targetType != null) {if (((GenericHttpMessageConverter<?>) converter).canWrite(targetType, valueClass, null)) {result.addAll(converter.getSupportedMediaTypes());}}else if (converter.canWrite(valueClass, null)) {result.addAll(converter.getSupportedMediaTypes());}}return result;}else {return Collections.singletonList(MediaType.ALL);}}

該方法中通過遍歷類型轉換器,根據類型轉換器獲取到支持的媒體類型。常見的類型轉化器有 StringHttpMessageConverter 支持轉換為 String 類型,MappingJackson2HttpMessageConverter 支持轉換為 json 類型,MappingJackson2XmlHttpMessageConverter 支持轉換為 XML 類型。以轉換為 JSON 數據為例。我們最終選擇的媒體類型就是 “application/json” ,然后調用 AbstractGenericHttpMessageConverter # write 方法將數據寫入到 response body 中。

AbstractGenericHttpMessageConverter # write

public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType,HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {final HttpHeaders headers = outputMessage.getHeaders();// 添加響應頭// 設置 Content-Type 為application/jsonaddDefaultHeaders(headers, t, contentType);if (outputMessage instanceof StreamingHttpOutputMessage) {StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() {@Overridepublic OutputStream getBody() {return outputStream;}@Overridepublic HttpHeaders getHeaders() {return headers;}}));}else {// 寫入數據到 response body 中writeInternal(t, type, outputMessage);outputMessage.getBody().flush();}}

該方法中設置了響應頭的 Content-Type 為 application/json,然后調用 writeInternal() 方法寫數據

AbstractJackson2HttpMessageConverter # writeInternal

protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException {// 獲取媒體類型為 application/jsonMediaType contentType = outputMessage.getHeaders().getContentType();// 獲取 JSON 數據的編碼為 UTF-8JsonEncoding encoding = getJsonEncoding(contentType);// 獲取到 HttpServletResponse 的輸出流對象// 用于將數據寫入到 response body 中OutputStream outputStream = StreamUtils.nonClosing(outputMessage.getBody());// 生成 JSON 數據的類JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputStream, encoding);try {writePrefix(generator, object);Object value = object;Class<?> serializationView = null;FilterProvider filters = null;JavaType javaType = null;if (object instanceof MappingJacksonValue) {MappingJacksonValue container = (MappingJacksonValue) object;value = container.getValue();serializationView = container.getSerializationView();filters = container.getFilters();}if (type != null && TypeUtils.isAssignable(type, value.getClass())) {// 獲取 java 類型,一般是我們自定義的類javaType = getJavaType(type, null);}// 用于操作可序列化對象的類// 我們自定義的類一定要實現 Serializable 接口,并設置 get/set 方法ObjectWriter objectWriter = (serializationView != null ?this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer());if (filters != null) {objectWriter = objectWriter.with(filters);}if (javaType != null && javaType.isContainerType()) {objectWriter = objectWriter.forType(javaType);}SerializationConfig config = objectWriter.getConfig();if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {objectWriter = objectWriter.with(this.ssePrettyPrinter);}// 寫入數據objectWriter.writeValue(generator, value);writeSuffix(generator, object);generator.flush();generator.close();}catch (InvalidDefinitionException ex) {throw new HttpMessageConversionException('Type definition error: ' + ex.getType(), ex);}catch (JsonProcessingException ex) {throw new HttpMessageNotWritableException('Could not write JSON: ' + ex.getOriginalMessage(), ex);}}

該方法中通過調用 outputMessage.getBody() 方法獲取到了 HttpServletResponse 的輸出流對象,用于將數據輸出到 response body 中。并設置了 JSON 數據的編碼格式為 UTF-8,然后通過 ObjectWriter 對象操作我們自定義的可序列化的對象,將該對象轉換為 JSON 格式輸出到 response body 中。

AbstractJackson2HttpMessageConverter # getJsonEncoding

protected JsonEncoding getJsonEncoding(@Nullable MediaType contentType) {if (contentType != null && contentType.getCharset() != null) {Charset charset = contentType.getCharset();JsonEncoding encoding = ENCODINGS.get(charset.name());if (encoding != null) {return encoding;}}return JsonEncoding.UTF8;}

設置 JSON 數據的編碼格式為 UTF-8

ServletServerHttpResponse # getBody

public OutputStream getBody() throws IOException {this.bodyUsed = true;writeHeaders();return this.servletResponse.getOutputStream();}

獲取 HttpServletResponse 的輸出流

到這里我們已經實現了將數據轉化為 JSON 格式輸出到 response body 中了。

還記得前面提到的 mavContainer.setRequestHandled(true) 這個方法嗎,前面我說了調用了這個方法后,就不再做視圖解析了,我們這里再具體分析一下。

RequestMappingHandlerAdapter # invokeHandlerMethod

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {...invocableMethod.invokeAndHandle(webRequest, mavContainer);...return getModelAndView(mavContainer, modelFactory, webRequest);... }

可以看到 getModelAndView() 方法是在 invokeAndHandle() 方法之后調用了,也就是在調用 getModelAndView() 方法前,我們已經調用了 mavContainer.setRequestHandled(true) 方法了。getModelAndView() 方法就是做視圖解析的,我們來看一下該方法。

RequestMappingHandlerAdapter # getModelAndView

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {modelFactory.updateModel(webRequest, mavContainer);// 是否已經完全處理了,若為 true,則直接返回 null// mavContainer.setRequestHandled(true) 已設置為 true 了if (mavContainer.isRequestHandled()) {return null;}// 下面的代碼是做視圖解析ModelMap model = mavContainer.getModel();ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());if (!mavContainer.isViewReference()) {mav.setView((View) mavContainer.getView());}if (model instanceof RedirectAttributes) {Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);if (request != null) {RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);}}return mav;}

可以看到,該方法一開始就調用了 mavContainer.isRequestHandled() 方法,如果為 true,則返回 null,并進行下面的視圖解析。而 mavContainer.setRequestHandled(true) 方法已經將其設置為 true 了。這就是為什么加了 @ResponseBody 注解的方法不做視圖解析的原因。

四、總結 @ResponseBody 注解即可加在方法中,也可以通過 @RestController 注解加在類上 類上添加了 @RestController 注解等效于為該類的所有方法上添加 @ResponseBody 注解 @ResponseBody 通過各種類型轉換器實現數據的轉換,如將數據轉換為 String、JSON、XML 等格式。并將數據寫入到 response body 中。而且它們使用的都是 UTF-8 編碼。 對于自定義的 Java 類轉換為 JSON 格式的數據,該類要是可序列化的。 使用了 @ResponseBody 注解標記的方法不再做視圖解

到此這篇關于Java源碼解析之@ResponseBody注解原理的文章就介紹到這了,更多相關@ResponseBody注解原理內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Spring
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
国产欧美自拍| 欧美日韩视频免费看| 久久精品青草| 亚洲免费影视| 91成人在线网站| 成人三级高清视频在线看| 久久精品国产www456c0m| 伊人国产精品| 麻豆精品少妇| 亚洲欧美日韩高清在线| 国产午夜精品一区在线观看| 久久影视三级福利片| 91精品久久久久久久久久不卡| 爽爽淫人综合网网站| 国产精品久久久久久久久免费高清| 日韩国产一区二区| 亚洲精品第一| www.com.cn成人| 亚洲精品动态| 在线人成日本视频| 亚洲激情二区| 国产日韩欧美在线播放不卡| www在线观看黄色| 亚洲激情另类| 久久国产精品色av免费看| 九九色在线视频| 免费日韩精品中文字幕视频在线| 亚洲无线观看| 国产成人精品福利| 中文在线不卡| 国产精品久久久久久久久久齐齐| 久久91导航| 久热精品在线| 麻豆国产精品777777在线| 麻豆精品蜜桃| 日韩高清在线不卡| 午夜影院一区| 香蕉久久一区| 成人久久一区| 日韩久久一区| 九九色在线视频| 综合亚洲色图| 高潮一区二区| 国产aⅴ精品一区二区三区久久| 亚洲欧美日韩综合国产aⅴ| 国产精品v一区二区三区| 91精品亚洲| 国产精品伦一区二区| 九一精品国产| 国产剧情在线观看一区| 今天的高清视频免费播放成人| 欧美另类中文字幕| 极品裸体白嫩激情啪啪国产精品| 精品亚洲自拍| 亚洲免费毛片| 欧美性感美女一区二区| 日韩区一区二| 欧美手机在线| 久久精品国产亚洲一区二区三区| 日韩在线一区二区| 色一区二区三区| 青草国产精品久久久久久| 久久久久美女| 国产精品99久久免费| 日韩国产一区| 国产欧美日韩亚洲一区二区三区| 亚洲高清av| 加勒比视频一区| 日本成人在线网站| 五月精品视频| 欧美好骚综合网| 欧美天堂一区二区| 亚洲精品一区二区在线看| 国产69精品久久| 日韩高清三区| 精品中文一区| 久久国产日韩欧美精品| 国际精品欧美精品| 日韩精品欧美精品| 亚洲在线网站| 激情黄产视频在线免费观看| 国产欧美一区二区色老头| 久久在线电影| 高清久久精品| 国产伦精品一区二区三区视频 | 亚洲精品第一| 亚洲电影在线一区二区三区| 国产不卡精品| 鲁大师精品99久久久| 欧美日韩午夜| 日本不卡中文字幕| 蜜乳av另类精品一区二区| 亚洲小说欧美另类婷婷| 亚洲欧洲美洲av| 国产成年精品| 精品三级久久久| 国产无遮挡裸体免费久久| 蜜桃视频一区二区三区| 九九色在线视频| 黄色网一区二区| 麻豆久久久久久| 国产精品亚洲欧美一级在线| 日韩国产欧美三级| 日韩区欧美区| 亚洲专区视频| 亚洲精选91| 婷婷成人av| 国产九九精品| 国产日韩欧美一区在线| 青草综合视频| 欧美亚洲tv| 欧美日本精品| 国产精品最新自拍| 日韩成人在线看| 欧美一区自拍| 国产精品久久久亚洲一区| 久久精品99久久久| 欧美日韩中出| 日韩高清不卡一区| 国产极品模特精品一二 | 一区免费视频| 亚洲视频www| 综合一区二区三区| 日韩国产欧美三级| 国产日产一区| 麻豆久久久久久久| 精品一区二区三区在线观看视频| 日本va欧美va欧美va精品| 日韩高清在线不卡| 欧美片第1页综合| 欧美a级一区二区| 精品一区二区男人吃奶| av免费不卡国产观看| 成人免费电影网址| 最新国产拍偷乱拍精品| 97在线精品| 欧美日韩精品免费观看视欧美高清免费大片 | 99精品99| 91欧美日韩在线| 国产精品videossex久久发布 | 黄色不卡一区| 中文一区在线| 亚洲18在线| 国产精一区二区| 美腿丝袜亚洲三区| 日韩久久一区二区三区| 欧美精品自拍| 日本aⅴ精品一区二区三区| 国产精品porn| 久久久久久黄| 在线精品视频一区| 日韩久久99| 久久亚洲精品中文字幕| 日韩中文在线电影| 亚洲在线网站| 青青草91久久久久久久久| 麻豆精品视频在线| 99热精品久久| 亚洲毛片在线免费| 国产极品嫩模在线观看91精品| 福利一区二区| 欧美日韩国产探花| 国产亚洲欧美日韩在线观看一区二区 | 久久久免费人体| 日本精品在线中文字幕| 亚洲欧美日韩在线观看a三区| 日韩avvvv在线播放| 麻豆久久一区| 欧美精品一区二区三区精品| 日本成人中文字幕在线视频| 国产a亚洲精品| 亚洲免费影视| 免费日韩成人| 激情久久五月| 青青草伊人久久| 四虎4545www国产精品| 美女精品在线观看| 欧美成a人片免费观看久久五月天| 久久精品青草| 欧美三级第一页| 高清av一区| 黄色日韩精品| 麻豆传媒一区二区三区| 香蕉国产精品| 国产精品1区| 日韩不卡视频在线观看| 亚洲精品少妇| 日本国产精品| 久久精品 人人爱| 尤物tv在线精品| 国产免费av一区二区三区| 中文字幕系列一区| 国产麻豆精品久久| 亚洲女同一区| 久久精品三级| 蜜臀av性久久久久蜜臀aⅴ流畅| 高清日韩中文字幕| 亚洲精品一二三**| 亚洲免费福利| 精品国产三区在线|