- 在
HttpServlet
中,可以通过request.getParameter()
获取请求传入的参数、通过request.getHeader()
获取请求头内容、通过request.getRequestURL()
获取请求的URL。 - 而在Spring MVC中,可以直接通过注解的方式获取请求相关的各类信息。
- Spring MVC 框架会将 HTTP 请求的信息绑定到相应的方法入参中,并根据方法的返回值类型做出相应的后续处理。
@RequestParam注解
- 在处理方法入参处使用
@RequestParam
可以把请求参数传递给请求方法
-
value
:参数名 -
required
:是否必须。默认为 true, 表示请求参数中必须包含对应的参数,若不存在,将抛出异常 -
defaultValue
: 默认值,当没有传递参数时使用该值
代码
@RequestMapping("/requestParamTest")
String requestParamTest(@RequestParam(value = "id", defaultValue = "0") Integer id,
@RequestParam(value = "name", required = true) String name) {
System.out.println("id : " + id);
System.out.println("name : " + name);
System.out.println("Person : " + new Person(name, id));
return "hello";
}
<form action="requestParamTest" method="get">
<label>id
<input type="text" name="id"/>
</label><br/>
<label>name
<input type="text" name="name"/>
</label><br/>
<input type="submit" value="submit">
</form><br/>
通过
@RequestParam
的value
值和input标签的name
值对应。
@RequestMapping("/requestParamTest3")
String requestParamTest3(Integer id, String name) {
System.out.println("id : " + id);
System.out.println("name : " + name);
System.out.println("Person : " + new Person(name, id));
return "hello";
}
<form action="requestParamTest3" method="get">
<label>id
<input type="text" name="id"/>
</label><br/>
<label>name
<input type="text" name="name"/>
</label><br/>
<input type="submit" value="submit">
</form><br/>
当@
RequestParam
的value
值和input标签的name
值相同时,注解可以省略
@RequestHeader 注解
- 通过
@RequestHeader
即可将请求头中的属性值绑定到处理方法的入参中
代码
@RequestMapping("/requestHeaderTest")
String requestHeaderTest(@RequestHeader("Accept-Language") String acceptLanguage,
@RequestHeader("User-Agent")String userAgent) {
System.out.println("Accept-Language : " + acceptLanguage);
System.out.println("User-Agent : " + userAgent);
return "hello";
}
@CookieValue 注解
-
@CookieValue
可让处理方法入参绑定某个 Cookie 值
代码
@RequestMapping("/cookieValueTest")
String cookieValueTest(@CookieValue(value="JSESSIONID", required=false) String JSESSIONID) {
System.out.println("JSESSIONID : " + JSESSIONID);
return "hello";
}
@PathVariable 注解
- 常用于Rest风格的参数传入。在URL上添加参数,然后在处理的时候,直接将参数提取,作为参数输入到方法上。
代码
@GetMapping("/testPathVariable/{variable}")
public String testPathVariable(@PathVariable Integer variable) {
log.info(variable.toString());
return variable.toString();
}
http://localhost:8080/testPathVariable/10
输入时,variable
为 10
@RequestBody 注解
- 当注解方法入参的时候,如果是对应的Java Bean,就会对Java Bean的属性进行注入;如果参数是String类型,就会将整个请求体交给该参数。
-
@RequestBody
标注,可以处理json格式的请求体
代码
@RequestMapping("/getJsonTest")
public String getJsonTest(@RequestBody Employee employee) {
System.out.println(employee);
return "success";
}
@RequestMapping("/requestBodyTest")
public String requestBodyTest(@RequestBody String requestBody) {
System.out.println(requestBody); // {"id":0,"name":"john","gender":"male"}
return "success";
}
@MatrixVariable 注解
- 可以实现通过另一种方式,传递参数。在路径变量上,通过
;
分割,添加多个参数。如http://localhost:8080/testMatrixVariable/a;mv1=b;mv2=c
。
@GetMapping("/testMatrixVariable/{pathVariable}")
public Map<String, Object> testMatrixVariable(@PathVariable String pathVariable,
@MatrixVariable("mv1") String mv1,
@MatrixVariable("mv2") String mv2) {
log.info(pathVariable); // a
log.info(mv1); // b
log.info(mv2); // c
Map<String, Object> map = new HashMap<>();
map.put("pathVariable", pathVariable);
map.put("mv1", mv1);
map.put("mv2", mv2);
return map;
}
- 但是,该方法默认不能实现。因为底层有一个
UrlPathHelper
用于处理请求过来的Url,默认将private boolean removeSemicolonContent = true;
,将分号后面的内容去除。因此,还需要自己配置,将该值设为false
@Configuration(proxyBeanMethods = false)
public class Config {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
};
}
}
使用POJO作为参数
- Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值。支持级联属性
代码
// java bean
public class Father extends Person {
private Person son;
/*忽略get、set、toString方法*/
// java bean
public class Person {
private Integer id;
private String name;
/*忽略get、set、toString方法*/
}
@RequestMapping("requestParamTest2")
String requestParamTest2(Person person) {
System.out.println("Person : " + person);
return "hello";
}
<form action="requestParamTest2" method="get">
<label>id
<input type="text" name="id"/>
</label><br/>
<label>name
<input type="text" name="name"/>
</label><br/>
<input type="submit" value="submit">
</form><br/>
Spring MVC会根据
input
标签的name
匹配 pojo 的属性。
@RequestMapping("/requestParamTest4")
String requestParamTest4(Father father) {
System.out.println("father : " + father);
return "hello";
}
<form action="requestParamTest4" method="post">
<label>id
<input type="text" name="id"/>
</label><br/>
<label>name
<input type="text" name="name"/>
</label><br/>
<label>son.id
<input type="text" name="son.id"/>
</label><br/>
<label>son.name
<input type="text" name="son.name"/>
</label><br/>
<input type="submit" value="submit">
</form><br/>
级联的pojo,通过
属性名.属性名
的方式定位。
使用Servlet原生API作为参数
- MVC 的
Handler
方法(Controller
中的方法)可以接受的ServletAPI 类型的参数
-
HttpServletRequest
:就是Servlet中的那个request
-
HttpServletResponse
:就是Servlet中的那个response
-
HttpSession
:就是通过request.getSession()
获取的那个session
java.security.Principal
Locale
-
InputStream
:就是通过request.getInputStream()
获取的那个InputStream
-
OutputStream
:就是通过response.getOutputStream()
获取的那个OutputStream
-
Reader
:就是通过request.getReader()
获取的那个Reader
-
Writer
:就是通过response.getWriter()
获取的那个Writer
@RequestMapping("/rawApiTest")
String rawApiTest(Locale locale) {
System.out.println(locale);
return "hello";
}
源代码解析
参数的传入过程
- 重点是一个叫做
HandlerMethodArgumentResolver
的参数解析器。通过该解析器,将传入的参数与方法参数进行一一绑定。 - 执行步骤如下:
- 在
DispatcherServlet
中的doDispatch()
方法,真正执行处理请求。而到了真正方法执行的一步,会先查找参数,再通过反射执行方法 doDispatch()
,执行方法- 适配器中执行方法
- 依旧在适配器内,将所有的参数解析器放入要被一个叫
invocableMethod
的对象中(该对象存储了要执行的方法和参数之类的信息)。此时有27个不同的参数解析器。 - 再次进入一个要执行方法的对象
- 该方法里面,又有一个执行方法的方法
- 然后就先解析参数,再执行方法。这个应该是最重要的过程了。
- 首先,在
getArgumentResolver
方法中找到指定的解析器,然后调用解析器的resolveArgument
方法,解析参数。 - 而找到解析器的步骤也很简单,就是遍历所有解析器,看哪个支持,就直接返回该解析器。
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
} else {
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
}
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);
if (result == null) {
Iterator var3 = this.argumentResolvers.iterator();
while(var3.hasNext()) {
HandlerMethodArgumentResolver resolver = (HandlerMethodArgumentResolver)var3.next();
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, resolver);
break;
}
}
}
return result;
}
- 解析参数,如今就进入 了一个抽象的参数解析器内部了
- 进入到具体的参数解析器内部,并在request中找到了想要的参数。
- 至此,参数解析就完成了。