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

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

SpringMVC注解之@ResponseBody注解原理

瀏覽:235日期:2022-08-11 17:04:20
目錄一、介紹二、作用范圍三、源碼分析四、總結(jié)一、介紹 @ResponseBody 注解的作用是將方法的返回值通過(guò)適當(dāng)?shù)霓D(zhuǎn)換器轉(zhuǎn)換為指定的格式之后,寫入到 response 對(duì)象的 body 區(qū),通常用來(lái)返回 JSON、XML 數(shù)據(jù)。 使用了 @ResponseBody 注解標(biāo)記的方法不再做視圖解析二、作用范圍 標(biāo)記在方法上 標(biāo)記在類上

通過(guò) @RestController 注解實(shí)現(xiàn),此時(shí)所有的方法都將會(huì)被添加 @ResponseBody 注解

三、源碼分析

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

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;}}

該方法中調(diào)用了 handleReturnValue() 方法去處理返回值。SpringMVC 中使用 RequestResponseBodyMethodProcessor 類來(lái)處理 @ResponseBody 標(biāo)記的方法

RequestResponseBodyMethodProcessor # handleReturnValue

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

該方法中通過(guò) mavContainer.setRequestHandled(true); 設(shè)置請(qǐng)求已經(jīng)被完全處理了,則后面不再做視圖解析。然后調(diào)用了 writeWithMessageConverters() 方法。

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {// 存儲(chǔ)響應(yīng)體的信息Object body;// 返回值類型Class<?> valueType;// 目標(biāo)類型Type targetType;// 返回值類型是否是 CharSequence // 是則將 返回值類型和目標(biāo)類型設(shè)置為 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());}// 返回值類型是否是實(shí)現(xiàn)了 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);// 可產(chǎn)生的媒體類型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) {// 使用類型轉(zhuǎn)換器將請(qǐng)求寫入到 response body 中g(shù)enericConverter.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);}}

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

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<>();// 遍歷類型轉(zhuǎn)化器,獲取支持的媒體類型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);}}

該方法中通過(guò)遍歷類型轉(zhuǎn)換器,根據(jù)類型轉(zhuǎn)換器獲取到支持的媒體類型。常見的類型轉(zhuǎn)化器有 StringHttpMessageConverter 支持轉(zhuǎn)換為 String 類型,MappingJackson2HttpMessageConverter 支持轉(zhuǎn)換為 json 類型,MappingJackson2XmlHttpMessageConverter 支持轉(zhuǎn)換為 XML 類型。以轉(zhuǎn)換為 JSON 數(shù)據(jù)為例。我們最終選擇的媒體類型就是 “application/json” ,然后調(diào)用 AbstractGenericHttpMessageConverter # write 方法將數(shù)據(jù)寫入到 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();// 添加響應(yīng)頭// 設(shè)置 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 {// 寫入數(shù)據(jù)到 response body 中writeInternal(t, type, outputMessage);outputMessage.getBody().flush();}}

該方法中設(shè)置了響應(yīng)頭的 Content-Type 為 application/json,然后調(diào)用 writeInternal() 方法寫數(shù)據(jù)

AbstractJackson2HttpMessageConverter # writeInternal

protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException {// 獲取媒體類型為 application/jsonMediaType contentType = outputMessage.getHeaders().getContentType();// 獲取 JSON 數(shù)據(jù)的編碼為 UTF-8JsonEncoding encoding = getJsonEncoding(contentType);// 獲取到 HttpServletResponse 的輸出流對(duì)象// 用于將數(shù)據(jù)寫入到 response body 中OutputStream outputStream = StreamUtils.nonClosing(outputMessage.getBody());// 生成 JSON 數(shù)據(jù)的類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);}// 用于操作可序列化對(duì)象的類// 我們自定義的類一定要實(shí)現(xiàn) Serializable 接口,并設(shè)置 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);}// 寫入數(shù)據(jù)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);}}

該方法中通過(guò)調(diào)用 outputMessage.getBody() 方法獲取到了 HttpServletResponse 的輸出流對(duì)象,用于將數(shù)據(jù)輸出到 response body 中。并設(shè)置了 JSON 數(shù)據(jù)的編碼格式為 UTF-8,然后通過(guò) ObjectWriter 對(duì)象操作我們自定義的可序列化的對(duì)象,將該對(duì)象轉(zhuǎn)換為 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;}

設(shè)置 JSON 數(shù)據(jù)的編碼格式為 UTF-8

ServletServerHttpResponse # getBody

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

獲取 HttpServletResponse 的輸出流

到這里我們已經(jīng)實(shí)現(xiàn)了將數(shù)據(jù)轉(zhuǎn)化為 JSON 格式輸出到 response body 中了。

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

RequestMappingHandlerAdapter # invokeHandlerMethod

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

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

RequestMappingHandlerAdapter # getModelAndView

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {modelFactory.updateModel(webRequest, mavContainer);// 是否已經(jīng)完全處理了,若為 true,則直接返回 null// mavContainer.setRequestHandled(true) 已設(shè)置為 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;}

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

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

到此這篇關(guān)于Java源碼解析之@ResponseBody注解原理的文章就介紹到這了,更多相關(guān)@ResponseBody注解原理內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Spring
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
91精品二区| 亚洲欧美网站在线观看| 色综合视频一区二区三区日韩 | 国产欧美日韩精品一区二区免费| 婷婷亚洲精品| 国产欧美大片| 久久中文字幕一区二区| 亚洲一区资源| 亚洲激情精品| 九九在线精品| 亚洲一区二区三区高清不卡| 99亚洲视频| 日韩精品中文字幕吗一区二区| 日本免费在线视频不卡一不卡二| 91精品福利观看| 精品亚洲自拍| 亚洲网站视频| 天堂va在线高清一区| 国产精品久久久久久久久久妞妞| 久久免费福利| 免费欧美一区| 91麻豆精品激情在线观看最新| 国产精品午夜一区二区三区| 国产精品毛片久久| 亚洲精品成人图区| 亚洲精品97| 日韩福利视频一区| 一区二区精品伦理...| 在线综合亚洲| 国产精品久久久一区二区| 精品丝袜在线| 日韩欧美中文字幕电影| 欧美韩日一区| 免费观看在线色综合| 日韩高清不卡一区| 亚洲精品黄色| 97精品国产福利一区二区三区| 精品一区免费| 日韩中文一区二区| 色综合视频一区二区三区日韩 | 爽好久久久欧美精品| 91午夜精品| 亚洲啊v在线| 久久亚洲图片| 麻豆mv在线观看| 亚洲综合中文| 亚洲成人不卡| 国产精品igao视频网网址不卡日韩| 三级在线看中文字幕完整版| 亚洲色图综合| 久久国产电影| 麻豆精品久久久| 在线视频亚洲欧美中文| 97成人超碰| 日韩av一级| 精品黄色一级片| 日本中文字幕视频一区| 国产综合色产| 91亚洲国产成人久久精品| 蜜桃av一区二区三区电影| 日韩国产在线| 国产精品视频一区二区三区综合| 亚洲精品午夜av福利久久蜜桃| 欧美国产极品| 日韩精品亚洲专区| 亚洲欧美日本视频在线观看| 亚洲爱爱视频| 精品国产黄a∨片高清在线| 国产精品密蕾丝视频下载| 国产亚洲网站| 国产精品资源| 亚洲精品一级二级三级| 韩国三级一区| 久久影视三级福利片| 日韩黄色在线观看| 日韩伦理福利| 国产精品国码视频| 中文字幕av一区二区三区四区| 欧美日韩在线二区| 激情国产在线| 麻豆精品av| 国产精品激情| 国产伦乱精品| 狠狠色综合网| 成人羞羞视频在线看网址| 亚洲精品第一| 爽爽淫人综合网网站| 欧美日韩免费观看一区=区三区| 日韩大片免费观看| 中文字幕在线视频网站| 精品一区二区三区中文字幕视频| 国产日韩精品视频一区二区三区| 亚洲久草在线| 亚洲男人在线| 午夜久久av| 日本少妇一区二区| 日韩精品成人| 一区在线免费| 国产精品3区| 国产精品红桃| 欧美综合社区国产| 亚洲一区二区三区四区电影 | 精品午夜av| 日本成人在线网站| 日韩精品中文字幕一区二区| 婷婷视频一区二区三区| 无码日韩精品一区二区免费| 日韩影片在线观看| 日韩国产高清在线| 亚洲一二三区视频| 日韩精品中文字幕吗一区二区| 亚洲一级大片| 四虎精品永久免费| 国产亚洲人成a在线v网站| 911精品国产| 久久99精品久久久久久园产越南| 激情不卡一区二区三区视频在线| 国产日韩一区二区三区在线| 日韩高清不卡一区二区| 国产极品嫩模在线观看91精品| 麻豆国产精品| 亚洲伊人av| 午夜精品网站| 免费中文字幕日韩欧美| 久久不射中文字幕| 日韩欧美四区| 麻豆中文一区二区| а√在线中文在线新版| 精品中文一区| 日韩高清在线不卡| 久久一区欧美| 久久亚洲国产| 日韩专区视频网站| 久久影院资源站| 一区福利视频| 鲁大师精品99久久久| 精品视频一区二区三区四区五区| 日韩欧美一区免费| 在线精品福利| 欧美精品三级在线| 中文av在线全新| 亚洲一区黄色| 国产欧美另类| 久久一区二区三区电影| 日日摸夜夜添夜夜添国产精品| 四虎国产精品免费观看| 亚洲激情婷婷| 美女在线视频一区| 亚洲电影在线一区二区三区| 欧美午夜三级| 999久久久国产精品| 欧美性www| 黄色av一区| 麻豆精品国产91久久久久久| 精品国产免费人成网站| 男女男精品视频网| 精品国产第一福利网站| 天堂av一区| 欧美精选一区二区三区| 91成人小视频| 国产精品毛片| 国产成人免费视频网站视频社区| 91九色精品国产一区二区| 国产精品videosex极品| 性色一区二区| 免费高潮视频95在线观看网站| 日韩精品欧美大片| 欧美成人综合| 国产精品毛片久久| 免费不卡在线观看| 欧美不卡高清一区二区三区| 欧美一区二区三区久久精品| 亚洲精品一区二区妖精| 麻豆国产精品777777在线| 蜜臀av一区二区三区| 日韩精品看片| 日本精品黄色| 日本午夜精品视频在线观看| 免费精品国产的网站免费观看| 久久精品免视看国产成人| 日本亚洲视频| 国产毛片久久| 天堂中文av在线资源库| 欧美黄页在线免费观看| 国产亚洲一级| 亚洲二区视频| 日本在线高清| 美女国产一区二区三区| 手机精品视频在线观看| 九九在线精品| 99精品国产一区二区三区| 久久97视频| 国产极品模特精品一二| 日本不卡在线视频| 在线观看视频免费一区二区三区| 在线综合视频| 日韩午夜高潮| 91精品福利| 99视频精品免费观看| 日韩欧美一区免费|