RequestMappingHandlerAdapter的主要功能解析参数和返回结果,解析参数RequestMappingHandlerAdapter内部会调用HandlerMethodReturnValueHandler组件对参数进行解析。本章节讲述@RequestParam的解析过程模拟
RequestParamMethodArgumentResolver是解析@RequestParam注解的解析器,
1,创建处理器
RequestParamMethodArgumentResolver resolver=new RequestParamMethodArgumentResolver(beanFactory,false);
RequestParamMethodArgumentResolver传入参数解析
public RequestParamMethodArgumentResolver(@Nullable ConfigurableBeanFactory beanFactory, boolean useDefaultResolution) {
super(beanFactory);
this.useDefaultResolution = useDefaultResolution;
}
ConfigurableBeanFactory:工厂类型里面会有许多后处理器,有时候参数解析的时候会使用到这些后处理器,所有这里需要传进来一个工厂,用来提供后处理器
boolean:false表示一定要有@RequestParam这个注解才能解析,true不用
这里在提一句
这个参数解析器可以解析有@RequestParam注解的和不带@RequestParam注解的,他们之间的区别是@RequestParam注解的内部名字要和传过来的一样,而不加注解的变量名要一样
调用了resolver.supportsParameter(MethodParameter parameter)方法看所传进来的参数是否是@RequestParam类型的是的话就进行解析,不会则不解析
从下面这段源码我们可以看出会判断参数注解是否是@RequestParam是则返回true
不是则返回false
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(RequestParam.class)) {
if (!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
return true;
} else {
RequestParam requestParam = (RequestParam)parameter.getParameterAnnotation(RequestParam.class);
return requestParam != null && StringUtils.hasText(requestParam.name());
}
} else if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
} else {
parameter = parameter.nestedIfOptional();
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
} else {
return this.useDefaultResolution ? BeanUtils.isSimpleProperty(parameter.getNestedParameterType()) : false;
}
}
}
resolveArgument方法是进行参数解析并且得到结果的,需要传入的四个参数这里特别说明一下
resolver.resolveArgument((MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory)
MethodParameter parameter:是上一篇文章中有提到到RequestMappingHandlerMapping里面的value属性HandlerMethod,里面封装了方法等信息
ModelAndViewContainer:准备ModelAndVieywContainer来存在Model短暂的结果,为model准备的前后端分离以后不再引用视图方法。
NativeWebRequest:前端传入request的信息会被存放到这里供后面参数解析器进行解析调用。
WebDataBinderFactory:准备类型转换器,可以把传过来的数值类型转换为自己定义的类型,例如传进来的参数是字符串但是我们接收的时候使用int类型接收的这时候WebDataBinderFactory就会进行类型转换。这也是为什么我们传参和接收参数的类型不用一样。
重点代码解析
for (MethodParameter methodParameter : handlerMethod.getMethodParameters()) {
//1,引入参数解析器
//false表示一定要有@RequestParam这个注解才能解析,true不用
//这个参数解析器可以解析有@RequestParam注解的和不带@RequestParam注解的,他们之间的区别是@RequestParam注解的内部名字要和传过来的一样,而不加注解的变量名要一样
RequestParamMethodArgumentResolver resolver=new RequestParamMethodArgumentResolver(beanFactory,false);
//2.判断是否该参数解析器是否可以解析这个参数
if (resolver.supportsParameter(methodParameter)) {
//3,可以解析调用resolveArgument()方法进行解析,并且返回参数
Object argument = resolver.resolveArgument(methodParameter, modelAndViewContainer, new ServletWebRequest(request), defaultDataBinderFactory);
}
代码演示
public class Dis02 {
@SneakyThrows
public static void main(String[] args) {
AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(WebConfig.class);
//里面的后处理器会提供解析功能${JAVA_HOME}
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
//准备Request
HttpServletRequest request= mock();
//控制器方法,HandlerMethod
HandlerMethod handlerMethod=new HandlerMethod(new Controller(),Controller.class.getMethod("test", String.class, String.class, int.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, com.example.mvc01.dispatcherServlet.Controller.User.class, com.example.mvc01.dispatcherServlet.Controller.User.class, com.example.mvc01.dispatcherServlet.Controller.User.class));
//准备类型转换器,可以把传过来的数值类型转换为自己定义的类型
DefaultDataBinderFactory defaultDataBinderFactory=new DefaultDataBinderFactory(new ConfigurableWebBindingInitializer());
//准备ModelAndVieywContainer来存在Model短暂的结果
ModelAndViewContainer modelAndViewContainer=new ModelAndViewContainer();
//解析每个参数值
//获取每个参数
for (MethodParameter methodParameter : handlerMethod.getMethodParameters()) {
//1,引入参数解析器
//false表示一定要有@RequestParam这个注解才能解析,true不用
//这个参数解析器可以解析有@RequestParam注解的和不带@RequestParam注解的,他们之间的区别是@RequestParam注解的内部名字要和传过来的一样,而不加注解的变量名要一样
RequestParamMethodArgumentResolver resolver=new RequestParamMethodArgumentResolver(beanFactory,false);
//2.判断是否该参数解析器是否可以解析这个参数
if (resolver.supportsParameter(methodParameter)) {
//3,可以解析调用resolveArgument()方法进行解析,并且返回参数
Object argument = resolver.resolveArgument(methodParameter, modelAndViewContainer, new ServletWebRequest(request), defaultDataBinderFactory);
}
}
}
//模拟发送数据
private static HttpServletRequest mock(){
MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest();
mockHttpServletRequest.setParameter("name1","李四");
mockHttpServletRequest.setParameter("name2","张三");
mockHttpServletRequest.addPart(new MockPart("file","abc","hello".getBytes(StandardCharsets.UTF_8)));
Map<String, String> stringStringMap = new AntPathMatcher().extractUriTemplateVariables("/test/{id}", "/test/123");
mockHttpServletRequest.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE,stringStringMap);
mockHttpServletRequest.setContentType("application/json");
mockHttpServletRequest.setCookies(new Cookie("token","123456"));
mockHttpServletRequest.setParameter("name","wangwu");
mockHttpServletRequest.setParameter("age","18");
mockHttpServletRequest.setContent(null);
return new StandardMultipartHttpServletRequest(mockHttpServletRequest);
}
static class Controller{
public void test(@RequestParam("name1") String name1,
String name2,
@RequestParam("age")int age,
@RequestParam("file") MultipartFile file,
@PathVariable("id")int id,
@CookieValue("token")String token,
@RequestParam(name = "home",defaultValue = "${JAVA_HOME}")String home1,
@RequestHeader("content-Type") String header,
HttpServletRequest httpServletRequest,
@ModelAttribute com.example.mvc01.dispatcherServlet.Controller.User user1,
com.example.mvc01.dispatcherServlet.Controller.User user2,
@RequestBody com.example.mvc01.dispatcherServlet.Controller.User user3){
}
}
}
本文中有涉及到spring的后处理器,想要了解的可以在评论区留言。单独出一篇文章讲解
以下是对RequestMappingHandlerAdapter的详解,可以观看哦
SpringMVC九大组件之二RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter是如何配合使用的