当前位置 博文首页 > 文章内容

    Spring MVC请求参数的深入解析

    作者:shunshunshun18 栏目:未分类 时间:2021-05-06 14:43:01

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



    请求参数解析

    客户端请求在handlerMapping中找到对应handler后,将会继续执行DispatchServlet的doPatch()方法。

    首先是找到handler对应的适配器。

    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

    进入到getHandlerAdapter(mappedHandler.getHandler())方法中

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    		if (this.handlerAdapters != null) {
    			for (HandlerAdapter adapter : this.handlerAdapters) {
    				if (adapter.supports(handler)) {
    					return adapter;
    				}
    			}
    		}
    		throw new ServletException("No adapter for handler [" + handler +
    				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    	}

    这里存在多个适配器,如图:

    其中使用@RequestMaping注解修饰的控制器都将适配第一个适配器;而函数式方法将会使用第二个适配器。

    跟踪请求,这里将会获得第一个适配器,判断也简单,如下:

    public final boolean supports(Object handler) {
    		return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    	}

    如果是HandlerMethod类型的处理器就采用这个适配器,而客户端请求正好对应的是HandlerMethod处理器。

    找到适配器后,将会真正执行处理器逻辑。如下:

    // Actually invoke the handler.
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

    进入RequestMappingHandlerAdapter,执行适配器核心方法:

    @Override
    	protected ModelAndView handleInternal(HttpServletRequest request,
    			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
    		ModelAndView mav;
    		checkRequest(request);
    
    		// Execute invokeHandlerMethod in synchronized block if required.
    		if (this.synchronizeOnSession) {
    			HttpSession session = request.getSession(false);
    			if (session != null) {
    				Object mutex = WebUtils.getSessionMutex(session);
    				synchronized (mutex) {
    					mav = invokeHandlerMethod(request, response, handlerMethod);
    				}
    			}
    			else {
    				// No HttpSession available -> no mutex necessary
    				mav = invokeHandlerMethod(request, response, handlerMethod);
    			}
    		}
    		else {
    			// No synchronization on session demanded at all...
    			mav = invokeHandlerMethod(request, response, handlerMethod);
    		}
    
    		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
    			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
    				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
    			}
    			else {
    				prepareResponse(response);
    			}
    		}
    
    		return mav;
    	}

    其核心代码为实际执行处理器方法:

    mav = invokeHandlerMethod(request, response, handlerMethod);

    同样,我们打开RequestMappingHandlerAdapter中的invokeHandlerMethod方法:

    @Nullable
    	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
    			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
    		ServletWebRequest webRequest = new ServletWebRequest(request, response);
    		try {
                //通过处理器获得真正的执行方法及其参数列表
    			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
                //给执行方法对象添加参数解析器
    			if (this.argumentResolvers != null) {
    				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    			}
                //给执行方法对象添加返回值处理器
    			if (this.returnValueHandlers != null) {
    				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    			}
                //处理器对象装配完成,执行控制器方法
    		invocableMethod.invokeAndHandle(webRequest, mavContainer);
    		if (asyncManager.isConcurrentHandlingStarted()) {
    			return null;
    		}
    
    		return getModelAndView(mavContainer, modelFactory, webRequest);
    	}
    	finally {
    		webRequest.requestCompleted();
    	}
    }

    在这个关键方法中,首先执行请求对应的控制器逻辑,之后进行系列处理,根据返回值处理器处理返回值。

    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
    			Object... providedArgs) throws Exception {
            //执行控制器方法
    		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    		setResponseStatus(webRequest);
    
    		if (returnValue == null) {
    			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
    				disableContentCachingIfNecessary(webRequest);
    				mavContainer.setRequestHandled(true);
    				return;
    			}
    		}
    		else if (StringUtils.hasText(getResponseStatusReason())) {
    			mavContainer.setRequestHandled(true);
    			return;
    		}
    
    		mavContainer.setRequestHandled(false);
    		Assert.state(this.returnValueHandlers != null, "No return value handlers");
    		try {
                //处理返回值
    			this.returnValueHandlers.handleReturnValue(
    					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    		}
    		catch (Exception ex) {
    			if (logger.isTraceEnabled()) {
    				logger.trace(formatErrorForReturnValue(returnValue), ex);
    			}
    			throw ex;
    		}
    	}

    以下给出部分参数解析器及返回值处理器截图:

    参数解析器。对应每一个参数(路径变量、矩阵变量、获得请求头、请求域等)的获取方式

    返回值处理器。ModelAndView、ResponseBody等。每个处理器处理不同类别的返回值类型。

    接下来,真正进入到最终执行method方法invocableMethod.invokeAndHandle(webRequest, mavContainer);,这里是真是执行控制器中映射的方法。

    以下为获得参数列表对应值的逻辑,参数获取完成后将会执行真正的控制器逻辑。

    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
    			Object... providedArgs) throws Exception {
        //通过参数解析器获取参数列表每一个参数的值
    		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    		if (logger.isTraceEnabled()) {
    			logger.trace("Arguments: " + Arrays.toString(args));
    		}
    		return doInvoke(args);
    	}

    进入解析逻辑,解析是对比每一个参数绑定的注解,如果注解一致将会使用对应的解析器将请求传递的参数值获取到。

    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
    			Object... providedArgs) throws Exception {
            //获得控制器参数列表,每一个参数包含其参数类型,参数次序、注解修饰(用于使用对应解析器)
    		MethodParameter[] parameters = getMethodParameters();
    		if (ObjectUtils.isEmpty(parameters)) {
    			return EMPTY_ARGS;
    		}
    
    		Object[] args = new Object[parameters.length];
    		for (int i = 0; i < parameters.length; i++) {
                //轮循获得参数
    			MethodParameter parameter = parameters[i];
    			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
    			args[i] = findProvidedArgument(parameter, providedArgs);
    			if (args[i] != null) {
    				continue;
    			}
                //判断解析器是否支持当前参数的解析
    			if (!this.resolvers.supportsParameter(parameter)) {
    				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
    			}
    			try {
                    //获得参数值的核心方法
    				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
    			}
    			catch (Exception ex) {
    				// Leave stack trace for later, exception may actually be resolved and handled...
    				if (logger.isDebugEnabled()) {
    					String exMsg = ex.getMessage();
    					if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
    						logger.debug(formatArgumentError(parameter, exMsg));
    					}
    				}
    				throw ex;
    			}
    		}
    		return args;
    	}

    查看解析器是否指出当前参数部分代码,可以了解到SpringMVC的缓存策略。

    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        //从缓存中获取当前参数的解析器
        HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    	//缓存中不存在则将这个参数对应的解析器加到缓存中,提升后续相同请求响应速度。	
        if (result == null) {
    			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
    				if (resolver.supportsParameter(parameter)) {
    					result = resolver;
    					this.argumentResolverCache.put(parameter, result);
    					break;
    				}
    			}
    		}
    		return result;
    	}

    参数列表

    以下为获得的参数列表第一个参数部分属性

    其对应的是控制器中id参数:

    @GetMapping(value = "/student01/{id}/car/{name}")
        public Map<String, Object> testAnnotation(@PathVariable(name = "id") String id){
            Map<String, Object> map = new HashMap<>();
            map.put("id", id);
        return map;
        }

    以上即为获得请求Handler对应的适配器,处理参数映射、执行控制器逻辑、返回值处理的核心源码处理。

    总结