#基础 ###类名称 ####HandlerMethodArgumentResolver 解决方法参数到指定信息参数集合的策略接口。 ###方法名称 supportsParameter 检查指定参数是否被该接口支持 参数 MethodParameter parameter 要被检查的方法参数。 resolveArgument 将请求参数中的参数方法转换成变量 参数 一、parameter 要被转换的参数 二、mavContainer 目前请求的ModelAndViewContainer 三、webRequest 如今的请求 四、binderFactory 建立实例的工厂 五、返回被转换好的值java
###问题: springmvc在请求到方法以前如何包装到request对象到方法头对象。 有三种状况: @RequestParam 系统自带标签 @CurrentUser User user 自定义标签 JqGridPage jqGridPage ###springmvc参数处理流程web
类:DispatcherServlet protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; //处理器前置处理 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } //获取处理器适配 <!-- --> HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); try { //真正的触发适配器处理请求() mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } finally { if (asyncManager.isConcurrentHandlingStarted()) { return; } } //设置view applyDefaultViewName(request, mv); //处理器后置处理 mappedHandler.applyPostHandle(processedRequest, response, mv); }
springmvc设置了五种处理器的适配器。他们分别是AbstractHandlerMethodAdapter HttpRequestHandlerAdapter RequestMappingHandlerAdapter SimpleControllerHandlerAdapter(/) SimpleServletHandlerAdapter。在从上到下配置的过程当中遇到了合适的匹配,那么就将采用该适配器处理请求。咱们拿SimpleControllerHandlerAdapter为例继续分析。spring
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { //委托WebContentGenerator作检查和准备工做 checkAndPrepare(request, response, this instanceof LastModified); // 在session的互斥变量同步模块执行handleRequestInternal if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { return handleRequestInternal(request, response); } } } return handleRequestInternal(request, response); } 类ParameterizableViewController protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { //返回对象ModelAndView return new ModelAndView(getViewName(), RequestContextUtils.getInputFlashMap(request)); }
SimpleControllerHandlerAdapter适配器很简单就是将咱们请求的路径包装成新的返回对象ModelAndView对象返回。这个方法适用于刚刚登录系统想要请求登录页面的时候。 接下来咱们再以RequestMappingHandlerAdapter为例作请求。json
类RequestMappingHandlerAdapter protected final ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { return invokeHandleMethod(request, response, handlerMethod); } private ModelAndView invokeHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { //得到处理器 ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory); //执行处理器 requestMappingMethod.invokeAndHandle(webRequest, mavContainer); } //得到返回的ModelandView return getModelAndView(mavContainer, modelFactory, webRequest); } 类:ServletInvocableHandlerMethod public final void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); } 类InvocableHandlerMethod public final Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); Object returnValue = invoke(args); return returnValue; } private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //得到方法的全部参数 MethodParameter[] parameters = getMethodParameters(); Object[] args = new Object[parameters.length]; //循环每个参数得到参数的参数解析器 for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); GenericTypeResolver.resolveParameterType(parameter, getBean().getClass()); args[i] = resolveProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } if (this.argumentResolvers.supportsParameter(parameter)) { try { //执行解析器(这里使用了组合模式将全部的解析器都放置到了HandlerMethodArgumentResolverComposite对象中统一处理,good method~) args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); continue; } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(getArgumentResolutionErrorMessage("Error resolving argument", i), ex); } throw ex; } } if (args[i] == null) { String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i); throw new IllegalStateException(msg); } } return args; } 类:HandlerMethodArgumentResolverComposite public Object resolveArgument( MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { //得到支持方法参数的处理器去处理该参数的事宜。 HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]"); return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); }
在一个request请求首先要得到可以处理该请求的适配器。而后在适配器中对该请求作处理。处理很复杂,我简略了一些代码。这里主要说方法头的参数处理: 步骤一、在适配器中调用方法handleInternal()作一些参数校验和准备工做。 步骤二、调用invokeHandleMethod(request, response, handlerMethod)得到处理参数的类ServletInvocableHandlerMethod。 步骤三、调用ServletInvocableHandlerMethod类中的方法invokeAndHandle()处理参数。 步骤四、invokeAndHandle方法调invokeForRequest方法处理每个参数。 步骤五、invokeForRequest方法调用父类invokeForRequest()方法去找到每个参数解析器并解析参数。 步骤5:调用invoke(args)方法将步骤五中的参数包装成一个对象返回。 ###参数解析器 回到咱们上面说的三种参数处理方式 @RequestParam 系统自带标签和JqGridPage jqGridPage对象都是是由ModelAttributeMethodProcessor处理器处理。session
public boolean supportsReturnType(MethodParameter returnType) { //支持标签@ModelAttribute if (returnType.getMethodAnnotation(ModelAttribute.class) != null) { return true; } //对未注释标签的若是是非普通对象也返回true else if (this.annotationNotRequired) { return !BeanUtils.isSimpleProperty(returnType.getParameterType()); } else { return false; } } //参数解析过程 @Override public final Object resolveArgument( MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception { //得到参数名称 String name = ModelFactory.getNameForParameter(parameter); //若是mavContainer没有这个参数则调用mavContainer.getModel().get(name)返回参数,若是返回参数失败会调用createAttribute(name, parameter, binderFactory, request)建立一个实例对象 Object attribute = (mavContainer.containsAttribute(name)) ? mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request); //建立WebDataBinder类型的对象 WebDataBinder binder = binderFactory.createBinder(request, attribute, name); if (binder.getTarget() != null) { //绑定request参数到对象中去 bindRequestParameters(binder, request); //作校验 validateIfApplicable(binder, parameter); //与错误抛异常 if (binder.getBindingResult().hasErrors()) { if (isBindExceptionRequired(binder, parameter)) { throw new BindException(binder.getBindingResult()); } } } //没有错误将得到的数据放置到mvc容器中mavContainer,返回实例对象。 Map<String, Object> bindingResultModel = binder.getBindingResult().getModel(); mavContainer.removeAttributes(bindingResultModel); mavContainer.addAllAttributes(bindingResultModel); return binder.getTarget(); }
@CurrentUser User自定义标签咱们能够扩展HandlerMethodArgumentResolver方法自定义参数解析器。mvc
public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver { public CurrentUserMethodArgumentResolver() { } @Override public boolean supportsParameter(MethodParameter parameter) { //参数带有标签@CurrentUser则返回true if (parameter.hasParameterAnnotation(CurrentUser.class)) { return true; } return false; } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { //获取request中的参数,将做为结果返回 CurrentUser currentUserAnnotation = parameter.getParameterAnnotation(CurrentUser.class); return webRequest.getAttribute(currentUserAnnotation.value(), NativeWebRequest.SCOPE_REQUEST); } }
最后别忘了将自定义的resolver放置到mvc而配置文件中。app
<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <!-- StringHttpMessageConverter编码为UTF-8,防止乱码 --> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="UTF-8"/> <property name = "supportedMediaTypes"> <list> <bean class="org.springframework.http.MediaType"> <constructor-arg index="0" value="text"/> <constructor-arg index="1" value="plain"/> <constructor-arg index="2" value="UTF-8"/> </bean> <bean class="org.springframework.http.MediaType"> <constructor-arg index="0" value="*"/> <constructor-arg index="1" value="*"/> <constructor-arg index="2" value="UTF-8"/> </bean> </list> </property> </bean> <!-- 避免IE执行AJAX时,返回JSON出现下载文件 --> <bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>application/json;charset=UTF-8</value> </list> </property> <!--<property name="serializerFeature">--> <!--这个地方加上这个功能吧,能本身配置一些东西,好比时间的格式化,null输出""等等--> <!--</property>--> </bean> </mvc:message-converters> <mvc:argument-resolvers> <bean class="web.bind.method.annotation.PageableMethodArgumentResolver"/> <bean class="web.bind.method.annotation.SearchableMethodArgumentResolver"/> <bean class="web.bind.method.annotation.FormModelMethodArgumentResolver"/> <bean class="web.bind.method.annotation.CurrentUserMethodArgumentResolver"/> </mvc:argument-resolvers> </mvc:annotation-driven>
至此,咱们从源码级别分别阐述了springmvc过程当中参数的处理时机和参数处理流程。这块内容在咱们处理参数方面内容给了一个启示。能够本身定义处理器处理前台页面复杂的参数请求。
###参考文献: ####http://jinnianshilongnian.iteye.com/blog/1608234 ####http://jinnianshilongnian.iteye.com/blog/1498155async