springmvc方法参数处理

#基础 ###类名称 ####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

相关文章
相关标签/搜索