1. @RequestMapping
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径,RequestMapping注解有六个属性(value, method,consumes,produces,params,headers),RequestMapping("....str...")。
1.1. value, method
value: 指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明);
method: 指定请求的method类型, GET、POST、PUT、DELETE等;
value的uri值为以下三类:
A) 可以指定为普通的具体值;
B) 可以指定为含有某变量的一类值(URI Template Patterns with Path Variables);
C) 可以指定为含正则表达式的一类值( URI Template Patterns with Regular Expressions);
1.2. consumes,produces
consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
1.3. params,headers
params: 指定request中必须包含某些参数值是,才让该方法处理;
headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求;
2. @RequestBody
- 该注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上
- 再把HttpMessageConverter返回的对象数据绑定到 controller中方法的参数上
- GET、POST方式提时, 根据request header Content-Type的值来判断:
application/x-www-form-urlencoded, 可选(即非必须,因为这种情况的数据@RequestParam, @ModelAttribute也可以处理,当然@RequestBody也能处理);
multipart/form-data, 不能处理(即使用@RequestBody不能处理这种格式的数据);
其他格式, 必须(其他格式包括application/json, application/xml等。这些格式的数据,必须使用@RequestBody来处理);
- PUT方式提交时, 根据request header Content-Type的值来判断:
application/x-www-form-urlencoded, 必须;
multipart/form-data, 不能处理;
其他格式, 必须;
- 该注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上
- 再把HttpMessageConverter返回的对象数据绑定到 controller中方法的参数上
- GET、POST方式提时, 根据request header Content-Type的值来判断:
application/x-www-form-urlencoded, 可选(即非必须,因为这种情况的数据@RequestParam, @ModelAttribute也可以处理,当然@RequestBody也能处理);
multipart/form-data, 不能处理(即使用@RequestBody不能处理这种格式的数据);
其他格式, 必须(其他格式包括application/json, application/xml等。这些格式的数据,必须使用@RequestBody来处理); - PUT方式提交时, 根据request header Content-Type的值来判断:
application/x-www-form-urlencoded, 必须;
multipart/form-data, 不能处理;
其他格式, 必须;
注解源码:
说明:
- request的body部分的数据编码格式由header部分的Content-Type指定;
- 从源码中可以看到,@RequestBody 用在方法参数上面,用来将请求参数绑定
到request body中,通过HttpMessageConverter
封装为具体的JavaBean。通俗点讲就是你在一个参数上加上该注解,spring就会将request body中的json/xml对象解析成该参数类型的Javabean对象,是RESTful开发中经常用到的注解
3. @ResponseBody
用于将Controller的方法返回的对象,通过适当HttpMessageConverter
转换为指定格式后,写入到Response对象的body数据区。
使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;MappingJacksonHttpMessageConverter
调用了 objectMapper.writeValue(OutputStream stream, Object)
方法,使用@ResponseBody注解返回的对象就传入Object参数内。若返回的对象为已经格式化好的json串时,不使用@RequestBody注解,而应该这样处理:response.setContentType("application/json; charset=UTF-8");
response.getWriter().print(jsonStr);
直接输出到body区,然后的视图为void。
4. @RequestParam @RequestBody @PathVariable @RequestHeader @CookieValue @SessionAttributes @ModelAttribute 等参数绑定注解
handler method 参数绑定常用的注解,我们根据他们处理的Request的不同分为四类常用类型
A. 处理requet uri 部分(这里指uri template中variable,不含queryString部分)的注解: @PathVariable;
B、处理request header部分的注解: @RequestHeader, @CookieValue;
C、处理request body部分的注解:@RequestParam, @RequestBody;
D、处理attribute类型是注解: @SessionAttributes, @ModelAttribute;
4.1. @PathVariable
当使用@RequestMapping URI template 样式映射时, 即 someUrl/{paramId}, 这时的paramId可通过 @Pathvariable注解绑定它传过来的值到方法的参数上,若方法参数名称和需要绑定的uri template中变量名称不一致,需要在@PathVariable("name")指定uri template中的名称。
4.1. @RequestHeader
@RequestHeader 注解,可以把Request请求header部分的值绑定到方法的参数上。
示例代码:
这是一个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
request header部分的 Accept-Encoding的值,绑定到参数encoding上了, Keep-Alive header的值绑定到参数keepAlive上
4.2. @CookieValue
@CookieValue 可以把Request header中关于cookie的值绑定到方法的参数上
示例:
例如有如下Cookie值:JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84
参数绑定的代码:
4.3. @RequestParam
- @RequestParam常用来处理简单类型的绑定,通过Request.getParameter() 获取的String可直接转换为简单类型的情况( String--> 简单类型的转换操作由ConversionService配置的转换器来完成);因为使用request.getParameter()方式获取参数,所以可以处理get 方式中queryString的值,也可以处理post方式中 body data的值;
- @RequestParam用来处理Content-Type: 为 application/x-www-form-urlencoded编码的内容,提交方式GET、POST;
- @RequestParam该注解有两个属性: value、required; value用来指定要传入值的id名称,required用来指示参数是否必须绑定
@
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";
}
}
4.4. @SessionAttributes
该注解用来绑定HttpSession中的attribute对象的值,便于在方法中的参数里使用。
该注解有value、types两个属性,可以通过名字和类型指定要使用的attribute 对象;
4.5. @ModelAttribute
该注解有两个用法,一个是用于方法上,一个是用于参数上;
用于方法上时: 通常用来在处理@RequestMapping之前,为请求绑定需要从后台查询的model;
用于参数上时: 用来通过名称对应,把相应名称的值绑定到注解的参数bean上;要绑定的值来源于:
A) @SessionAttributes 启用的attribute 对象上;
B) @ModelAttribute 用于方法上时指定的model对象;
C) 上述两种情况都没有时,new一个需要绑定的bean对象,然后把request中按名称对应的方式把值绑定到bean中。
补充
通过分析AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter的源代码发现,方法的参数 在不给定参数的情况下:
- 若要绑定的对象时简单类型: 调用@RequestParam来处理的。
- 若要绑定的对象时复杂类型: 调用@ModelAttribute来处理的。
这里的简单类型指java的原始类型(boolean, int 等)、原始类型对象(Boolean, Int等)、String、Date等ConversionService里可以直接String转换成目标对象的类型;
AnnotationMethodHandlerAdapter中绑定参数的部分源代码
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) {
// 若没有发现注解
Object
argValue =
resolveCommonArgument(methodParam, webRequest);
//判断WebRquest是否可赋值给参数
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)) {
// 判断是否参数类型是否是简单类型,若是在使用@RequestParam方式来处理,否则使用@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中使用的参数绑定,代码稍微有些不同,有兴趣的同仁可以分析下,最后处理的结果都是一样的。
/**
* Annotation indicating a method parameter should be bound to the body of the web request.
* The body of the request is passed through an {@link HttpMessageConverter} to resolve the
* method argument depending on the content type of the request. Optionally, automatic
* validation can be applied by annotating the argument with {@code @Valid}.
*/
@
Target(
ElementType.
PARAMETER)
@
Retention(
RetentionPolicy.
RUNTIME)
@
Documented
public @
interface
RequestBody {
/**
* Whether body content is required.
* <p>Default is {@code true}, leading to an exception thrown in case
* there is no body content. Switch this to {@code false} if you prefer
* {@code null} to be passed when the body content is {@code null}.
* @since 3.2
*/
boolean
required()
default
true;
}
通过分析AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter的源代码发现,方法的参数 在不给定参数的情况下:
- 若要绑定的对象时简单类型: 调用@RequestParam来处理的。
- 若要绑定的对象时复杂类型: 调用@ModelAttribute来处理的。
这里的简单类型指java的原始类型(boolean, int 等)、原始类型对象(Boolean, Int等)、String、Date等ConversionService里可以直接String转换成目标对象的类型;
AnnotationMethodHandlerAdapter中绑定参数的部分源代码