欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringMVC解析JSON請(qǐng)求數(shù)據(jù)問題解析

 更新時(shí)間:2017年04月11日 10:37:43   作者:木葉之榮  
這篇文章主要介紹了SpringMVC解析JSON請(qǐng)求數(shù)據(jù)問題解析,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

這幾年都在搞前后端分離、RESTful風(fēng)格,我們項(xiàng)目中也在這樣用。前幾天有人遇到了解析JSON格式的請(qǐng)求數(shù)據(jù)的問題,然后說了一下解析的方式,今天就寫篇文章簡單的分析一下后臺(tái)對(duì)于JSON格式請(qǐng)求數(shù)據(jù)是怎么解析的。

先把例子的代碼貼出來:

前端

<input type="button" value="測試JSON數(shù)據(jù)" onclick="testJSON()" /> 
<script type="text/javascript"> 
  function testJSON() { 
    $.ajax({ 
      type: "POST", 
      url: "/testJson", 
      contentType: "application/json", 
      dataType: "json", 
      data: JSON.stringify({"name":"張三"}), 
      success: function (jsonResult) { 
        alert(jsonResult); 
      } 
    }); 
  } 
</script> 

后臺(tái)處理代碼如下:

@RequestMapping(value ="testJson") 
public String testJson(@RequestBody Map name, HttpServletRequest request){ 
  System.out.println(name); 
  return "jsonp"; 
} 

這里需要注意的是:要在參數(shù)對(duì)象上加上@RequestBody注解,這個(gè)一定不能少,后臺(tái)在接收J(rèn)SON數(shù)據(jù)的時(shí)候一定要用自定義的對(duì)象或者M(jìn)ap對(duì)象去接收,不要用JDK中的簡單對(duì)象(String/Integer/Long)來接收。

接下來我再把抓出來的http請(qǐng)求貼一下:

Content-Type:application/json 

這里需要注意的是:Request Payload中的格式一定要和上圖一致,其他格式SpringMVC會(huì)解析不出來。

OK,如上的代碼就可以搞定一個(gè)JSON請(qǐng)求數(shù)據(jù)的解析了。下面我們來分析一下SpringMVC是怎么處理JSON請(qǐng)求的。

SpringMVC處理請(qǐng)求的簡單時(shí)序圖如下:

正常情況下,一個(gè)請(qǐng)求在SpringMVC中一般會(huì)調(diào)用doDispatch這個(gè)方法,我們進(jìn)入到這個(gè)方法中直接跳到

mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 

這一行,這一行上面的內(nèi)容我們以后再找機(jī)會(huì)分析。

ha.handle這個(gè)方法會(huì)調(diào)用org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter中的handle方法,這個(gè)方法里面很簡單,就是調(diào)用了handleInternal這個(gè)方法,代碼如下:

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) 
    throws Exception { 
 
  return handleInternal(request, response, (HandlerMethod) handler); 
} 

而handleInternal這個(gè)方法調(diào)用的是org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter中的handleInternal方法,我們進(jìn)入到這個(gè)方法中看看這個(gè)方法中都干了一些什么事:

@Override 
protected ModelAndView handleInternal(HttpServletRequest request, 
    HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { 
 
  ModelAndView mav; 
  checkRequest(request);//檢查是不是所支持的請(qǐng)求類型、是不是要求session 
 
  // Execute invokeHandlerMethod in synchronized block if required. 
  if (this.synchronizeOnSession) {//session中是不是要求同步執(zhí)行 
    HttpSession session = request.getSession(false); 
    if (session != null) { 
      Object mutex = WebUtils.getSessionMutex(session); 
      synchronized (mutex) {//同步執(zhí)行方法調(diào)用 
        mav = invokeHandlerMethod(request, response, handlerMethod); 
      } 
    } 
    else { 
      // No HttpSession available -> no mutex necessary 
      mav = invokeHandlerMethod(request, response, handlerMethod); 
    } 
  } 
  else { 
    // No synchronization on session demanded at all... 
    mav = invokeHandlerMethod(request, response, handlerMethod);//這三個(gè)invokeHandlerMethod調(diào)用的是同一個(gè)方法 
  }//緩存的設(shè)置 
  if (!response.containsHeader(HEADER_CACHE_CONTROL)) { 
    if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { 
      applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); 
    } 
    else { 
      prepareResponse(response); 
    } 
  } 
 
  return mav; 
} 

在上面的這個(gè)方法中我們需要關(guān)注的是invokeHandlerMethod這個(gè)方法。invokeHandlerMethod這個(gè)方法有點(diǎn)復(fù)雜,這個(gè)方法中干了很多的事,像創(chuàng)建數(shù)據(jù)驗(yàn)證類、創(chuàng)建方法處理類、模型視圖容器等。在這里我們先忽略這些,直接跳到

invocableMethod.invokeAndHandle(webRequest, mavContainer); 

這里。這個(gè)方法在org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod中。在這個(gè)方法中我們只關(guān)注第一句話:

Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); 

invokeForRequest這個(gè)方法在org.springframework.web.method.support.InvocableHandlerMethod中,同樣在這個(gè)方法中我們也只關(guān)注第一句話:

Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); 

getMethodArgumentValues從這個(gè)方法名我們可以看出來這個(gè)方法是獲取方法參數(shù)值的,這個(gè)類和上面的方法在同一個(gè)類中。我們進(jìn)到這個(gè)方法中看一下:

  private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, 
      Object... providedArgs) throws Exception { 
//獲取參數(shù)對(duì)象數(shù)組 方法中的參數(shù)都在這個(gè)對(duì)象數(shù)組中存放著。 
    MethodParameter[] parameters = getMethodParameters(); 
    Object[] args = new Object[parameters.length]; 
    for (int i = 0; i < parameters.length; i++) { 
      MethodParameter parameter = parameters[i]; 
      parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); 
      GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());//獲取參數(shù)的類型(處理參數(shù)中的泛型) 
      args[i] = resolveProvidedArgument(parameter, providedArgs);//如果提供了參數(shù)的值的話,直接返回 
      if (args[i] != null) { 
        continue; 
      } 
      if (this.argumentResolvers.supportsParameter(parameter)) { //(1) 支持的參數(shù)類型 
        try { 
          args[i] = this.argumentResolvers.resolveArgument( // (2) 給參數(shù)賦值、校驗(yàn)的一些操作 
              parameter, mavContainer, request, this.dataBinderFactory); 
          continue; 
        } 
        catch (Exception ex) { 
          if (logger.isDebugEnabled()) { 
            logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex); 
          } 
          throw ex; 
        } 
      } 
      if (args[i] == null) { 
        String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i); 
        throw new IllegalStateException(msg); 
      } 
    } 
    return args; 
  } 

我們先來看看上面的代碼中(1)的地方。這個(gè)地方是給方法中的參數(shù)匹配一個(gè)合適的解析器。這個(gè)方法的真正調(diào)用的是
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#getArgumentResolver這個(gè)方法。

private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { 
  HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);//如果緩存中已經(jīng)存在了,則從緩存中取 
  if (result == null) { 
    for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {//遍歷所有的參數(shù)解析器 
      if (logger.isTraceEnabled()) { 
        logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" + 
            parameter.getGenericParameterType() + "]"); 
      } 
      if (methodArgumentResolver.supportsParameter(parameter)) {//匹配合適的參數(shù)解析器并放入到緩存中 
        result = methodArgumentResolver; 
        this.argumentResolverCache.put(parameter, result); 
        break; 
      } 
    } 
  } 
  return result; 
} 

那SpringMVC種提供了多少參數(shù)解析器呢?看下圖所示:

大概有30來個(gè),瞬間覺得SpringMVC好強(qiáng)大啊,給人一種無論你在Header里、Cookie里、Body里、還是Path里,無論是什么類型的參數(shù)我都能給你解析了的霸氣。我們這里的匹配到的參數(shù)解析器是org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor這個(gè)類。我們接著來看上面代碼中的(2)。resolveArgument這個(gè)方法真的調(diào)用的就是RequestResponseBodyMethodProcessor這個(gè)類中的resolveArgument的方法。我們進(jìn)入到這個(gè)方法中看一下:

  public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, 
      NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { 
//這里是對(duì)參數(shù)的解析賦值 
    Object arg = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());//[1] 
    String name = Conventions.getVariableNameForParameter(parameter); 
//獲取參數(shù)校驗(yàn)的具體類 
    WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name); 
    if (arg != null) { 
      validateIfApplicable(binder, parameter);//進(jìn)行參數(shù)校驗(yàn) 
      if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { 
        throw new MethodArgumentNotValidException(parameter, binder.getBindingResult()); 
      } 
    } 
    mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult()); 
 
    return arg; 
  } 

我們重點(diǎn)看上面代碼中[1]的地方。方法中的代碼如下:

  @Override 
  protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter methodParam, 
      Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException { 
//獲取請(qǐng)求對(duì)象 
    HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); 
    ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest); 
//從請(qǐng)求輸入流中解析出參數(shù)的值 
    Object arg = readWithMessageConverters(inputMessage, methodParam, paramType); 
    if (arg == null) { 
      if (checkRequired(methodParam)) {//校驗(yàn)參數(shù)是不是必須的 
        throw new HttpMessageNotReadableException("Required request body is missing: " + 
            methodParam.getMethod().toGenericString()); 
      } 
    } 
    return arg; 
  } 

我們重點(diǎn)要看的是org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver中的readWithMessageConverters方法。

這個(gè)方法很長,在這個(gè)方法中會(huì)獲取ContentType、參數(shù)的類型、Method、重新封裝Request等等的操作。我們需要關(guān)注這三行代碼:

inputMessage = getAdvice().beforeBodyRead(inputMessage, param, targetType, converterType); 
body = genericConverter.read(targetType, contextClass, inputMessage);[1] 
body = getAdvice().afterBodyRead(body, inputMessage, param, targetType, converterType); 

為參數(shù)賦值的是[1]這行代碼。這里調(diào)用的是org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter中的read方法,代碼如下:

@Override 
public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) 
    throws IOException, HttpMessageNotReadableException { 
/獲取Java中的類型 
  JavaType javaType = getJavaType(type, contextClass); 
  return readJavaType(javaType, inputMessage);//按照J(rèn)ava的類型,為參數(shù)賦值 
} 
private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) { 
  try { 
    if (inputMessage instanceof MappingJacksonInputMessage) { 
      Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView(); 
      if (deserializationView != null) { 
        return this.objectMapper.readerWithView(deserializationView).forType(javaType). 
            readValue(inputMessage.getBody()); 
      } 
    } 
    return this.objectMapper.readValue(inputMessage.getBody(), javaType);//[1]調(diào)用Jackson中的方法,解析Body的內(nèi)容,賦值為java的類型 
  } 
  catch (IOException ex) { 
    throw new HttpMessageNotReadableException("Could not read document: " + ex.getMessage(), ex); 
  } 
} 

this.objectMapper.readValue這個(gè)方法會(huì)掉到Jackson相關(guān)的jar中。再往下跟的話還有很深,說實(shí)在的里面有很多的方法我還沒看明白,所以我們就不繼續(xù)往下走了。這個(gè)方法中大致干的事是按照相應(yīng)的編碼讀取HTTP請(qǐng)求中請(qǐng)求體里的內(nèi)容,由于是JSON格式的,所以又會(huì)把JSON格式的數(shù)據(jù)轉(zhuǎn)換為傳入進(jìn)去的Java類型對(duì)象。

后記:

如果我們知道請(qǐng)求格式是JSON的話,我們可以自己寫一個(gè)簡單的請(qǐng)求體的解析,但是在項(xiàng)目中最好別這樣做。代碼如下:

@RequestMapping(value ="testJson") 
public String testJson(HttpServletRequest request){ 
  try { 
    InputStream inputStream = request.getInputStream(); 
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 
    byte[] bytes = new byte[1024]; 
    int flag = 0; 
    while ((flag = inputStream.read(bytes)) > 0){ 
      byteArrayOutputStream.write(bytes,0,flag); 
    } 
    System.out.println(new String(byteArrayOutputStream.toByteArray(),request.getCharacterEncoding())); 
  } catch (IOException e) { 
    e.printStackTrace(); 
  } 
  return "jsonp"; 
} 

請(qǐng)求信息如下:

后臺(tái)輸出結(jié)果如下:

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java復(fù)制(拷貝)數(shù)組的五種方法匯總

    Java復(fù)制(拷貝)數(shù)組的五種方法匯總

    java基礎(chǔ)在Java中我們經(jīng)常需要復(fù)制一個(gè)數(shù)組,下面這篇文章主要給大家介紹了關(guān)于Java復(fù)制(拷貝)數(shù)組的五種方法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-03-03
  • springboot結(jié)合mybatis操作事務(wù)配置的處理

    springboot結(jié)合mybatis操作事務(wù)配置的處理

    在操作數(shù)據(jù)庫的時(shí)候,經(jīng)常會(huì)使用事務(wù)的處理,本文主要介紹了springboot結(jié)合mybatis操作事務(wù)配置的處理,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07
  • Java遞歸遍歷樹形結(jié)構(gòu)的實(shí)現(xiàn)代碼

    Java遞歸遍歷樹形結(jié)構(gòu)的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Java遞歸遍歷樹形結(jié)構(gòu)的相關(guān)資料,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2016-03-03
  • 將一個(gè)數(shù)組按照固定大小進(jìn)行拆分成數(shù)組的方法

    將一個(gè)數(shù)組按照固定大小進(jìn)行拆分成數(shù)組的方法

    下面小編就為大家?guī)硪黄獙⒁粋€(gè)數(shù)組按照固定大小進(jìn)行拆分成數(shù)組的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-11-11
  • SpringBoot測試junit遇到的坑及解決

    SpringBoot測試junit遇到的坑及解決

    這篇文章主要介紹了SpringBoot測試junit遇到的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • SpringBoot整合Freemarker實(shí)現(xiàn)頁面靜態(tài)化的詳細(xì)步驟

    SpringBoot整合Freemarker實(shí)現(xiàn)頁面靜態(tài)化的詳細(xì)步驟

    這篇文章主要介紹了SpringBoot整合Freemarker實(shí)現(xiàn)頁面靜態(tài)化,第一步要?jiǎng)?chuàng)建項(xiàng)目添加依賴,本文分步驟給大家詳細(xì)講解,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-10-10
  • RestTemplate請(qǐng)求失敗自動(dòng)重啟機(jī)制精講

    RestTemplate請(qǐng)求失敗自動(dòng)重啟機(jī)制精講

    這篇文章主要為大家介紹了RestTemplate請(qǐng)求失敗自定義處理的方法,自動(dòng)重試的機(jī)制精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多所進(jìn)步,早日升職加薪
    2022-03-03
  • 關(guān)于Spring中的@Configuration中的proxyBeanMethods屬性

    關(guān)于Spring中的@Configuration中的proxyBeanMethods屬性

    這篇文章主要介紹了關(guān)于Spring中的@Configuration中的proxyBeanMethods屬性,需要的朋友可以參考下
    2023-07-07
  • 淺談Java對(duì)象禁止使用基本類型

    淺談Java對(duì)象禁止使用基本類型

    本文主要介紹了淺談Java對(duì)象禁止使用基本類型,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • Java通過導(dǎo)出超大Excel文件解決內(nèi)存溢出問題

    Java通過導(dǎo)出超大Excel文件解決內(nèi)存溢出問題

    導(dǎo)出excel是咱Java開發(fā)的必備技能,下面這篇文章主要給大家介紹了關(guān)于Java通過導(dǎo)出超大Excel文件解決內(nèi)存溢出問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-09-09

最新評(píng)論