SpringMVC參數(shù)綁定

轉(zhuǎn)自:http://blog.csdn.net/walkerjong/article/details/7946109

簡(jiǎn)介:

handler method 參數(shù)綁定常用的注解,我們根據(jù)他們處理的Request的不同內(nèi)容部分分為四類(lèi):(主要講解常用類(lèi)型)

  • @PathVariable:處理requet uri(這里指uri template中variable,不含queryString部分)
  • @RequestHeader涡驮、@CookieValue:處理request header部分
  • @RequestParam骄噪、@RequestBody:處理request body部分
  • @SessionAttributes共苛、@ModelAttribute:處理attribute類(lèi)型
@PathVariable

當(dāng)使用@RequestMapping URI template 樣式映射時(shí)嗤栓, 即 someUrl/{paramId},這時(shí)的paramId可通過(guò) @Pathvariable注解綁定它傳過(guò)來(lái)的值到方法的參數(shù)上

示例代碼:

@Controller  
@RequestMapping("/owners/{ownerId}")  
public class RelativePathUriTemplateController {  
    @RequestMapping("/pets/{petId}")  
    public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model){      
    // implementation omitted  
    }  
}  

上面代碼把URI template 中變量 ownerId的值和petId的值印颤,綁定到方法的參數(shù)上。若方法參數(shù)名稱(chēng)和需要綁定的uri template中變量名稱(chēng)不一致郊艘,需要在@PathVariable("name")指定uri template中的名稱(chēng)

@RequestHeader僚祷、@CookieValue

@RequestHeader

  • 可以把Request請(qǐng)求header部分的值綁定到方法的參數(shù)上
  • 這是一個(gè)Request 的header部分:
Host                    localhost:8080  
Accept                  text/html,application/xhtml+xml,application/xml;q=0.9  
Accept-Language         fr,en-gb;q=0.7,en;q=0.3  
Accept-Encoding         gzip,deflate  
Accept-Charset          ISO-8859-1,utf-8;q=0.7,*;q=0.7  
Keep-Alive              300  
@RequestMapping("/displayHeaderInfo.do")  
public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding,  
                                @RequestHeader("Keep-Alive") long keepAlive)  {  
    //...
}  

上面的代碼尺锚,把request header部分的Accept-Encoding的值綁定到參數(shù)encoding上酷师,Keep-Alive header的值綁定到參數(shù)keepAlive上

@CookieValue

  • 可以把Request header中關(guān)于cookie的值綁定到方法的參數(shù)上
  • 例如有如下Cookie值:
JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84  

參數(shù)綁定的代碼:

@RequestMapping("/displayHeaderInfo.do")  
public void displayHeaderInfo(@CookieValue("JSESSIONID") String cookie){  
    //...
}  

即把JSESSIONID的值綁定到參數(shù)cookie上

@RequestParam讶凉、@RequestBody

@RequestParam

  • 常用來(lái)處理簡(jiǎn)單類(lèi)型的綁定,通過(guò)Request.getParameter()獲取的String可直接轉(zhuǎn)換為簡(jiǎn)單類(lèi)型的情況(String-->簡(jiǎn)單類(lèi)型的轉(zhuǎn)換操作由ConversionService配置的轉(zhuǎn)換器來(lái)完成)山孔;因?yàn)槭褂胷equest.getParameter()方式獲取參數(shù)懂讯,所以可以處理get 方式中queryString的值,也可以處理post方式中body data的值
  • 用來(lái)處理Content-Type為:application/x-www-form-urlencoded編碼的內(nèi)容台颠,提交方式GET褐望、POST
  • 該注解有兩個(gè)屬性:value、required:value用來(lái)指定要傳入值的id名稱(chēng)串前,required用來(lái)指示參數(shù)是否必須綁定
  • 示例代碼:
@Controller  
@RequestMapping("/pets")  
@SessionAttributes("pet")  
public class EditPetForm{
    @RequestMapping(method = RequestMethod.GET)  
    public String setupForm(@RequestParam("petId") int petId, ModelMap model){
        Pet pet = this.clinic.loadPet(petId);  
        model.addAttribute("pet", pet);  
        return "petForm";  
    }
}

@RequestBody

  • 常用來(lái)處理Content-Type不是application/x-www-form-urlencoded編碼的內(nèi)容瘫里,例如application/json、application/xml等
  • 它是通過(guò)使用HandlerAdapter配置的HttpMessageConverters來(lái)解析post data body荡碾,然后綁定到相應(yīng)的bean上的谨读;
    因?yàn)榕渲糜蠪ormHttpMessageConverter,所以也可以用來(lái)處理application/x-www-form-urlencoded的內(nèi)容玩荠,處理完的結(jié)果放在一個(gè)MultiValueMap<String, String>里漆腌,這種情況在某些特殊需求下使用贼邓,詳情查看FormHttpMessageConverter api
  • 示例代碼:
@RequestMapping(value = "/something", method = RequestMethod.PUT)  
public void handle(@RequestBody String body, Writer writer) throws IOException{
    writer.write(body);  
}  
@SessionAttributes阶冈、@ModelAttribute

@SessionAttributes

  • 用來(lái)綁定HttpSession中的attribute對(duì)象的值,便于在方法中的參數(shù)里使用塑径,有value女坑、types兩個(gè)屬性,可以通過(guò)名字和類(lèi)型指定要使用的attribute對(duì)象
  • 示例代碼:
@Controller  
@RequestMapping("/editPet.do")  
@SessionAttributes("pet")  
public class EditPetForm {  
    // ...  
}  

@ModelAttribute

  • 該注解有兩個(gè)用法统舀,一個(gè)是用于方法上匆骗,一個(gè)是用于參數(shù)上:
    用于方法上時(shí): 通常用來(lái)在處理@RequestMapping之前,為請(qǐng)求綁定需要從后臺(tái)查詢(xún)的model
    用于參數(shù)上時(shí): 用來(lái)通過(guò)名稱(chēng)對(duì)應(yīng)誉简,把相應(yīng)名稱(chēng)的值綁定到注解的參數(shù)bean上
  • 綁定的值來(lái)源于:
    A) @SessionAttributes啟用的attribute 對(duì)象上
    B) @ModelAttribute用于方法上時(shí)指定的model對(duì)象
    C) 上述兩種情況都沒(méi)有時(shí)碉就,new一個(gè)需要綁定的bean對(duì)象,然后把request中按名稱(chēng)對(duì)應(yīng)的方式把值綁定到bean中闷串。

用到方法上@ModelAttribute的示例代碼:

// Add one attribute  
// The return value of the method is added to the model under the name "account"  
// You can customize the name via @ModelAttribute("myAccount")  
@ModelAttribute  
public Account addAccount(@RequestParam String number) {  
    return accountManager.findAccount(number);  
}  

這種方式實(shí)際的效果就是在調(diào)用@RequestMapping的方法之前瓮钥,為request對(duì)象的model里put("account", Account)

用在參數(shù)上的@ModelAttribute示例代碼:

@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)  
public String processSubmit(@ModelAttribute Pet pet) {  
     
}  

首先查詢(xún) @SessionAttributes有無(wú)綁定的Pet對(duì)象,若沒(méi)有則查詢(xún)@ModelAttribute方法層面上是否綁定了Pet對(duì)象,若沒(méi)有則將URI template中的值按對(duì)應(yīng)的名稱(chēng)綁定到Pet對(duì)象的各屬性上

補(bǔ)充講解:

問(wèn)題: 在不給定注解的情況下碉熄,參數(shù)是怎樣綁定的桨武?

通過(guò)分析AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter的源代碼發(fā)現(xiàn),方法的參數(shù)在不給定參數(shù)的情況下:

若要綁定的對(duì)象時(shí)簡(jiǎn)單類(lèi)型: 調(diào)用@RequestParam來(lái)處理的

若要綁定的對(duì)象時(shí)復(fù)雜類(lèi)型: 調(diào)用@ModelAttribute來(lái)處理的

這里的簡(jiǎn)單類(lèi)型指Java的原始類(lèi)型(boolean, int 等)锈津、原始類(lèi)型對(duì)象(Boolean, Int等)呀酸、String、Date等ConversionService里可以直接String轉(zhuǎn)換成目標(biāo)對(duì)象的類(lèi)型

下面貼出AnnotationMethodHandlerAdapter中綁定參數(shù)的部分源代碼:

private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
                                         NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {

    Class[] paramTypes = handlerMethod.getParameterTypes();
    Object[] args = new Object[paramTypes.length];

    for (int i = 0; i < args.length; i++) {
        MethodParameter methodParam = new MethodParameter(handlerMethod, i);
        methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
        GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
        String paramName = null;
        String headerName = null;
        boolean requestBodyFound = false;
        String cookieName = null;
        String pathVarName = null;
        String attrName = null;
        boolean required = false;
        String defaultValue = null;
        boolean validate = false;
        Object[] validationHints = null;
        int annotationsFound = 0;
        Annotation[] paramAnns = methodParam.getParameterAnnotations();

        for (Annotation paramAnn : paramAnns) {
            if (RequestParam.class.isInstance(paramAnn)) {
                RequestParam requestParam = (RequestParam) paramAnn;
                paramName = requestParam.value();
                required = requestParam.required();
                defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
                annotationsFound++;
            } else if (RequestHeader.class.isInstance(paramAnn)) {
                RequestHeader requestHeader = (RequestHeader) paramAnn;
                headerName = requestHeader.value();
                required = requestHeader.required();
                defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
                annotationsFound++;
            } else if (RequestBody.class.isInstance(paramAnn)) {
                requestBodyFound = true;
                annotationsFound++;
            } else if (CookieValue.class.isInstance(paramAnn)) {
                CookieValue cookieValue = (CookieValue) paramAnn;
                cookieName = cookieValue.value();
                required = cookieValue.required();
                defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
                annotationsFound++;
            } else if (PathVariable.class.isInstance(paramAnn)) {
                PathVariable pathVar = (PathVariable) paramAnn;
                pathVarName = pathVar.value();
                annotationsFound++;
            } else if (ModelAttribute.class.isInstance(paramAnn)) {
                ModelAttribute attr = (ModelAttribute) paramAnn;
                attrName = attr.value();
                annotationsFound++;
            } else if (Value.class.isInstance(paramAnn)) {
                defaultValue = ((Value) paramAnn).value();
            } else if (paramAnn.annotationType().getSimpleName().startsWith("Valid")) {
                validate = true;
                Object value = AnnotationUtils.getValue(paramAnn);
                validationHints = (value instanceof Object[] ? (Object[]) value : new Object[]{value});
            }
        }

        if (annotationsFound > 1) {
            throw new IllegalStateException("Handler parameter annotations are exclusive choices - " +
                    "do not specify more than one such annotation on the same parameter: " + handlerMethod);
        }

        if (annotationsFound == 0) {// 若沒(méi)有發(fā)現(xiàn)注解
            Object argValue = resolveCommonArgument(methodParam, webRequest);    //判斷WebRquest是否可賦值給參數(shù)
            if (argValue != WebArgumentResolver.UNRESOLVED) {
                args[i] = argValue;
            } else if (defaultValue != null) {
                args[i] = resolveDefaultValue(defaultValue);
            } else {
                Class<?> paramType = methodParam.getParameterType();
                if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
                    if (!paramType.isAssignableFrom(implicitModel.getClass())) {
                        throw new IllegalStateException("Argument [" + paramType.getSimpleName() + "] is of type " +
                                "Model or Map but is not assignable from the actual model. You may need to switch " +
                                "newer MVC infrastructure classes to use this argument.");
                    }
                    args[i] = implicitModel;
                } else if (SessionStatus.class.isAssignableFrom(paramType)) {
                    args[i] = this.sessionStatus;
                } else if (HttpEntity.class.isAssignableFrom(paramType)) {
                    args[i] = resolveHttpEntityRequest(methodParam, webRequest);
                } else if (Errors.class.isAssignableFrom(paramType)) {
                    throw new IllegalStateException("Errors/BindingResult argument declared " +
                            "without preceding model attribute. Check your handler method signature!");
                } else if (BeanUtils.isSimpleProperty(paramType)) {// 判斷是否參數(shù)類(lèi)型是否是簡(jiǎn)單類(lèi)型琼梆,若是在使用@RequestParam方式來(lái)處理,否則使用@ModelAttribute方式處理
                    paramName = "";
                } else {
                    attrName = "";
                }
            }
        }

        if (paramName != null) {
            args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
        } else if (headerName != null) {
            args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
        } else if (requestBodyFound) {
            args[i] = resolveRequestBody(methodParam, webRequest, handler);
        } else if (cookieName != null) {
            args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
        } else if (pathVarName != null) {
            args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
        } else if (attrName != null) {
            WebDataBinder binder =
                    resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
            boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
            if (binder.getTarget() != null) {
                doBind(binder, webRequest, validate, validationHints, !assignBindingResult);
            }
            args[i] = binder.getTarget();
            if (assignBindingResult) {
                args[i + 1] = binder.getBindingResult();
                i++;
            }
            implicitModel.putAll(binder.getBindingResult().getModel());
        }
    }
    return args;
}

RequestMappingHandlerAdapter中使用的參數(shù)綁定性誉,代碼稍微有些不同,有興趣的同仁可以分析下叮叹,最后處理的結(jié)果都是一樣的

示例:

@RequestMapping({"/", "/home"})  
public String showHomePage(String key){  
    logger.debug("key="+key);  
    return "home";  
}  

這種情況下艾栋,就調(diào)用默認(rèn)的@RequestParam來(lái)處理

@RequestMapping(method = RequestMethod.POST)  
public String doRegister(User user){  
    if(logger.isDebugEnabled()){  
        logger.debug("process url[/user], method[post] in "+getClass());  
        logger.debug(user);  
    } 
    return "user";  
}  

這種情況下,就調(diào)用@ModelAttribute來(lái)處理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蛉顽,一起剝皮案震驚了整個(gè)濱河市蝗砾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌携冤,老刑警劉巖悼粮,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異曾棕,居然都是意外死亡扣猫,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)翘地,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)申尤,“玉大人,你說(shuō)我怎么就攤上這事衙耕∶链” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵橙喘,是天一觀的道長(zhǎng)时鸵。 經(jīng)常有香客問(wèn)我,道長(zhǎng)厅瞎,這世上最難降的妖魔是什么饰潜? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮和簸,結(jié)果婚禮上彭雾,老公的妹妹穿的比我還像新娘。我一直安慰自己锁保,他們只是感情好薯酝,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布南誊。 她就那樣靜靜地躺著,像睡著了一般蜜托。 火紅的嫁衣襯著肌膚如雪抄囚。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天橄务,我揣著相機(jī)與錄音幔托,去河邊找鬼。 笑死蜂挪,一個(gè)胖子當(dāng)著我的面吹牛重挑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播棠涮,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼谬哀,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了严肪?” 一聲冷哼從身側(cè)響起史煎,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎驳糯,沒(méi)想到半個(gè)月后篇梭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡酝枢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年恬偷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帘睦。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡袍患,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出竣付,到底是詐尸還是另有隱情诡延,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布卑笨,位于F島的核電站孕暇,受9級(jí)特大地震影響仑撞,放射性物質(zhì)發(fā)生泄漏赤兴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一隧哮、第九天 我趴在偏房一處隱蔽的房頂上張望桶良。 院中可真熱鬧,春花似錦沮翔、人聲如沸陨帆。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)疲牵。三九已至承二,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纲爸,已是汗流浹背亥鸠。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留识啦,地道東北人负蚊。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像颓哮,于是被迫代替她去往敵國(guó)和親家妆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Spring的模型-視圖-控制器(MVC)框架是圍繞一個(gè)DispatcherServlet來(lái)設(shè)計(jì)的冕茅,這個(gè)Servl...
    alexpdh閱讀 2,649評(píng)論 0 3
  • 1伤极、@Controller 在SpringMVC 中,控制器Controller 負(fù)責(zé)處理由DispatcherS...
    圣光會(huì)制裁你丨閱讀 1,675評(píng)論 0 23
  • 了解springmvc是如何進(jìn)行參數(shù)綁定的,使用idea對(duì)springmvc源碼進(jìn)行逐步調(diào)試,源碼版本4.3.2 ...
    lialzm閱讀 3,123評(píng)論 3 7
  • @RequestParam 綁定單個(gè)請(qǐng)求 @RequestParam 有required(是否必填),defaul...
    lialzm閱讀 2,368評(píng)論 0 9
  • 本次旅行的前5天姨伤,每一天我們都興奮至極塑荒,奔跑于草原,騎馬姜挺,射箭齿税,喂小羊,各種姿勢(shì)的美拍炊豪,拍花凌箕,拍草,拍風(fēng)景词渤,更多的...
    超人媽媽_68eb閱讀 404評(píng)論 0 0