Spring MVC 源码分析之 请求参数解析

一、前言

在前面几篇文章分析了请求转发、Controller查找及拦截器的加载等信息,那么当带有参数的请求发送到服务端,SpringMVC又是怎样把请求参数,分析转换后传入到对应的方法中的呢?本篇文章主要分析请求参数的解析、类型转换及数据的绑定。

二、请求执行者适配器

再次分析DispatcherServlet 中的 doDispatch方法发现,在获取到 Handler后会再次跟进Handler的找到执行此handler的适配器。如所示:

1、查看HandlerAdapter接口的方法列表

public interface HandlerAdapter {
	
	boolean supports(Object handler);

	
	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;


	long getLastModified(HttpServletRequest request, Object handler);

}

说明:

此处主要关心 supports方法,此方法是返回此适配器是否支持执行对应handler。上文分析的所有的 Controller 解析成的Handler是通过适配器  RequestMappingHandlerAdapter 处理的,接下来将分析 次实现了。

2、适配器 RequestMappingHandlerAdapter 继承关系

3、初始化

RequestMappingHandlerAdapter实现了 InitializingBean 接口,属性Spring 接口的都知道,此接口只有一个方法 afterPropertiesSet(),是在类初始化完成后调用的方法。那么接下来咱们看看此类中afterPropertiesSet() 方法的具体实现。

public void afterPropertiesSet() {
		// 初始化 加了注解 @ControllerAdvice 的类的属性信息
		initControllerAdviceCache();

  //主要实现三个变量  argumentResolvers initBinderArgumentResolvers  returnValueHandlers
		if (this.argumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.initBinderArgumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.returnValueHandlers == null) {
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}

接着再看看 initControllerAdviceCace 方法的具体实现

private void initControllerAdviceCache() {
		if (getApplicationContext() == null) {
			return;
		}
       
        // 在Spring容器中获取所有加了注解 @ControllerAdvice 的类
		List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

		List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();

		for (ControllerAdviceBean adviceBean : adviceBeans) {
			Class<?> beanType = adviceBean.getBeanType();
			if (beanType == null) {
				throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
			}
//在当前类中获取所有添加了注解 @ModelAttribute 且没有添加 注解@RequestMapping的方法,并且添加到缓存中。
			Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
			if (!attrMethods.isEmpty()) {
				this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
			}
在当前类中获取所有添加了注解 @InitBinder的方法,并且添加到缓存中。
			Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
			if (!binderMethods.isEmpty()) {
				this.initBinderAdviceCache.put(adviceBean, binderMethods);
			}
//判断是否实现了 接口 RequestBodyAdvice
			if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
				requestResponseBodyAdviceBeans.add(adviceBean);
			}
		}

		if (!requestResponseBodyAdviceBeans.isEmpty()) {
			this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
		}

		
	}

4、handle 处理方法

RequestMappingHandlerAdapter类的handle方法是在其父类中实现的,但在父类中只是调用了模板方法  handleInternal ,handleInternal方法又有具体的子类进行实现,那么接下来在分析一下 handleInternal 方法。

@Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
//检测方法给定的Request是否支持 且是否要求 session。
		checkRequest(request);

		// Execute invokeHandlerMethod in synchronized block if required.
		if (this.synchronizeOnSession) {
		   // 省略 同步Session调用
		}
		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;
	}

此种的重点方法为 invokeHandlerMethod 此方法较为复杂,接下来主要分析一下此方法。

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			if (this.argumentResolvers != null) {
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}
			if (this.returnValueHandlers != null) {
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}
			invocableMethod.setDataBinderFactory(binderFactory);
			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

			AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
			asyncWebRequest.setTimeout(this.asyncRequestTimeout);

			WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
			asyncManager.setTaskExecutor(this.taskExecutor);
			asyncManager.setAsyncWebRequest(asyncWebRequest);
			asyncManager.registerCallableInterceptors(this.callableInterceptors);
			asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

			if (asyncManager.hasConcurrentResult()) {
				Object result = asyncManager.getConcurrentResult();
				mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
				asyncManager.clearConcurrentResult();
				LogFormatUtils.traceDebug(logger, traceOn -> {
					String formatted = LogFormatUtils.formatValue(result, !traceOn);
					return "Resume with async result [" + formatted + "]";
				});
				invocableMethod = invocableMethod.wrapConcurrentResult(result);
			}

			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}

此方法的功能分析:

1)、首先使用request和response创建了ServletWebRequest类型的webRequest,在ArgumentResolver解析参数时使用的request就是这个webRequest,当然如果我们的处理器需要HttpServletRequest类型的参数,ArgumentResolver会给我们设置原始的request。

2)、接着对WebDataBinderFactory、ModelFactory、ServletInvocableHandlerMethod这三个类型的变量进行了定义和初始化,下面先分别介绍一下这三个变量。

对三个变量的解析:

  • WebDataBinderFactory 的作用从名字就可以看出是用来创建WebDataBinder的,WebDataBinder用于参数绑定,主要功能就是实现参数跟String之间的类型转换,ArgumentResolver在进行参数解析的过程中会用到WebDataBinder,另外ModelFactory在更新Model时也会用到它。
  • ModelFactory是用来处理Model的,主要包含两个功能:①在处理器具体处理之前对Model进行初始化;②在处理完请求后对Model参数进行更新。
  • ServletInvocableHandlerMethod 类型非常重要,它继承自HandlerMethod,并且可以直接执行。实际请求的处理就是通过它来执行的,参数绑定、处理请求以及返回值处理都在它里边完成。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值