【原创】遨游springmvc之HandlerMethodArgumentResolver
2016-08-13 22:48
393 查看
1.前言
记得大三刚开始接触springmvc的时候,我们总是会写如下方法public String doSomethine(HttpSerlvetRequest request){ //doSomethine }
然后现在看了这简直看不下去。
因为:1.加重了我们对请求传过来来的值的取值代码,会使控制器中request.getParamater()之类的代码越来越多;2.不利于测试;3.request.getParamater()只能获取string,如果是Long等其他类型的参数还需要强转,使用起来非常不方便。
所以springmvc从3.1开始便加强了这方便的功能,那就是HandlerMethodArgumentResolver,springmvc通过HandlerMethodArgumentResolver对传入的参数进行了一些列的装配绑定。
2.原理
2.1 接口说明
HandlerMethodArgumentResolver只有2个方法,supportParameter()决定了传入的参数是否启用该解析器,resolveArgument则是真正解析参数的过程,并且返回。源码2.1.1
public interface HandlerMethodArgumentResolver { /** * Whether the given {@linkplain MethodParameter method parameter} is * supported by this resolver. * @param parameter the method parameter to check * @return {@code true} if this resolver supports the supplied parameter; * {@code false} otherwise */ boolean supportsParameter(MethodParameter parameter); /** * Resolves a method parameter into an argument value from a given request. * A {@link ModelAndViewContainer} provides access to the model for the * request. A {@link WebDataBinderFactory} provides a way to create * a {@link WebDataBinder} instance when needed for data binding and * type conversion purposes. * @param parameter the method parameter to resolve. This parameter must * have previously been passed to {@link #supportsParameter} which must * have returned {@code true}. * @param mavContainer the ModelAndViewContainer for the current request * @param webRequest the current request * @param binderFactory a factory for creating {@link WebDataBinder} instances * @return the resolved argument value, or {@code null} * @throws Exception in case of errors with the preparation of argument values */ Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception; }
2.2 原理介绍
在我们对springmvc的实际使用中,经常会看到@RequestParam、@PathVariable、@ModelAttribute等注解在某个控制器方法的参数前面,springmvc通过这些注解在HandlerMethodArgumentResolver中的supportParameter()中进行判定,去寻找对应的参数解析器,并在解析器程序中处理了参数绑定的一些逻辑。springmvc在适配器RequestMappingHandlerAdapter中加入了一系列默认的参数解析器
源码2.2.1
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); // Annotation-based argument resolution resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new PathVariableMethodArgumentResolver()); resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new MatrixVariableMethodArgumentResolver()); resolvers.add(new MatrixVariableMapMethodArgumentResolver()); resolvers.add(new ServletModelAttributeMethodProcessor(false)); resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); resolvers.add(new RequestHeaderMapMethodArgumentResolver()); resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new SessionAttributeMethodArgumentResolver()); resolvers.add(new RequestAttributeMethodArgumentResolver()); // Type-based argument resolution resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RedirectAttributesMethodArgumentResolver()); resolvers.add(new ModelMethodProcessor()); resolvers.add(new MapMethodProcessor()); resolvers.add(new ErrorsMethodArgumentResolver()); resolvers.add(new SessionStatusMethodArgumentResolver()); resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); // Custom arguments if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } // Catch-all resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; }
解析器具体负责的方面:
RequestParamMethodArgumentResolver 处理@RequestParam(required=false) RequestParamMapMethodArgumentResolver 处理@RequestParam Map map PathVariableMethodArgumentResolver 处理@PathVariable PathVariableMapMethodArgumentResolver 处理@PathVariable MatrixVariableMethodArgumentResolver 处理@PathVariable Map map MatrixVariableMapMethodArgumentResolver 处理@MatrixVariable 多个变量可以使用“;” ServletModelAttributeMethodProcessor 处理@ModelAttribute(required=false) 或者 非基本类型 RequestResponseBodyMethodProcessor 处理@RequestBody RequestPartMethodArgumentResolver 处理@RequestPart RequestHeaderMethodArgumentResolver 处理@RequestHeaderMethodArgumentResolver RequestHeaderMapMethodArgumentResolver 处理@RequestHeader Map map ServletCookieValueMethodArgumentResolver 处理@CookieValue ExpressionValueMethodArgumentResolver 处理@Value SessionAttributeMethodArgumentResolver 处理@SessionAttribute RequestAttributeMethodArgumentResolver 处理@RequestAttribute ServletRequestMethodArgumentResolver 处理ServletRequest、MultipartRequest、HttpSession、Principal、Locale、TimeZone、java.time.ZoneId、InputStream、Reader、org.springframework.http.HttpMethod ServletResponseMethodArgumentResolver 处理ServletResponse、OutputStream、Writer HttpEntityMethodProcessor 处理@HttpEntity、@RequestEntity RedirectAttributesMethodArgumentResolver 处理RedirectAttributes ModelMethodProcessor 处理Model model MapMethodProcessor 处理Map map ErrorsMethodArgumentResolver 处理Errors 数据绑定时使用 SessionStatusMethodArgumentResolver 处理SessionStatus UriComponentsBuilderMethodArgumentResolver 处理UriComponentsBuilder和ServletUriComponentsBuilder
3.实例
我们一般在使用springmvc的时候玩不了像struts2这样的在参数中接收a1.name=xx,a2.name=xx这样的,但是这并不表示springmvc做不到,我们来自定义一个参数解析器来实现所说的功能。springmvc默认提供了ModelAttributeMethodProcessor来解析实体,而我们是要实现功能和ModelAttributeMethodProcessor相似的,但又是可以实现a1.name=xx,a2.name=xx来初始化javabean的一个参数解析器
3.1 注解@Multi
用在解析器supportParameter()方法中来判定方法需要经过自定义参数解析器来解析@Target (ElementType.PARAMETER) @Retention (RetentionPolicy.RUNTIME) @Documented public @interface Multi { String value() default ""; }
3.2 MultiHandlerMethodArgumentResolver
public class MultiHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(Multi.class);//参数前面带@Multi } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { { Multi multi = parameter.getParameterAnnotation(Multi.class); String name = StringUtils.isEmpty(multi.value()) ? parameter.getParameterName() : multi.value();//参数名:默认去@Multi的value值 如果是""则去获取参数值的命名变量 //String name = ModelFactory.getNameForParameter(parameter); Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, webRequest)); if (! mavContainer.isBindingDisabled(name)) { ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class); if (ann != null && ! ann.binding()) { mavContainer.setBindingDisabled(name); } } WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name); if (binder.getTarget() != null) { if (! mavContainer.isBindingDisabled(name)) { bindRequestParameters(binder, webRequest, name); } validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new BindException(binder.getBindingResult()); } } // Add resolved attribute and BindingResult at the end of the model Map<String, Object> bindingResultModel = binder.getBindingResult().getModel(); mavContainer.removeAttributes(bindingResultModel); mavContainer.addAllAttributes(bindingResultModel); return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter); } } protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request, String parameterName) { ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class); ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder; //servletBinder.setFieldDefaultPrefix(servletBinder.getObjectName()+"."); servletBinder.setFieldDefaultPrefix(parameterName + "."); servletBinder.bind(servletRequest); } protected Object createAttribute(String attributeName, MethodParameter methodParam, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception { return BeanUtils.instantiateClass(methodParam.getParameterType()); } protected void validateIfApplicable(WebDataBinder binder, MethodParameter methodParam) { Annotation[] annotations = methodParam.getParameterAnnotations(); for (Annotation ann : annotations) { Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) { Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann)); Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[]{hints}); binder.validate(validationHints); break; } } } protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) { int i = methodParam.getParameterIndex(); Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes(); boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1])); return ! hasBindingResult; } }
3.3 配置
<mvc:annotation-driven > <mvc:argument-resolvers> <bean class="com.kings.template.mvc.MultiHandlerMethodArgumentResolver"/> </mvc:argument-resolvers> </mvc:annotation-driven>
3.4 控制器
3.4.1 实例1
@RequestMapping (value = "/argumentresolver/1", method = RequestMethod.GET) @ResponseBody public List<Person> list(@Multi(value = "p1") Person p1,@Multi(value = "p2") Person p2) { return Lists.newArrayList(p1,p2); }
访问:http://localhost:8080/kingstemplate//argumentresolver/1?p1.name=ws&p2.name=kings
结果:
[{"name":"ws","telephone":null,"sex":null,"race":null,"u":null},{"name":"kings","telephone":null,"sex":null,"race":null,"u":null}]
3.4.2 实例2
@RequestMapping (value = "/argumentresolver/1", method = RequestMethod.GET) @ResponseBody public List<Person> list(@Multi Person p1,@Multi Person p3) { return Lists.newArrayList(p1,p3); }
访问:http://localhost:8080/kingstemplate//argumentresolver/1?p1.name=ws&p3.name=kings
结果:
[{"name":"ws","telephone":null,"sex":null,"race":null,"u":null},{"name":"kings","telephone":null,"sex":null,"race":null,"u":null}]
4.友情附录表
参数注解使用注解 | 描述 |
@RequestParam | 接收基本类型,处理request body部分的注解,不能处理bean类型 |
@PathVariable | 接受url中的参数,即 someUrl/{paramId}, 这时的paramId可通过 @Pathvariable注解绑定它传过来的值到方法的参数上。 |
@MatrixVariable | 多个变量可以使用“;”来接收 |
@ModelAttribute | 用于方法上时: 通常用来在处理@RequestMapping之前,为请求绑定需要从后台查询的model; 用于参数上时: 用来通过名称对应,把相应名称的值绑定到注解的参数bean上; |
@RequestBody | 处理request body部分的注解,处理Content-Type: 不是application/x-www-form-urlencoded编码的内容,例如application/json, application/xml等 |
@RequestHeader | 获取Request请求header部分的参数 |
@RequestPart | 处理Content-Type:multipart/form-data的参数,如MultipartFile |
@CookieValue | 可以把Request header中关于cookie的值绑定到方法的参数上 |
@SessionAttribute | 绑定HttpSession中的attribute对象的值 |
@RequestAttribute | 接受request中的attribute |
5 总结
参数解析器能帮助我们更加方便的将请求参数绑定到handlermethod上,在控制器方法上帮助我们节约了更多的重复代码。发现一个机制的导航😳
相关文章推荐
- springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一)
- 1.盘点springmvc的常用接口之HandlerMethodArgumentResolver
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- SpringMVC中的参数组装:HandlerMethodArgumentResolver
- springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一)
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- SpringMVC源码总结(十)自定义HandlerMethodArgumentResolver
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- SpringMVC之HandlerMethodArgumentResolver和<mvc:argument-resolvers>
- SpringMVC中的参数组装:HandlerMethodArgumentResolver
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- springMVC的自定义参数解析器HandlerMethodArgumentResolver的使用
- springMVC源码解析--HandlerMethodArgumentResolverComposite参数解析器集合(二)
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- SpringMVC中的参数组装:HandlerMethodArgumentResolver
- SpringMVC中的参数组装:HandlerMethodArgumentResolver
- SpringMVC杂记(十七) HandlerMethodArgumentResolver接口应用example
- SpringMVC重要接口(一)HandlerMethodArgumentResolver
- 【原创】遨游springmvc之HandlerExceptionResolver