一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - SpringMVC解析JSON請求數據問題解析

SpringMVC解析JSON請求數據問題解析

2020-09-09 13:17木葉之榮 Java教程

這篇文章主要介紹了SpringMVC解析JSON請求數據問題解析,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

這幾年都在搞前后端分離、restful風格,我們項目中也在這樣用。前幾天有人遇到了解析json格式的請求數據的問題,然后說了一下解析的方式,今天就寫篇文章簡單的分析一下后臺對于json格式請求數據是怎么解析的。

先把例子的代碼貼出來:

前端

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<input type="button" value="測試json數據" 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>

后臺處理代碼如下:

?
1
2
3
4
5
@requestmapping(value ="testjson")
public string testjson(@requestbody map name, httpservletrequest request){
  system.out.println(name);
  return "jsonp";
}

這里需要注意的是:要在參數對象上加上@requestbody注解,這個一定不能少,后臺在接收json數據的時候一定要用自定義的對象或者map對象去接收,不要用jdk中的簡單對象(string/integer/long)來接收。

接下來我再把抓出來的http請求貼一下:

?
1
content-type:application/json

SpringMVC解析JSON請求數據問題解析

這里需要注意的是:request payload中的格式一定要和上圖一致,其他格式springmvc會解析不出來。

ok,如上的代碼就可以搞定一個json請求數據的解析了。下面我們來分析一下springmvc是怎么處理json請求的。

springmvc處理請求的簡單時序圖如下:

SpringMVC解析JSON請求數據問題解析

正常情況下,一個請求在springmvc中一般會調用dodispatch這個方法,我們進入到這個方法中直接跳到

?
1
mv = ha.handle(processedrequest, response, mappedhandler.gethandler());

這一行,這一行上面的內容我們以后再找機會分析。

ha.handle這個方法會調用org.springframework.web.servlet.mvc.method.abstracthandlermethodadapter中的handle方法,這個方法里面很簡單,就是調用了handleinternal這個方法,代碼如下:

?
1
2
3
4
5
public final modelandview handle(httpservletrequest request, httpservletresponse response, object handler)
    throws exception {
 
  return handleinternal(request, response, (handlermethod) handler);
}

而handleinternal這個方法調用的是org.springframework.web.servlet.mvc.method.annotation.requestmappinghandleradapter中的handleinternal方法,我們進入到這個方法中看看這個方法中都干了一些什么事:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@override
protected modelandview handleinternal(httpservletrequest request,
    httpservletresponse response, handlermethod handlermethod) throws exception {
 
  modelandview mav;
  checkrequest(request);//檢查是不是所支持的請求類型、是不是要求session
 
  // execute invokehandlermethod in synchronized block if required.
  if (this.synchronizeonsession) {//session中是不是要求同步執行
    httpsession session = request.getsession(false);
    if (session != null) {
      object mutex = webutils.getsessionmutex(session);
      synchronized (mutex) {//同步執行方法調用
        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);//這三個invokehandlermethod調用的是同一個方法
  }//緩存的設置
  if (!response.containsheader(header_cache_control)) {
    if (getsessionattributeshandler(handlermethod).hassessionattributes()) {
      applycacheseconds(response, this.cachesecondsforsessionattributehandlers);
    }
    else {
      prepareresponse(response);
    }
  }
 
  return mav;
}

在上面的這個方法中我們需要關注的是invokehandlermethod這個方法。invokehandlermethod這個方法有點復雜,這個方法中干了很多的事,像創建數據驗證類、創建方法處理類、模型視圖容器等。在這里我們先忽略這些,直接跳到

?
1
invocablemethod.invokeandhandle(webrequest, mavcontainer);

這里。這個方法在org.springframework.web.servlet.mvc.method.annotation.servletinvocablehandlermethod中。在這個方法中我們只關注第一句話:

?
1
object returnvalue = invokeforrequest(webrequest, mavcontainer, providedargs);

invokeforrequest這個方法在org.springframework.web.method.support.invocablehandlermethod中,同樣在這個方法中我們也只關注第一句話:

?
1
object[] args = getmethodargumentvalues(request, mavcontainer, providedargs);

getmethodargumentvalues從這個方法名我們可以看出來這個方法是獲取方法參數值的,這個類和上面的方法在同一個類中。我們進到這個方法中看一下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
  private object[] getmethodargumentvalues(nativewebrequest request, modelandviewcontainer mavcontainer,
      object... providedargs) throws exception {
//獲取參數對象數組 方法中的參數都在這個對象數組中存放著。
    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());//獲取參數的類型(處理參數中的泛型)
      args[i] = resolveprovidedargument(parameter, providedargs);//如果提供了參數的值的話,直接返回
      if (args[i] != null) {
        continue;
      }
      if (this.argumentresolvers.supportsparameter(parameter)) { //(1) 支持的參數類型
        try {
          args[i] = this.argumentresolvers.resolveargument( // (2) 給參數賦值、校驗的一些操作
              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)的地方。這個地方是給方法中的參數匹配一個合適的解析器。這個方法的真正調用的是
org.springframework.web.method.support.handlermethodargumentresolvercomposite#getargumentresolver這個方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private handlermethodargumentresolver getargumentresolver(methodparameter parameter) {
  handlermethodargumentresolver result = this.argumentresolvercache.get(parameter);//如果緩存中已經存在了,則從緩存中取
  if (result == null) {
    for (handlermethodargumentresolver methodargumentresolver : this.argumentresolvers) {//遍歷所有的參數解析器
      if (logger.istraceenabled()) {
        logger.trace("testing if argument resolver [" + methodargumentresolver + "] supports [" +
            parameter.getgenericparametertype() + "]");
      }
      if (methodargumentresolver.supportsparameter(parameter)) {//匹配合適的參數解析器并放入到緩存中
        result = methodargumentresolver;
        this.argumentresolvercache.put(parameter, result);
        break;
      }
    }
  }
  return result;
}

那springmvc種提供了多少參數解析器呢?看下圖所示:

SpringMVC解析JSON請求數據問題解析

SpringMVC解析JSON請求數據問題解析

大概有30來個,瞬間覺得springmvc好強大啊,給人一種無論你在header里、cookie里、body里、還是path里,無論是什么類型的參數我都能給你解析了的霸氣。我們這里的匹配到的參數解析器是org.springframework.web.servlet.mvc.method.annotation.requestresponsebodymethodprocessor這個類。我們接著來看上面代碼中的(2)。resolveargument這個方法真的調用的就是requestresponsebodymethodprocessor這個類中的resolveargument的方法。我們進入到這個方法中看一下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  public object resolveargument(methodparameter parameter, modelandviewcontainer mavcontainer,
      nativewebrequest webrequest, webdatabinderfactory binderfactory) throws exception {
//這里是對參數的解析賦值
    object arg = readwithmessageconverters(webrequest, parameter, parameter.getgenericparametertype());//[1]
    string name = conventions.getvariablenameforparameter(parameter);
//獲取參數校驗的具體類
    webdatabinder binder = binderfactory.createbinder(webrequest, arg, name);
    if (arg != null) {
      validateifapplicable(binder, parameter);//進行參數校驗
      if (binder.getbindingresult().haserrors() && isbindexceptionrequired(binder, parameter)) {
        throw new methodargumentnotvalidexception(parameter, binder.getbindingresult());
      }
    }
    mavcontainer.addattribute(bindingresult.model_key_prefix + name, binder.getbindingresult());
 
    return arg;
  }

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  @override
  protected <t> object readwithmessageconverters(nativewebrequest webrequest, methodparameter methodparam,
      type paramtype) throws ioexception, httpmediatypenotsupportedexception, httpmessagenotreadableexception {
//獲取請求對象
    httpservletrequest servletrequest = webrequest.getnativerequest(httpservletrequest.class);
    servletserverhttprequest inputmessage = new servletserverhttprequest(servletrequest);
//從請求輸入流中解析出參數的值
    object arg = readwithmessageconverters(inputmessage, methodparam, paramtype);
    if (arg == null) {
      if (checkrequired(methodparam)) {//校驗參數是不是必須的
        throw new httpmessagenotreadableexception("required request body is missing: " +
            methodparam.getmethod().togenericstring());
      }
    }
    return arg;
  }

我們重點要看的是org.springframework.web.servlet.mvc.method.annotation.abstractmessageconvertermethodargumentresolver中的readwithmessageconverters方法。

這個方法很長,在這個方法中會獲取contenttype、參數的類型、method、重新封裝request等等的操作。我們需要關注這三行代碼:

?
1
2
3
inputmessage = getadvice().beforebodyread(inputmessage, param, targettype, convertertype);
body = genericconverter.read(targettype, contextclass, inputmessage);[1]
body = getadvice().afterbodyread(body, inputmessage, param, targettype, convertertype);

為參數賦值的是[1]這行代碼。這里調用的是org.springframework.http.converter.json.abstractjackson2httpmessageconverter中的read方法,代碼如下:

?
1
2
3
4
5
6
7
@override
public object read(type type, class<?> contextclass, httpinputmessage inputmessage)
    throws ioexception, httpmessagenotreadableexception {
/獲取java中的類型
  javatype javatype = getjavatype(type, contextclass);
  return readjavatype(javatype, inputmessage);//按照java的類型,為參數賦值
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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]調用jackson中的方法,解析body的內容,賦值為java的類型
  }
  catch (ioexception ex) {
    throw new httpmessagenotreadableexception("could not read document: " + ex.getmessage(), ex);
  }
}

this.objectmapper.readvalue這個方法會掉到jackson相關的jar中。再往下跟的話還有很深,說實在的里面有很多的方法我還沒看明白,所以我們就不繼續往下走了。這個方法中大致干的事是按照相應的編碼讀取http請求中請求體里的內容,由于是json格式的,所以又會把json格式的數據轉換為傳入進去的java類型對象。

后記:

如果我們知道請求格式是json的話,我們可以自己寫一個簡單的請求體的解析,但是在項目中最好別這樣做。代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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";
}

請求信息如下:

SpringMVC解析JSON請求數據問題解析

后臺輸出結果如下:

SpringMVC解析JSON請求數據問題解析

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://blog.csdn.net/zknxx/article/details/68952518

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 特级毛片全部免费播放器 | 星星动漫在线观看无删减 | 日本高清中文字幕视频在线 | 亚洲精彩视频在线观看 | 欧美日韩中文字幕一区二区高清 | 久久亚洲电影www电影网 | 免费国产在线观看 | 日本色网址 | 国产成人盗摄精品 | 猛h辣h高h文湿校园1v1 | 精品视频久久久久 | 男人j进女屁股视频在线观看 | 男gay网站视频免费观看 | 四虎永久在线精品国产 | 超强台风免费观看完整版视频 | 国产福利微拍精品一区二区 | 蜜桃成人影院 | 91精品国产综合久 | 久久久久免费视频 | 国产愉拍 | 国产成人精品曰本亚洲77美色 | 无人区尖叫之夜美女姐姐视频 | 国产在线欧美精品 | 亚洲美女啪啪 | 免费老外的毛片清高 | 欧美日韩国产另类一区二区三区 | 精品欧美一区二区在线观看欧美熟 | 欧美日韩国产一区二区三区在线观看 | 无码中文字幕av免费放 | 欧美免赞性视频 | ange venus与黑人| 亚洲精品视频网 | av中文字幕网免费观看 | 半挠脚心半黄的网站 | 日本在线播放 | 男人v天堂 | 青草草视频在线观看 | 欧美高清3dfreexxxx性 | 久久99热成人精品国产 | 国产人人草| 日韩欧美三级视频 |