這幾年都在搞前后端分離、restful風(fēng)格,我們項(xiàng)目中也在這樣用。前幾天有人遇到了解析json格式的請(qǐng)求數(shù)據(jù)的問(wèn)題,然后說(shuō)了一下解析的方式,今天就寫(xiě)篇文章簡(jiǎn)單的分析一下后臺(tái)對(duì)于json格式請(qǐng)求數(shù)據(jù)是怎么解析的。
先把例子的代碼貼出來(lái):
前端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<input type= "button" value= "測(cè)試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)處理代碼如下:
1
2
3
4
5
|
@requestmapping (value = "testjson" ) public string testjson( @requestbody map name, httpservletrequest request){ system.out.println(name); return "jsonp" ; } |
這里需要注意的是:要在參數(shù)對(duì)象上加上@requestbody注解,這個(gè)一定不能少,后臺(tái)在接收json數(shù)據(jù)的時(shí)候一定要用自定義的對(duì)象或者map對(duì)象去接收,不要用jdk中的簡(jiǎn)單對(duì)象(string/integer/long)來(lái)接收。
接下來(lái)我再把抓出來(lái)的http請(qǐng)求貼一下:
1
|
content-type:application/json |
這里需要注意的是:request payload中的格式一定要和上圖一致,其他格式springmvc會(huì)解析不出來(lái)。
ok,如上的代碼就可以搞定一個(gè)json請(qǐng)求數(shù)據(jù)的解析了。下面我們來(lái)分析一下springmvc是怎么處理json請(qǐng)求的。
springmvc處理請(qǐng)求的簡(jiǎn)單時(shí)序圖如下:
正常情況下,一個(gè)請(qǐng)求在springmvc中一般會(huì)調(diào)用dodispatch這個(gè)方法,我們進(jìn)入到這個(gè)方法中直接跳到
1
|
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è)方法里面很簡(jiǎn)單,就是調(diào)用了handleinternal這個(gè)方法,代碼如下:
1
2
3
4
5
|
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è)方法中都干了一些什么事:
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); //檢查是不是所支持的請(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)建方法處理類、模型視圖容器等。在這里我們先忽略這些,直接跳到
1
|
invocablemethod.invokeandhandle(webrequest, mavcontainer); |
這里。這個(gè)方法在org.springframework.web.servlet.mvc.method.annotation.servletinvocablehandlermethod中。在這個(gè)方法中我們只關(guān)注第一句話:
1
|
object returnvalue = invokeforrequest(webrequest, mavcontainer, providedargs); |
invokeforrequest這個(gè)方法在org.springframework.web.method.support.invocablehandlermethod中,同樣在這個(gè)方法中我們也只關(guān)注第一句話:
1
|
object[] args = getmethodargumentvalues(request, mavcontainer, providedargs); |
getmethodargumentvalues從這個(gè)方法名我們可以看出來(lái)這個(gè)方法是獲取方法參數(shù)值的,這個(gè)類和上面的方法在同一個(gè)類中。我們進(jìn)到這個(gè)方法中看一下:
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 { //獲取參數(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; } |
我們先來(lái)看看上面的代碼中(1)的地方。這個(gè)地方是給方法中的參數(shù)匹配一個(gè)合適的解析器。這個(gè)方法的真正調(diào)用的是
org.springframework.web.method.support.handlermethodargumentresolvercomposite#getargumentresolver這個(gè)方法。
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); //如果緩存中已經(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來(lái)個(gè),瞬間覺(jué)得springmvc好強(qiáng)大啊,給人一種無(wú)論你在header里、cookie里、body里、還是path里,無(wú)論是什么類型的參數(shù)我都能給你解析了的霸氣。我們這里的匹配到的參數(shù)解析器是org.springframework.web.servlet.mvc.method.annotation.requestresponsebodymethodprocessor這個(gè)類。我們接著來(lái)看上面代碼中的(2)。resolveargument這個(gè)方法真的調(diào)用的就是requestresponsebodymethodprocessor這個(gè)類中的resolveargument的方法。我們進(jìn)入到這個(gè)方法中看一下:
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 { //這里是對(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]的地方。方法中的代碼如下:
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 { //獲取請(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è)方法很長(zhǎng),在這個(gè)方法中會(huì)獲取contenttype、參數(shù)的類型、method、重新封裝request等等的操作。我們需要關(guān)注這三行代碼:
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); |
為參數(shù)賦值的是[1]這行代碼。這里調(diào)用的是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的類型,為參數(shù)賦值 } |
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]調(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ì)掉到j(luò)ackson相關(guān)的jar中。再往下跟的話還有很深,說(shuō)實(shí)在的里面有很多的方法我還沒(méi)看明白,所以我們就不繼續(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的話,我們可以自己寫(xiě)一個(gè)簡(jiǎn)單的請(qǐng)求體的解析,但是在項(xiàng)目中最好別這樣做。代碼如下:
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" ; } |
請(qǐng)求信息如下:
后臺(tái)輸出結(jié)果如下:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:http://blog.csdn.net/zknxx/article/details/68952518