轉(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)處理