文档章节

SpringMVC4.x源码分析(四):一个request请求的完整流程和各组件介绍

祖大俊
 祖大俊
发布于 2018/06/11 21:36
字数 1447
阅读 265
收藏 0

本节主要目标是探查一个request请求的完整流程,以及流程中各种web组件的简单介绍,组件的细节,将在后续组件专题文章中详细介绍。

DispatcherServlet初始化完毕后,我们发现它注册2个HandlerMapping,3个HandlerAdapter,主要是为了兼容旧的过时的Controllor编写模式,在本例中,实际生效的是RequestMappingHandlerMapping和RequestMappingHandlerAdapter。

我们来看看这几个对象是什么时候注册进去的。

AnnotationDrivenBeanDefinitionParser#parse()方法中,有一段:

// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
public static void registerDefaultComponents(ParserContext parserContext, Object source) {
	registerBeanNameUrlHandlerMapping(parserContext, source);
	registerHttpRequestHandlerAdapter(parserContext, source);
	registerSimpleControllerHandlerAdapter(parserContext, source);
	registerHandlerMappingIntrospector(parserContext, source);
}

BeanNameUrlHandlerMapping用于处理如下形式的Controllor(已过时):

public class SomeControllor implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return null;
    }
}
<bean id="/some" class="com.spring.controllor.SomeControllor" />

RequestMappingHandlerMapping是本例真正使用的处理@RequestMapping形式的Controllor。同理,本例使用RequestMappingHandlerAdapter,其余读者可自行查看。

RequestMappingHandlerAdapter的初始化:

RequestMappingHandlerAdapter同样实现了InitializingBean接口,实例化对象时,会调用afterPropertiesSet()方法。

@Override
public void afterPropertiesSet() {
	// 初始化@ControllerAdvice标注的类
	initControllerAdviceCache();
    // 参数解析器
	if (this.argumentResolvers == null) {
		List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
		this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
	}
    // @InitBinder标注的方法参数处理器
	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、@InitBinder、argumentResolvers、returnValueHandlers。

其中argumentResolvers和returnValueHandlers会用到HttpMessageConverter消息转换器,用以处理@ResponseBody和@RequestBody所标注的方法或参数。

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(), this.requestResponseBodyAdvice));
    // 使用消息转换器
	resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
	resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
	resolvers.add(new RequestHeaderMapMethodArgumentResolver());
	resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
	resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
	resolvers.add(new SessionAttributeMethodArgumentResolver());
	resolvers.add(new RequestAttributeMethodArgumentResolver());

	// Type-based argument resolution
	resolvers.add(new ServletRequestMethodArgumentResolver());
	resolvers.add(new ServletResponseMethodArgumentResolver());
    // 使用消息转换器
	resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
	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;
}

方法getMessageConverters()获取的,就是默认的HttpMessageConverter消息转换器。

RequestMappingHandlerAdapter的构造函数,会默认加入4个消息转换器,但是,遗憾的是,它会被Spring自动注入的消息转换器给覆盖掉。

public RequestMappingHandlerAdapter() {
	StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
	stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316

	this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);
	this.messageConverters.add(new ByteArrayHttpMessageConverter());
	this.messageConverters.add(stringHttpMessageConverter);
	this.messageConverters.add(new SourceHttpMessageConverter<Source>());
	this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}

覆盖逻辑:AnnotationDrivenBeanDefinitionParser#getMessageConverters()方法返回的消息转换器,会覆盖构造函数加入的默认转换器:

private ManagedList<?> getMessageConverters(Element element, Object source, ParserContext parserContext) {
	Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");
	ManagedList<? super Object> messageConverters = new ManagedList<Object>();
	if (convertersElement != null) {
		messageConverters.setSource(source);
		for (Element beanElement : DomUtils.getChildElementsByTagName(convertersElement, "bean", "ref")) {
			Object object = parserContext.getDelegate().parsePropertySubElement(beanElement, null);
			messageConverters.add(object);
		}
	}

	if (convertersElement == null || Boolean.valueOf(convertersElement.getAttribute("register-defaults"))) {
		messageConverters.setSource(source);
		messageConverters.add(createConverterDefinition(ByteArrayHttpMessageConverter.class, source));

		RootBeanDefinition stringConverterDef = createConverterDefinition(StringHttpMessageConverter.class, source);
		stringConverterDef.getPropertyValues().add("writeAcceptCharset", false);
		messageConverters.add(stringConverterDef);

		messageConverters.add(createConverterDefinition(ResourceHttpMessageConverter.class, source));
		messageConverters.add(createConverterDefinition(SourceHttpMessageConverter.class, source));
		messageConverters.add(createConverterDefinition(AllEncompassingFormHttpMessageConverter.class, source));

		if (romePresent) {
			messageConverters.add(createConverterDefinition(AtomFeedHttpMessageConverter.class, source));
			messageConverters.add(createConverterDefinition(RssChannelHttpMessageConverter.class, source));
		}

		if (jackson2XmlPresent) {
			Class<?> type = MappingJackson2XmlHttpMessageConverter.class;
			RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
			GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
			jacksonFactoryDef.getPropertyValues().add("createXmlMapper", true);
			jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
			messageConverters.add(jacksonConverterDef);
		}
		else if (jaxb2Present) {
			messageConverters.add(createConverterDefinition(Jaxb2RootElementHttpMessageConverter.class, source));
		}

		if (jackson2Present) {
			Class<?> type = MappingJackson2HttpMessageConverter.class;
			RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
			GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
			jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
			messageConverters.add(jacksonConverterDef);
		}
		else if (gsonPresent) {
			messageConverters.add(createConverterDefinition(GsonHttpMessageConverter.class, source));
		}
	}
	return messageConverters;
}

核心分发器DispatcherServlet的doDispatch()方法:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	HttpServletRequest processedRequest = request;
	HandlerExecutionChain mappedHandler = null;
	boolean multipartRequestParsed = false;

	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

	try {
		ModelAndView mv = null;
		Exception dispatchException = null;

		try {
			processedRequest = checkMultipart(request);
			multipartRequestParsed = (processedRequest != request);

			// 返回RequestMappingHandlerMapping生成的chain对象.
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null || mappedHandler.getHandler() == null) {
				noHandlerFound(processedRequest, response);
				return;
			}

			// 返回RequestMappingHandlerAdapter对象.
			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

			String method = request.getMethod();
			boolean isGet = "GET".equals(method);
			if (isGet || "HEAD".equals(method)) {
				long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
				if (logger.isDebugEnabled()) {
					logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
				}
				if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
					return;
				}
			}
              // 调用拦截器的preHandle()方法
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}

			// 处理请求,返回ModelAndView对象.
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

			applyDefaultViewName(processedRequest, mv);
            // 调用拦截器的postHandle()方法
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		}
		catch (Exception ex) {
			dispatchException = ex;
		}
		catch (Throwable err) {
			dispatchException = new NestedServletException("Handler dispatch failed", err);
		}
        // ViewResolver处理结果视图
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	}
	catch (Exception ex) {
        // 调用拦截器的afterCompletion()方法
		triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
	}
	catch (Throwable err) {
		triggerAfterCompletion(processedRequest, response, mappedHandler,
				new NestedServletException("Handler processing failed", err));
	}
	finally {
        //...
	}
}

InvocableHandlerMethod#invokeForRequest()方法中,使用argumentResolvers处理入参:

public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
    // 使用argumentResolvers处理入参
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    // 调用Controllor的目标方法
	Object returnValue = doInvoke(args);
	
	return returnValue;
}

ServletInvocableHandlerMethod#invokeAndHandle()方法中,使用returnValueHandlers处理方法返回值:

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	//...
	try {
		this.returnValueHandlers.handleReturnValue(
				returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}
	catch (Exception ex) {
		//...
		}
		throw ex;
	}
}

DispatcherServlet#render()方法中,使用viewResolvers解析View,并使用View渲染视图:

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
	Locale locale = this.localeResolver.resolveLocale(request);
	response.setLocale(locale);

	View view;
	if (mv.isReference()) {
		// 使用viewResolvers解析出View
		view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
		//...
	}
	else {
		//...
	}
	try {
		if (mv.getStatus() != null) {
			response.setStatus(mv.getStatus().value());
		}
        // view渲染视图
		view.render(mv.getModelInternal(), request, response);
	}
	catch (Exception ex) {
		//...
	}
}

至此,一个reqeust请求的完整流程,就结束了,流程中,我们看到了一些非常重要的注解和组件。

注解:@ControllerAdvice、@InitBinder、@ResponseBody和@RequestBody

组件:HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler、HandlerInterceptor、HttpMessageConverter、ViewResolver

后续我们会对这些注解和组件,进行详细的分析。

补充:

本例我们并没有注册任何自定义的拦截器,但是,我们发现依然有一个ConversionServiceExposingInterceptor拦截器。

public class ConversionServiceExposingInterceptor extends HandlerInterceptorAdapter {

	private final ConversionService conversionService;

	public ConversionServiceExposingInterceptor(ConversionService conversionService) {
		Assert.notNull(conversionService, "The ConversionService may not be null");
		this.conversionService = conversionService;
	}


	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws ServletException, IOException {

		request.setAttribute(ConversionService.class.getName(), this.conversionService);
		return true;
	}

}

该拦截器给每一个reqeust请求放入一个ConversionService,那该拦截器是什么时候注册和创建的呢?

RequestMappingHandlerMapping实现了ApplicationContextAware接口,其父类会调用AbstractHandlerMapping#initApplicationContext()方法,该方法会自动加入ConversionServiceExposingInterceptor拦截器:

protected void initApplicationContext() throws BeansException {
	//...
	detectMappedInterceptors(this.adaptedInterceptors);
	//...
}

protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
	mappedInterceptors.addAll(
			BeanFactoryUtils.beansOfTypeIncludingAncestors(
					getApplicationContext(), MappedInterceptor.class, true, false).values());
}

这其中,MappedInterceptor类里,持有了ConversionServiceExposingInterceptor拦截器,是默认加入的。

原文出处:http://my.oschina.net/zudajun

© 著作权归作者所有

祖大俊
粉丝 802
博文 32
码字总数 52477
作品 0
昌平
私信 提问
Tomcat源码分析

下面谈谈我对Tomcat架构的理解 总体架构: 1、面向组件架构 2、基于JMX 3、事件侦听 1)面向组件架构 tomcat代码看似很庞大,但从结构上看却很清晰和简单,它主要由一堆组件组成,如Server、...

五大三粗
2015/09/28
177
0
Spring MVC的底层源码

一、Spring MVC请求处理流程 引用spring in action上的一张图来说明了springmvc的核心组件和请求处理流程: DispatcherServlet是springmvc中的前端控制器(front controller),负责接收request...

小致dad
04/27
68
0
深入剖析Vue源码 - 组件基础

组件是Vue的一个重要核心,我们在进行项目工程化时,会将页面的结构组件化,组件化意味着独立和共享。编写Vue组件是掌握Vue开发的核心基础,Vue官网也花了大篇幅介绍了组件的知识,并且也深入...

不做祖国的韭菜
05/29
0
0
谈谈 Tomcat 架构及启动过程[含部署]

谈谈 Tomcat 架构及启动过程[含部署] ImportNew2018-01-061 阅读 Tomcat 原文出处: Rainstorm 这个题目命的其实是很大的,写的时候还是很忐忑的,但我尽可能把这个过程描述清楚。因为这是读...

ImportNew
2018/01/06
0
0
谈谈 Tomcat 架构及启动过程[含部署]

原文出处:Rainstorm 这个题目命的其实是很大的,写的时候还是很忐忑的,但我尽可能把这个过程描述清楚。因为这是读过源码以后写的总结,在写的过程中可能会忽略一些前提条件,如果有哪些比较...

Rainstorm
2018/01/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

关于AsyncTask的onPostExcute方法是否会在Activity重建过程中调用的问题

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/XG1057415595/article/details/86774575 假设下面一种情况...

shzwork
今天
6
0
object 类中有哪些方法?

getClass(): 获取运行时类的对象 equals():判断其他对象是否与此对象相等 hashcode():返回该对象的哈希码值 toString():返回该对象的字符串表示 clone(): 创建并返此对象的一个副本 wait...

happywe
今天
6
0
Docker容器实战(七) - 容器中进程视野下的文件系统

前两文中,讲了Linux容器最基础的两种技术 Namespace 作用是“隔离”,它让应用进程只能看到该Namespace内的“世界” Cgroups 作用是“限制”,它给这个“世界”围上了一圈看不见的墙 这么一...

JavaEdge
今天
8
0
文件访问和共享的方法介绍

在上一篇文章中,你了解到文件有三个不同的权限集。拥有该文件的用户有一个集合,拥有该文件的组的成员有一个集合,然后最终一个集合适用于其他所有人。在长列表(ls -l)中这些权限使用符号...

老孟的Linux私房菜
今天
7
0
面试套路题目

作者:抱紧超越小姐姐 链接:https://www.nowcoder.com/discuss/309292?type=3 来源:牛客网 面试时候的潜台词 抱紧超越小姐姐 编辑于 2019-10-15 16:14:56APP内打开赞 3 | 收藏 4 | 回复24 ...

MtrS
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部