SpringMVC之RequestMappingHandlerAdapter

下面是HandlerAdapter的接口定义:

public interface HandlerAdapter {

	/**
	 * Given a handler instance, return whether or not this {@code HandlerAdapter}
	 * can support it. Typical HandlerAdapters will base the decision on the handler
	 * type. HandlerAdapters will usually only support one handler type each.
	 * <p>A typical implementation:
	 * <p>{@code
	 * return (handler instanceof MyHandler);
	 * }
	 * @param handler handler object to check
	 * @return whether or not this object can use the given handler
	 */
	boolean supports(Object handler);

	/**
	 * Use the given handler to handle this request.
	 * The workflow that is required may vary widely.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler handler to use. This object must have previously been passed
	 * to the {@code supports} method of this interface, which must have
	 * returned {@code true}.
	 * @throws Exception in case of errors
	 * @return ModelAndView object with the name of the view and the required
	 * model data, or {@code null} if the request has been handled directly
	 */
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

	/**
	 * Same contract as for HttpServlet's {@code getLastModified} method.
	 * Can simply return -1 if there's no support in the handler class.
	 * @param request current HTTP request
	 * @param handler handler to use
	 * @return the lastModified value for the given handler
	 * @see javax.servlet.http.HttpServlet#getLastModified
	 * @see org.springframework.web.servlet.mvc.LastModified#getLastModified
	 */
	long getLastModified(HttpServletRequest request, Object handler);

}

下main我们来看一下整个SpringMVC最复杂的组件RequestMappingHandlerAdapter。它的handleInternal方法就是用Handler处理请求,具体处理过程大致可以分为三步:
1、备好处理器所需要的参数
2、使用处理器处理请求
3、处理返回值,也就是将不同类型的返回值统一处理成ModelAndView类型
这三步中最麻烦的就是第一步参数准备,需要根据处理器的需要设置参数,而参数类型、个数都是不确定的,并且还使用了大量的组件。
参数来源:
1、request中相关的参数,主要包括url中的参数、post过来的参数以及请求头所包含的值
2、cookie中的参数
3、session中的参数
4、设置到FlashMap中的参数
5、SessionAttributes传递的参数
6、通过相应的注释了@ModelAttribute的方法进行设置的参数

public void afterPropertiesSet() {
		// 初始化注释了@ControllerAdvice的类的相关属性
		initControllerAdviceCache();

		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);
		}
	}

我们看初始化的步骤:
1、initControllerAdviceCache初始化注释了@ControllerAdvice的类的那三个属性
2、初始化argumentResolvers
3、初始化initBinderArgumentResolvers
4、初始化returnValueHandlers

private void initControllerAdviceCache() {
		if (getApplicationContext() == null) {
			return;
		}
		if (logger.isInfoEnabled()) {
			logger.info("Looking for @ControllerAdvice: " + getApplicationContext());
		}
		//获取所有注释了@ControllerAdvice的bean
		List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
		//排血
		Collections.sort(beans, new OrderComparator());

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

		for (ControllerAdviceBean bean : beans) {
			//查找注释了@ModelAttribute而且没有注释@RequestMapping的方法
			Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
			if (!attrMethods.isEmpty()) {
				//添加到缓存中
				this.modelAttributeAdviceCache.put(bean, attrMethods);
				logger.info("Detected @ModelAttribute methods in " + bean);
			}
			//查找注释了@InitBinder的方法
			Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
			if (!binderMethods.isEmpty()) {
				this.initBinderAdviceCache.put(bean, binderMethods);
				logger.info("Detected @InitBinder methods in " + bean);
			}
			//查找实现了ResponseBodyAdvice接口的类
			if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
				responseBodyAdviceBeans.add(bean);
				logger.info("Detected ResponseBodyAdvice bean in " + bean);
			}
		}
		//将所有实现了ResponseBodyAdvice接口的类保存起来
		if (!responseBodyAdviceBeans.isEmpty()) {
			this.responseBodyAdvice.addAll(0, responseBodyAdviceBeans);
		}
	}

initControllerAdviceCache方法执行完,下面我们看一下那三个getXXX方法:

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();

		// 添加按注释解析参数的解析器
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
		resolvers.add(new RequestParamMapMethodArgumentResolver());
		resolvers.add(new PathVariableMethodArgumentResolver());
		resolvers.add(new PathVariableMapMethodArgumentResolver());
		resolvers.add(new MatrixVariableMethodArgumentResolver());
		resolvers.add(new MatrixVariableMapMethodArgumentResolver());
		resolvers.add(new ServletModelAttributeMethodProcessor(false));
		resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
		resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
		resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new RequestHeaderMapMethodArgumentResolver());
		resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));

		// T添加按类型解析参数的解析器
		resolvers.add(new ServletRequestMethodArgumentResolver());
		resolvers.add(new ServletResponseMethodArgumentResolver());
		resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
		resolvers.add(new RedirectAttributesMethodArgumentResolver());
		resolvers.add(new ModelMethodProcessor());
		resolvers.add(new MapMethodProcessor());
		resolvers.add(new ErrorsMethodArgumentResolver());
		resolvers.add(new SessionStatusMethodArgumentResolver());
		resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

		// 添加自定义参数解析器,主要用于解析自定义类型
		if (getCustomArgumentResolvers() != null) {
			resolvers.addAll(getCustomArgumentResolvers());
		}

		//解析所有类型的参数
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
		resolvers.add(new ServletModelAttributeMethodProcessor(true));

		return resolvers;
	}

这里的解析器可以分为四类,通过注释解析的解析器,通过类型解析的解析器,自定义的解析器和解析所有类型的解析器
下面我们看一下他是如何工作的,他的入口方法是HandleInternal:

protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
		//判断handler是否有@SessionAttribute注释的参数
		if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
			// Always prevent caching in case of session attribute management.
			checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
		}
		else {
			// Uses configured default cacheSeconds setting.
			checkAndPrepare(request, response, true);
		}

		// 是否需要同步执行
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					return invokeHandleMethod(request, response, handlerMethod);
				}
			}
		}

		return invokeHandleMethod(request, response, handlerMethod);
	}

在这里面的核心方法有两个checkAndPrepare和invokeHandleMethod,下面我们就对他们来进行分析

protected final void checkAndPrepare(
			HttpServletRequest request, HttpServletResponse response, int cacheSeconds, boolean lastModified)
			throws ServletException {

		// 获取请求的方法
		String method = request.getMethod();
		//检查请求的方法类型是否支持
		if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
			throw new HttpRequestMethodNotSupportedException(
					method, StringUtils.toStringArray(this.supportedMethods));
		}

		// 如果session必须存在,判断session是否存在
		if (this.requireSession) {
			if (request.getSession(false) == null) {
				throw new HttpSessionRequiredException("Pre-existing session required but none found");
			}
		}

		//给response设置缓存过期时间
		applyCacheSeconds(response, cacheSeconds, lastModified);
	}
protected final void applyCacheSeconds(HttpServletResponse response, int seconds, boolean mustRevalidate) {
		if (seconds > 0) {
			cacheForSeconds(response, seconds, mustRevalidate);
		}
		else if (seconds == 0) {
			preventCaching(response);
		}
		// Leave caching to the client otherwise.
	}

checkAndPrepare方法首先检查了请求的类型是否支持,然后根据是否需要session判断session是否真的存在,最后给response设置过期时间

private ModelAndView invokeHandleMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
		//创建ServletWebRequest
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		//初始化几个Factory
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
		ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);

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

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

		final 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();

			if (logger.isDebugEnabled()) {
				logger.debug("Found concurrent result value [" + result + "]");
			}
			requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
		}

		requestMappingMethod.invokeAndHandle(webRequest, mavContainer);

		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}

		return getModelAndView(mavContainer, modelFactory, webRequest);
	}

在invokeHandleMethod方法中首先使用request和response创建了ServletWebRequest类型的webRequest,接着对WebDataBinderFactory、ModelFactory、ServletInvocableHandlerMethod这三个类型的变量进行了定义和初始化。

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
		Class<?> handlerType = handlerMethod.getBeanType();
		//检查当前Handler中的InitBinder方法是否已经存在缓存中
		Set<Method> methods = this.initBinderCache.get(handlerType);
		//如果没有则查找并放到缓存中
		if (methods == null) {
			methods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS);
			this.initBinderCache.put(handlerType, methods);
		}
		List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
		// 将所有符合条件的initBinder添加到集合中
		for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache .entrySet()) {
			if (entry.getKey().isApplicableToBeanType(handlerType)) {
				Object bean = entry.getKey().resolveBean();
				for (Method method : entry.getValue()) {
					initBinderMethods.add(createInitBinderMethod(bean, method));
				}
			}
		}
		//将当前Handler中的InitBinder方法添加到initBinderMethods
		for (Method method : methods) {
			Object bean = handlerMethod.getBean();
			initBinderMethods.add(createInitBinderMethod(bean, method));
		}
		return createDataBinderFactory(initBinderMethods);
	}

ModelFactory是用来处理Model的,主要包含两个功能:
1、在处理器具体处理之前对Model进行初始化
2、在处理完请求后对Model参数进行更新
给Model初始化具体包括三部分内容:
1、将原来的SessionAttributes中的值设置到Model
2、执行相应注释了@ModelAttribute的方法并将其值设置到Mode
3、处理器中注释了@ModelAttribute的参数如果同时在SessionAttributes中也配置了,而且在mavContainer中还没有值从全部SessionAttributes中查找出并设置出去

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
		//获取SessionAttributesHandler 
		SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
		//获取处理器的类型
		Class<?> handlerType = handlerMethod.getBeanType();
		//获取处理器类中注释了@ModelAttribute而且没有注释@RequestMapping的类型,第一次获取后添加到缓存中,以后直接从缓存中获取
		Set<Method> methods = this.modelAttributeCache.get(handlerType);
		if (methods == null) {
			methods = HandlerMethodSelector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
			this.modelAttributeCache.put(handlerType, methods);
		}
		List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
		// 先添加全局@ModelAttribute方法
		for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) {
			if (entry.getKey().isApplicableToBeanType(handlerType)) {
				Object bean = entry.getKey().resolveBean();
				for (Method method : entry.getValue()) {
					attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
				}
			}
		}
		for (Method method : methods) {
			Object bean = handlerMethod.getBean();
			attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
		}
		return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
	}

新建ModelFactory主要使用了三个参数,第一个是注释了@ModelAttribute的方法,第二个是WebDataBinderFactory,第三个是SessionAttributeHandler
SessionAttributesHandler的创建方法getSessionAttributeshandler主要用来添加:
1、注释了@ControllerAdvice的类定义的全局的@ModelAttribute方法
2、处理器自身的@ModelAttribute方法
ServletInvocableHandlerMethod类型继承自HandlerMethod,并且可以直接执行,实际请求的处理就是通过它来执行的,参数绑定、处理请求以及返回值处理都在它里边完成:

private ServletInvocableHandlerMethod createRequestMappingMethod(
			HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {

		ServletInvocableHandlerMethod requestMethod;
		//新建类ServletInvocableHandlerMethod
		requestMethod = new ServletInvocableHandlerMethod(handlerMethod);
		//设置参数
		requestMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		requestMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		requestMethod.setDataBinderFactory(binderFactory);
		requestMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
		return requestMethod;
	}

这三个参数创建完成之后的工作如下:
1、新建传递参数的ModelAndViewContainer容器,并将相应的参数设置到其Model中
2、执行请求
3、请求处理完后进行一些后置处理
新建ModelAndViewContainer类型的mavContainer参数,用于保存Model和View,它贯穿于整个处理过程,然后对mavContainer进行了设置,主要包括三部分内容
1、将FlashMap中的数据设置到Model中
2、使用modelFactory将SessionAttributes和注释了@ModelAttribute的方法的参数设置到Model
3、根据配置对ignoreDefaultModelOnRedirect进行了设置

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

执行请求,直接调用ServletInvocableHandlerMethod里的invokeAndHandle方法:

requestMappingMethod.invokeAndHandle(webRequest, mavContainer);

处理完请求后的后置处理,是在getModelAndView里面做的:

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
		//更新Model
		modelFactory.updateModel(webRequest, mavContainer);
		if (mavContainer.isRequestHandled()) {
			return null;
		}
		ModelMap model = mavContainer.getModel();
		//创建modelAndView
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
		//如果mavContainer里model的是RedirectAttributes类型,则将其值设置到FlashMap中
		if (model instanceof RedirectAttributes) {
			Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
		}
		return mav;
	}

在这里面一共做了三件事:
1、更新Model
2、创建ModelAndView
3、如果mavContainer里model的是RedirectAttributes类型,则将其值设置到FlashMap中
我们总结一下整个处理请求的过程
1、绑定参数,参数来源主要用request、cookie、session、flashMap、SessionAttribute传递的参数、通过相应的注释了@ModelAttribute的方法进行设置的参数
2、执行请求,使用ArgumentResolver解析参数、执行请求、使用ReturnValueHandler处理返回值等内容
3、处理返回值,model中参数的缓存和ModelAndView的创建

猜你喜欢

转载自blog.csdn.net/m0_37343985/article/details/84404409
今日推荐