SpringMVC源码解读--HandlerAdapter源码解读

Posted 低调的洋仔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC源码解读--HandlerAdapter源码解读相关的知识,希望对你有一定的参考价值。

 

一路的继承关系来看的话因为实现了InitializingBean的话,然后该类首先可以调用的方法就是

afterPropertiesSet

 

 

@Override
	public void afterPropertiesSet() 
		// Do this first, it may add ResponseBody advice beans
		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. 初始化注释了ControllerAdvice类的相关属性。

2. 初始化argumentResolvers,这个属性的话主要是用于给处理器方法和注释了@ModuleAttribute的方法设置参数。

3. 初始化initBinderArgumentResolvers,这个主要是用于给注释了@initBinder的方法设置参数。

5. 初始化returnValueHandlers,然后这个主要是用于将处理器结果处理成ModelAndView的结果类型。

 

 

 

private void initControllerAdviceCache() 
		if (getApplicationContext() == null) 
			return;
		
		if (logger.isInfoEnabled()) 
			logger.info("Looking for @ControllerAdvice: " + getApplicationContext());
		
		// 获取注释了ControllerAdvice的bean
		List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
		OrderComparator.sort(beans);// 排序
		
		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接口的类从前面添加到responseBodyAdvice属性。
		if (!responseBodyAdviceBeans.isEmpty()) 
			this.responseBodyAdvice.addAll(0, responseBodyAdviceBeans);
		
			// 获取注释了ControllerAdvice的bean
		List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
		OrderComparator.sort(beans);// 排序
		
		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接口的类从前面添加到responseBodyAdvice属性。
		if (!responseBodyAdviceBeans.isEmpty()) 
			this.responseBodyAdvice.addAll(0, responseBodyAdviceBeans);
		
	


同时注释了RequestMapping的方法,只是将返回值设置为Model而不是作为View使用了,但是不会提前执行。

 

实现了ResponseBodyAdvice的类,并没有在for循环中直接添加到responseBodyAdvice中,添加的代码就是this.reponseBodyAdvice.addAll(0,responseBodyAdvice);

先拍了个顺序然后进行的分发,然后将this.reposnseBodyAdvice中第零个设置成responseBodyAdviceBeans.

 

非常核心的属性设置过程。

 

/**
	 * Return the list of argument resolvers to use including built-in resolvers
	 * and custom resolvers provided via @link #setCustomArgumentResolvers.
	 */
	private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() 
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();

		// Annotation-based argument resolution// 添加按照注释解析参数的解析器
		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()));

		// Type-based argument resolution// 添加按照类型解析参数的解析器
		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());

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

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

		return resolvers;
	

自己无法编写一个PathVariable解析器来解析PathVariable中的参数,即使写出来注册到AbstractHandlerAdapater上面也不会被调用,SpringMVC的自己定义的解析器的顺序也是固定的,不可以改变的。

 

 

///

上面是初始化部分参数信息等。

 

实际调用的方法,handle方法调用了handleInternal方法。

 

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

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

		// Execute invokeHandlerMethod in synchronized block if required.
		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方法

 

检查请求类型是不是支持

2.检查session是不是必须的,判断session实际是否存在、

3 给reponse设置缓存过期时间。

 

/**
	 * Check and prepare the given request and response according to the settings
	 * of this generator. Checks for supported methods and a required session,
	 * and applies the given number of cache seconds.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param cacheSeconds positive number of seconds into the future that the
	 * response should be cacheable for, 0 to prevent caching
	 * @param lastModified if the mapped handler provides Last-Modified support
	 * @throws ServletException if the request cannot be handled because a check failed
	 */
	protected final void checkAndPrepare(
			HttpServletRequest request, HttpServletResponse response, int cacheSeconds, boolean lastModified)
			throws ServletException 

		// Check whether we should support the request method.
		String method = request.getMethod();
		if (this.supportedMethods != null && !this.supportedMethods.contains(method)) 
			throw new HttpRequestMethodNotSupportedException(
					method, StringUtils.toStringArray(this.supportedMethods));
		

		// Check whether a session is required.
		if (this.requireSession) 
			if (request.getSession(false) == null) 
				throw new HttpSessionRequiredException("Pre-existing session required but none found");
			
		

		// Do declarative cache control.
		// Revalidate if the controller supports last-modified.
		applyCacheSeconds(response, cacheSeconds, lastModified);
	

 

 

 

	/**
	 * Apply the given cache seconds and generate respective HTTP headers.
	 * <p>That is, allow caching for the given number of seconds in the
	 * case of a positive value, prevent caching if given a 0 value, else
	 * do nothing (i.e. leave caching to the client).
	 * @param response the current HTTP response
	 * @param seconds the (positive) number of seconds into the future that
	 * the response should be cacheable for; 0 to prevent caching; and
	 * a negative value to leave caching to the client.
	 * @param mustRevalidate whether the client should revalidate the resource
	 * (typically only necessary for controllers with last-modified support)
	 */
	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.
	

 

重点在这里

重点在这里

重点在这里

 

InvokeHandleMethod方法

 

/**
	 * Invoke the @link RequestMapping handler method preparing a @link ModelAndView
	 * if view resolution is required.
	 */
	private ModelAndView invokeHandleMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception 

		ServletWebRequest webRequest = new ServletWebRequest(request, response);

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


getDataBindFactory方法

 

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception 
		Class<?> handlerType = handlerMethod.getBeanType();
		Set<Method> methods = this.initBinderCache.get(handlerType);// initBinderCache中是不是有了这个类了
		if (methods == null) 
			methods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS);// 没有的话就创建好放进去
			this.initBinderCache.put(handlerType, methods);
		
		List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();// 定义保存InitBinder方法的临时变量
		// Global methods first所有符合条件的initBinderAdviceCache中的数据添加到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()) 
					initBinderMethods.add(createInitBinderMethod(bean, method));
				
			
		// 将当前Handler中的InitBinder方法,添加到initBinderMethods
		for (Method method : methods) 
			Object bean = handlerMethod.getBean();
			initBinderMethods.add(createInitBinderMethod(bean, method));
		// 创建DatabinderFactory并返回。
		return createDataBinderFactory(initBinderMethods);
	


ModleFactory是用来处理Modle的,主要包含俩功能,一个是处理器具体处理之前对Modle进行初始化,同时在处理完请求后对Model参数更新。

 

流程:

1. 将原来的SessionAttributes中的值设置到Model.

2. 执行相应的注释了ModelAttribute的方法并将其值设置为Model

3. 处理器中同时注释了ModelAttribute参数的如果同时在sessionAttribute中也配置了,而且在mavContainer中还没有值则从全部的SessionAttributes中查找出并设置进去。

 

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) 
		SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
		Class<?> handlerType = handlerMethod.getBeanType();
		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>();
		// Global methods first
		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);
	

 

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>重点在这里>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

到底是怎么调用的?在哪里调用的?

就在这里,这个方法就是完成了参数的填充以及方法的调用的。

 

 

/**
	 * Invokes the method and handles the return value through one of the
	 * configured @link HandlerMethodReturnValueHandlers.
	 * @param webRequest the current request
	 * @param mavContainer the ModelAndViewContainer for this request
	 * @param providedArgs "given" arguments matched by type (not resolved)
	 */
	public void invokeAndHandle(ServletWebRequest webRequest,
			ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception 

		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		setResponseStatus(webRequest);

		if (returnValue == null) 
			if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) 
				mavContainer.setRequestHandled(true);
				return;
			
		
		else if (StringUtils.hasText(this.responseReason)) 
			mavContainer.setRequestHandled(true);
			return;
		

		mavContainer.setRequestHandled(false);
		try 
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		
		catch (Exception ex) 
			if (logger.isTraceEnabled()) 
				logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
			
			throw ex;
		
	


接下来重点又是invokeForRequest方法。

 

 

/**
	 * Invoke the method after resolving its argument values in the context of the given request.
	 * <p>Argument values are commonly resolved through @link HandlerMethodArgumentResolvers.
	 * The @code provideArgs parameter however may supply argument values to be used directly,
	 * i.e. without argument resolution. Examples of provided argument values include a
	 * @link WebDataBinder, a @link SessionStatus, or a thrown exception instance.
	 * Provided argument values are checked before argument resolvers.
	 * @param request the current request
	 * @param mavContainer the ModelAndViewContainer for this request
	 * @param providedArgs "given" arguments matched by type, not resolved
	 * @return the raw value returned by the invoked method
	 * @exception Exception raised if no suitable argument resolver can be found,
	 * or if the method raised an exception
	 */
	public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception 

		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) 
			StringBuilder sb = new StringBuilder("Invoking [");
			sb.append(getBeanType().getSimpleName()).append(".");
			sb.append(getMethod().getName()).append("] method with arguments ");
			sb.append(Arrays.asList(args));
			logger.trace(sb.toString());
		
		Object returnValue = doInvoke(args);
		if (logger.isTraceEnabled()) 
			logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
		
		return returnValue;
	


然后通过getMethodArgumentValues方法获取到参数值的数组。

 

 

/**
	 * Get the method argument values for the current request.
	 */
	private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception 

		MethodParameter[] parameters = getMethodParameters();
		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) 
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
			args[i] = resolveProvidedArgument(parameter, providedArgs);
			if (args[i] != null) 
				continue;
			
			if (this.argumentResolvers.supportsParameter(parameter)) 
				try 
					args[i] = this.argumentResolvers.resolveArgument(
							parameter, mavContainer, request, this.dataBinderFactory);
					continue;
				
				catch (Exception ex) 
					if (logger.isDebugEnabled()) 
						logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
					
					throw ex;
				
			
			if (args[i] == null) 
				String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
				throw new IllegalStateException(msg);
			
		
		return args;
	

然后后面就是doInvoke方法来进行实际的调用了。

 

 

Object returnValue = doInvoke(args);

 

/**
	 * Invoke the handler method with the given argument values.
	 */
	protected Object doInvoke(Object... args) throws Exception 
		ReflectionUtils.makeAccessible(getBridgedMethod());
		try 
			return getBridgedMethod().invoke(getBean(), args);
		
		catch (IllegalArgumentException ex) 
			assertTargetBean(getBridgedMethod(), getBean(), args);
			throw new IllegalStateException(getInvocationErrorMessage(ex.getMessage(), args), ex);
		
		catch (InvocationTargetException ex) 
			// Unwrap for HandlerExceptionResolvers ...
			Throwable targetException = ex.getTargetException();
			if (targetException instanceof RuntimeException) 
				throw (RuntimeException) targetException;
			
			else if (targetException instanceof Error) 
				throw (Error) targetException;
			
			else if (targetException instanceof Exception) 
				throw (Exception) targetException;
			
			else 
				String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
				throw new IllegalStateException(msg, targetException);
			
		
	

到这里足够了method.invoke(bean, args),这个反射调用了。然后就OK了。


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

 

处理完请求后,在getModelAndView方法中会做几件事,

 

1. 调用ModelFactory的updateModel方法更新了Model包含设置了SessionAttributes和给Model设置BindingResult

2. 根据mavContainer创建了ModelAndView.

3. 如果mavContainer里面的model是RedirectAttributes类型将其设置到FlashMap中。

 

 

 

 

以上是关于SpringMVC源码解读--HandlerAdapter源码解读的主要内容,如果未能解决你的问题,请参考以下文章

SpringMVC源码解读 - HandlerMapping

SpringMVC源码解读 - RequestMapping注解实现解读 - RequestMappingInfo

SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系

SpringMVC源码解读--HandlerMapping代码解读

SpringMVC源码解读--HandlerMapping代码解读

SpringMVC源码解读--DispatcherServlet类