SpringMVC RequestMappingHandlerAdapter源码解析

SpringMVC RequestMappingHandlerAdapter

他是干什么

RequestMappingHandlerAdapter处理执行我们最常用的@requestMapping 的方法,也是目前mvc提供的最常用最复杂的 handlerAdapter相对于其他的。他功能很简单:通过反射执行我们标记 @requestMapping 的方法。但是他复杂在,需要我们对执行@requestMapping 入参的绑定(因为没有对入参做要求,所以很实用,但是处理起来会面临各种各样的情况,所以很复杂),和一些我们定义好的前置处理步骤比如@initbinder,@modeAttribute等标记代码块的执行,要给对参数,有的都要给。

初始化

我们先从初始化入手看他的结构,一看到InitializingBean,找afterPropertiesSet如下

	@Override
	public void afterPropertiesSet() {
		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);
		}
	}

先看第一行

//找到@ControllerAdvice 和ModelAttribute,InitBinder的method和bean
initControllerAdviceCache(); 



/*
* 对应源码
*/
private void initControllerAdviceCache() {
		if (getApplicationContext() == null) {
			return;
		}
		if (logger.isInfoEnabled()) {
			logger.info("Looking for @ControllerAdvice: " + getApplicationContext());
		}
		// 找到所有@ControllerAdvice标记的bean
		List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
		AnnotationAwareOrderComparator.sort(beans);//排序
		
		List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>();

		for (ControllerAdviceBean bean : beans) {
			//找到ModelAttribute标记,同时没有被RequestMapping标记的method
			Set<Method> attrMethods = MethodIntrospector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
			if (!attrMethods.isEmpty()) {
				this.modelAttributeAdviceCache.put(bean, attrMethods);
				if (logger.isInfoEnabled()) {
					logger.info("Detected @ModelAttribute methods in " + bean);
				}
			}
			//找到@initbinder标记的method
			Set<Method> binderMethods = MethodIntrospector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
			if (!binderMethods.isEmpty()) {
				this.initBinderAdviceCache.put(bean, binderMethods);
				if (logger.isInfoEnabled()) {
					logger.info("Detected @InitBinder methods in " + bean);
				}
			}
			//看是不是实现了RequestBodyAdvice接口,实现了放到一个list中
			if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
				requestResponseBodyAdviceBeans.add(bean);
				if (logger.isInfoEnabled()) {
					logger.info("Detected RequestBodyAdvice bean in " + bean);
				}
			}
			//看是不是实现了ResponseBodyAdvice接口,实现了放到一个list中
			if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
				requestResponseBodyAdviceBeans.add(bean);
				if (logger.isInfoEnabled()) {
					logger.info("Detected ResponseBodyAdvice bean in " + bean);
				}
			}
		}
		//把所有的bodyAdvice 放入一个requestResponseBodyAdvice,以备后面使用
		if (!requestResponseBodyAdviceBeans.isEmpty()) {
			this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
		}
	}


经过以上操作就把所有的spring中的@ControllerAdvice 注解的bean,和对应的@initbinder,@modeAttribute的method还有两个request/ResponseBodyAdvice都放到我们的RequestMappingHandlerAdapter中的,以备后面使用也就是完成的initControllerAdviceCache 初始化initControllerAdviceCache缓存。

后面三种就是加载各种解析器

调用过程

程序入口是handleInternal方法,看一下源码

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

		ModelAndView mav;
		//校验session是否必须存在,还有是否支这个请求
		checkRequest(request);

		// 是否对session同步
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// 如果没有可用的HttpSession ->不需要互斥
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// 如果根本不要同步
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}
		//看看response报文头中是否包含cache_control
		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			//判断当前这handler是否有@sessionAttribute,如果有就解析出来,放到缓存中准备下次使用
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				//设置返回缓存过期时间
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				prepareResponse(response);
			}
		}

		return mav;
	}

我们从头开始看

checkRequest(request);

protected final void checkRequest(HttpServletRequest request) throws ServletException {
		// 得到当前请求的方法类型,看看当前handleradapter是否支持这个方法
		String method = request.getMethod();
		if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
			throw new HttpRequestMethodNotSupportedException(
					method, StringUtils.toStringArray(this.supportedMethods));
		}

		// 看看是否必须要这个session,如果必须没有抛异常
		if (this.requireSession && request.getSession(false) == null) {
			throw new HttpSessionRequiredException("Pre-existing session required but none found");
		}
	}

第二段执行方法后面细说,剩下的一段就是

if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				prepareResponse(response);
			}
		}

这段代码总的意思是:
一般情况下,如果返回报文中没有对缓存的特殊控制,如果有@sessionAttribute则组织使用缓存,否则就什么也不做

下面我们看重点,执行方法

mav = invokeHandlerMethod(request, response, handlerMethod);



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

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			//看下面 1
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			//看下面 2
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

			//看下面3
			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			invocableMethod.setDataBinderFactory(binderFactory);
			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
			//新建传递参数的 mavcontainer容器,并把相应的参数设置进去
			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
			//设置flashMap的参数进去
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			//用modelFactoey把 sessionAttribute和modelAttribute的参数设置进去
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			//这个是在mavcontainer中会用到后面细说
			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();
				if (logger.isDebugEnabled()) {
					logger.debug("Found concurrent result value [" + result + "]");
				}
				//执行请求
				invocableMethod = invocableMethod.wrapConcurrentResult(result);
			}

			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}
			//看 4
			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}

我们看一下这个方法,首先封装了一个webRequest,这个用于argumentResolver解析参数时使用,然后就是对 WebDataBinderFactory, ModelFactory, ServletInvocableHandlerMethod进行定义和初始化

  1. WebDataBinderFactory
    用来创建WebDataBinder,WebDataBinder主要用来参数绑定,实现参数跟String之间的类型转换,看一下源码
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
		//取得bean的class
		Class<?> handlerType = handlerMethod.getBeanType();
		//看看是不是这个controller已经在缓存中有了
		Set<Method> methods = this.initBinderCache.get(handlerType);
		// 如果没有
		if (methods == null) {
			//查找这个controller看看是不是有initbinder,有的话加载到缓存中,下次使用
			methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
			this.initBinderCache.put(handlerType, methods);
		}
		//自定义保存initbinder方法的零时变量
		List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
		// 先找到符合条件的全局 initbinder,添加到initbindermethods中
		for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) {
			if (entry.getKey().isApplicableToBeanType(handlerType)) {
				Object bean = entry.getKey().resolveBean();
				for (Method method : entry.getValue()) {
					// 先添加全局的initbinder
					initBinderMethods.add(createInitBinderMethod(bean, method));
				}
			}
		}
		//再添加当前这个controller的
		for (Method method : methods) {
			Object bean = handlerMethod.getBean();
			initBinderMethods.add(createInitBinderMethod(bean, method));
		}
		//创建factory返回
		return createDataBinderFactory(initBinderMethods);
	}

从上面的源码可以看出,主要是找到所有的initbinder 来创建WebDataBinderFactory,注意下面这个MethodIntrospector.selectMethods 找不到也会返回一个空set,不是null,这个可以确保我们只用查一次,不用发现发没有,次次查

methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
  1. ModelFactoey
    用来处理model,主要是(1)在处理器处理之前对model初始化 (2)在处理完后对model进行参数更新

先说初始化,初始化主要是做三部分内容

  1. 将原来的@sessionAttribute 中的值设置到model中。
  2. 执行注释了@modelAttribute 的方法,并将值设置到model中。
  3. 处理器注释了@modelAttribute 的参数,如果同时在@sessionAttribute中也配置了,而且在mavContainer中还没有值,则从全部的sessionAttribute中找到值并设置上。

更新是先对SessionAttributes进行设置,设置的规则是如果处理器设置了sessionStatus中的setComplete 就将sessionAttributes清空,否则就将model中的参数设置给sessionAttribute,然后按照需要把给model设置参数对应的bindingResult下面看一下源码

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
		// 获取SessionAttributesHandler,也就是@sessionAttribute标签的一些信息
		SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
		Class<?> handlerType = handlerMethod.getBeanType();
		//同上面initbinder的逻辑,找到modeAttribute 的方法
		Set<Method> methods = this.modelAttributeCache.get(handlerType);
		if (methods == null) {
			methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
			this.modelAttributeCache.put(handlerType, methods);
		}
		List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
		// 和上面的一样,先是公共的,再是私有的
		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);
	}

看一下 new ModelFactory(attrMethods, binderFactory, sessionAttrHandler); 可以看出创建他需要 一个是要用的 initbinder一个是modelAttributes,还有一个是sessionAttributesm给齐了

  1. ServletInvocableHandlerMethod核心方法请求的处理就是通过他来执行,参数绑定,和请求处理都是他来完成,首先把handlerMethod 封装为 servletInvocableHandlermethod,然后再把解析器设置进去
			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			invocableMethod.setDataBinderFactory(binderFactory);
			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

4.getModelAndView(mavContainer, modelFactory, webRequest); 主要做三件事1)调用modelFactory更新了model。2)根据mavcontainer 创建了modelandview。3)如果mavcontainer是redirectAttribute类型的就将值设置到 falshmap中

getModelAndView(mavContainer, modelFactory, webRequest);

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();
		//创建mav
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
		//设置falshMap
		if (model instanceof RedirectAttributes) {
			Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
		}
		return mav;
	}


小结

总的来说逻辑是很简单的,就是用来很多陌生的组件,后面有专门的分析,主要流程的就是绑定参数,执行请求,处理返回值。

1)参数绑定
参数的主要来源就是有6类参数的主要来源就是有6类

  1. request中参数
  2. cookie中的参数
  3. session中参数
  4. falshMap中的参数
  5. @sessionAttribute中的参数
  6. @modelAttributes中的参数
    前面三个在request中处理,falshMap是直接在RequestMappingHandlerAdapter处理,后面两个在ModelFactory中处理

2) 处理请求,用的是ServletInvocableHandlerMethod
3) 处理收尾,主要是更新model中的缓存,和modelAndView的创建

猜你喜欢

转载自blog.csdn.net/qq_33221085/article/details/83343442