SpringBoot核心技术-Web开发-请求参数解析原理

  • HandlerMapping中找到能处理请求的Handler(Controller.method())
  • 为当前Handler 找一个适配器 HandlerAdapter; RequestMappingHandlerAdapter
  • 适配器执行目标方法并确定方法参数的每一个值 

1、HandlerAdapter

第一个:支持方法上标注的@RequestMapping

第二个:支持函数式编程

.....

2、执行目标方法

2.1 DispatcherServlet -- doDispatch

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

2.2 RequestMappingHandlerAdapter--handleInternal

ha.handle又会调用handleInternal方法(这个过程写在适配器继承的抽象类的handle方法中)

//AbstractHandlerMethodAdapter   
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return this.handleInternal(request, response, (HandlerMethod)handler);
}
=====RequestMappingHandlerAdapter类中的handleInternal方法==========
mav = invokeHandlerMethod(request, response, handlerMethod); //执行目标方法

=====invokeHandlerMethod的关键代码===========================
ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);

if (this.argumentResolvers != null) {//设置参数解析器
     invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {//设置返回值处理器
                        
     invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);

//创建了ModelAndViewContainer
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);

设置参数解析器:

参数解析器:确定将要执行的目标方法的每个参数的值是什么

SpringMVC目标方法能写多少种参数类型,取决于参数解析器。

image.png

image.png

 supportParameter:当前解析器是否支持这种参数

支持就调用resolveArgument

设置返回值处理器:

image.png

 2.3 invokeAndHandle

此方法内部完成了请求参数解析、方法执行、返回值处理和内容协商。本文只写到了请求参数解析,返回值处理以及内容协商在数据响应这篇文章中。

invocableMethod为ServletInvocableHandlerMethod对象

invokeForRequest:真正执行目标方法


=====ServletInvocableHandlerMethod中invokeAndHandle方法中的核心代码==============
Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
this.setResponseStatus(webRequest);

2.4 invokeForRequest

this.doInvoke(args),解析完成参数之后,反射执行目标方法。

=====invokeForRequest方法=====
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        //获取方法参数所有的值
        Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
        //反射执行Controller中的方法
        return this.doInvoke(args);
    }

2.5  getMethodArgumentValues:确定每个参数的值

上一步获取参数值的具体代码

============InvocableHandlerMethod类==========================
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		MethodParameter[] parameters = getMethodParameters();
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}
        
        //核心代码,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;
			}
            //1、遍历26个参数解析器是否支持parameter类型的参数
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
                //2、开始解析参数值
				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;
	}

 上图代码的细节:

2.5.1 遍历判断所有参数解析器是否支持当前参数

找到能解析的解析器,加入缓存,方便下一步解析该参数时直接获取。

======if (!this.resolvers.supportsParameter(parameter))底层实现=====	
@Nullable
	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;
	}

 2.5.2  解析参数的值

解析请求的值给到参数。

======args[i] = this.resolvers.resolveArgument======
 public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);
        if (resolver == null) {
            throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
        } else {
            return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        }
    }

=====最终调用AbstractNamedValueMethodArgumentResolver的resolveArgument方法=====

复杂参数解析

自定义参数解析

3、目标方法执行完成

将所有的数据(复杂参数Map、Model、request等类型)都放在 ModelAndViewContainer;包含要去的页面地址View。还包含Model数据。复杂参数解析原理

image.png

4、处理派发结果

doDispatch中:processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

processDispatchResult中的render渲染方法中,调用了renderMergedOutputModel。在下一个界面渲染成功之前,就将所有的复杂参数的值传进了request请求域中。

renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);

InternalResourceView:
@Override
	protected void renderMergedOutputModel(
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

		// Expose the model object as request attributes.
		exposeModelAsRequestAttributes(model, request);

		// Expose helpers as request attributes, if any.
		exposeHelpers(request);

		// Determine the path for the request dispatcher.
		String dispatcherPath = prepareForRendering(request, response);

		// Obtain a RequestDispatcher for the target resource (typically a JSP).
		RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
		if (rd == null) {
			throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
					"]: Check that the corresponding file exists within your web application archive!");
		}

		// If already included or response already committed, perform include, else forward.
		if (useInclude(request, response)) {
			response.setContentType(getContentType());
			if (logger.isDebugEnabled()) {
				logger.debug("Including [" + getUrl() + "]");
			}
			rd.include(request, response);
		}

		else {
			// Note: The forwarded resource is supposed to determine the content type itself.
			if (logger.isDebugEnabled()) {
				logger.debug("Forwarding to [" + getUrl() + "]");
			}
			rd.forward(request, response);
		}
	}

 renderMergedOutputModel中调用exposeModelAsRequestAttributes

暴露模型作为请求域属性
// Expose the model object as request attributes.
		exposeModelAsRequestAttributes(model, request);
protected void exposeModelAsRequestAttributes(Map<String, Object> model,
			HttpServletRequest request) throws Exception {

    //model中的所有数据遍历挨个放在请求域中
		model.forEach((name, value) -> {
			if (value != null) {
				request.setAttribute(name, value);
			}
			else {
				request.removeAttribute(name);
			}
		});
	}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值