参数解析器
-
初步了解 RequestMappingHandlerAdapter 的调用过程
-
控制器方法被封装为 HandlerMethod
-
准备对象绑定与类型转换
-
准备 ModelAndViewContainer 用来存储中间 Model 结果
-
解析每个参数值
-
-
解析参数依赖的就是各种参数解析器,它们都有两个重要方法
-
supportsParameter 判断是否支持方法参数
-
resolveArgument 解析方法参数
-
-
常见参数的解析
-
@RequestParam
-
省略 @RequestParam
-
@RequestParam(defaultValue)
-
MultipartFile
-
@PathVariable
-
@RequestHeader
-
@CookieValue
-
@Value
-
HttpServletRequest 等
-
@ModelAttribute
-
省略 @ModelAttribute
-
@RequestBody
-
-
组合模式在 Spring 中的体现
-
@RequestParam, @CookieValue 等注解中的参数名、默认值, 都可以写成活的, 即从 ${ } #{ }中获取
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();// 准备测试 RequestHttpServletRequest request = mockRequest();// 要点1. 控制器方法被封装为 HandlerMethodHandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));// 要点2. 准备对象绑定与类型转换ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);// 要点3. 准备 ModelAndViewContainer 用来存储中间 Model 结果ModelAndViewContainer container = new ModelAndViewContainer();// 要点4. 解析每个参数值for (MethodParameter parameter : handlerMethod.getMethodParameters()) {// 多个解析器组合HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();composite.addResolvers(// false 表示必须有 @RequestParamnew RequestParamMethodArgumentResolver(beanFactory, false),new PathVariableMethodArgumentResolver(),new RequestHeaderMethodArgumentResolver(beanFactory),new ServletCookieValueMethodArgumentResolver(beanFactory),new ExpressionValueMethodArgumentResolver(beanFactory),new ServletRequestMethodArgumentResolver(),new ServletModelAttributeMethodProcessor(false), // 必须有 @ModelAttributenew RequestResponseBodyMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())),new ServletModelAttributeMethodProcessor(true), // 省略了 @ModelAttributenew RequestParamMethodArgumentResolver(beanFactory, true) // 省略 @RequestParam);String annotations = Arrays.stream(parameter.getParameterAnnotations()).map(a -> a.annotationType().getSimpleName()).collect(Collectors.joining());String str = annotations.length() > 0 ? " @" + annotations + " " : " ";parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());if (composite.supportsParameter(parameter)) {// 支持此参数Object v = composite.resolveArgument(parameter, container, new ServletWebRequest(request), factory);
// System.out.println(v.getClass());System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName() + "->" + v);System.out.println("模型数据为:" + container.getModel());} else {System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName());}}
参数解析器的先后顺序也有要求,先解析需要带注解的参数,后解析不需要带注解的,不需要带注解的解析器也有顺序,先解析是对象不带注解的,后解析普通参数不带注解的
参数名的获取
javac命令
- 如果编译时添加了 -parameters 可以生成参数表, 反射时就可以拿到参数名
-
如果编译时添加了 -g 可以生成调试信息, 但分为两种情况
- 普通类, 会包含局部变量表, 用 asm 可以拿到参数名
- 接口, 不会包含局部变量表, 无法获得参数名,这也是 MyBatis 在实现 Mapper 接口时为何要提供 @Param 注解来辅助获得参数名
public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException {// 1. 反射获取参数名Method foo = Bean2.class.getMethod("foo", String.class, int.class);/*for (Parameter parameter : foo.getParameters()) {System.out.println(parameter.getName());}*/// 2. 基于 LocalVariableTable 本地变量表LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();String[] parameterNames = discoverer.getParameterNames(foo);System.out.println(Arrays.toString(parameterNames));}
spring中组合了这两种方法获取参数,类名:DefaltParameterNameDiscover
本地变量表的局限性:对于接口里的方法获取不到方法的参数