zoukankan      html  css  js  c++  java
  • SpringMVC:请求参数解析原理

    测试

    @Controller
    public class HelloController {
        @RequestMapping("/testVar/{id}")
        @ResponseBody
        public String testVar(@PathVariable Integer id){
            return "ok"+ id;
        }
    }
    

    image-20210406202135783

    原理剖析

    看SpringMVC源码还得从DispatcherServlet的doDispatch方法开始看

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		HttpServletRequest processedRequest = request;
    		HandlerExecutionChain mappedHandler = null;
    		boolean multipartRequestParsed = false;
    
    		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
    		try {
    			ModelAndView mv = null;
    			Exception dispatchException = null;
    
    			try {
    				processedRequest = checkMultipart(request);
    				multipartRequestParsed = (processedRequest != request);
    
                    //获取当前请求的处理器
    				// Determine handler for the current request.
    				mappedHandler = getHandler(processedRequest);
    				if (mappedHandler == null) {
    					noHandlerFound(processedRequest, response);
    					return;
    				}
    
                    //找到处理器适配器
    				// Determine handler adapter for the current request.
    				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    				// Process last-modified header, if supported by the handler.
    				String method = request.getMethod();
    				boolean isGet = "GET".equals(method);
    				if (isGet || "HEAD".equals(method)) {
    					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
    					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    						return;
    					}
    				}
    
    				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    					return;
    				}
    
                    //执行目标方法,并返回ModelAndView
    				// Actually invoke the handler.
    				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    				if (asyncManager.isConcurrentHandlingStarted()) {
    					return;
    				}
    
    				applyDefaultViewName(processedRequest, mv);
    				mappedHandler.applyPostHandle(processedRequest, response, mv);
    			}
    			catch (Exception ex) {
    				dispatchException = ex;
    			}
    			catch (Throwable err) {
    				// As of 4.3, we're processing Errors thrown from handler methods as well,
    				// making them available for @ExceptionHandler methods and other scenarios.
    				dispatchException = new NestedServletException("Handler dispatch failed", err);
    			}
    			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    		}
    		catch (Exception ex) {
    			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    		}
    		catch (Throwable err) {
    			triggerAfterCompletion(processedRequest, response, mappedHandler,
    					new NestedServletException("Handler processing failed", err));
    		}
    		finally {
    			if (asyncManager.isConcurrentHandlingStarted()) {
    				// Instead of postHandle and afterCompletion
    				if (mappedHandler != null) {
    					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
    				}
    			}
    			else {
    				// Clean up any resources used by a multipart request.
    				if (multipartRequestParsed) {
    					cleanupMultipart(processedRequest);
    				}
    			}
    		}
    	}
    

    所以我们只需要分析HandlerAdapter的handle方法,该方法调用了handleInternal方法

    image-20210406203201568

    image-20210406203341846

    进入invokeHandlerMethod方法:

    image-20210406203728399

    而这些参数解析器都实现了HandlerMethodArgumentResolver接口

    public interface HandlerMethodArgumentResolver {
    
    	//是否支持解析该参数
    	boolean supportsParameter(MethodParameter parameter);
    
    	//调用解析方法,进行解析
    	@Nullable
    	Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
    			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
    
    }
    

    继续断点,下面又设置了返回值处理器ReturnValueHandlers(不在本文讨论之内),总共15种

    image-20210406204209955

    断点往下走,到invocableMethod.invokeAndHandle(执行并处理)

    image-20210406204314647

    image-20210406204453407

    image-20210406204700961

    调用doInvoke,在doInvoke方法的args中已经把请求参数获取到了。所以可以推断,请求参数的解析源码应该在getMethodArgumentValues方法中。

    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);
                //这里的providedArgs为空,而findProvidedArgument方法里面直接判断providedArgs是否为空,
                //如果为空,直接返回null,所以这里不是请求参数解析的地方
    			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,所以这里就是解析请求参数的地方
    				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;
    	}
    

    源代码如下:getArgumentResolver会依次遍历所有的参数解析器去匹配

    image-20210406205916958

    因为我们标注了@PathVariable注解,那根据名字,我猜测可能用的是PathVariableMethodArgumentResolver。源码说话:

    image-20210406210745553

    现在找到了resolver,那我们看它resolveArgument是怎么执行的

    image-20210406211448909

    resolveName方法:

    image-20210406212014528

    至此,Spring MVC的请求参数解析结束。

    扩展

    通过上述源码分析,我们知道spring mvc请求参数解析的原理。

    而如何解析,都在HandlerMethodArgumentResolver接口的实现类中。所以我们能在参数位置写那些类型的参数,就需要根据HandlerMethodArgumentResolver了

  • 相关阅读:
    vue分页组件(二)
    ES6里let、const、var区别总结
    electron-vue项目打包踩坑记录
    node环境变量配置
    第一次把本地项目与git相连
    java项目环境搭建
    npm上发布vue插件
    HTML5日期时间输入类型注意事项(time,date)
    vue 分页组件
    vue里ref ($refs)用法
  • 原文地址:https://www.cnblogs.com/wwjj4811/p/14623854.html
Copyright © 2011-2022 走看看