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

相關文章
相關標籤/搜索