vlambda博客
学习文章列表

Tomcat与SpringMVC请求流程解析

努力是一种生活态度,与年龄无关。所以,无论什么时候,千万不可放纵自己,给自己找懒散和拖延的借口,对自己严格一点儿,时间长了,努力便成为一种心理习惯,一种生活方式!真正能激励你,温暖你,感动你的,不是励志语录心灵鸡汤, 也不是励志的故事, 而是充满正能量的你自己 !


生活中,有人给予帮助,那是幸运,没人给予帮助,那是命运。我们要学会在幸运青睐自己的时候学会感恩,在命运磨炼自己的时候学会坚韧。


前言

你是否曾好奇过,一个请求发起是怎么从我们的tomcat 服务器再到SpringMVC的具体的某一个controller?今天带大家来一览其架构细节,tomcat内部的架构分析会在下一节讨论,本篇主要探讨tomcat是如何和SpringMVC串联起来的。

Tomcat架构

如上图所示,tomcat整体的架构图。本篇重点介绍tomcat是如何跟springmvc衔接上的,在Wrapper中的StandardWrapperValve是tomcat处理消息的边界,在这个Value的invoke()方法中,消息被传递给了Servlet,自此进入了spring mvc的领域。源码分析:


Tomcat与SpringMVC请求流程解析

调用其过滤链

Tomcat与SpringMVC请求流程解析

重点方法:

Tomcat与SpringMVC请求流程解析

有图可知,最终调用的是servlet.service方法,由此正式进入SpringMVC领域了。


SpringMVC源码分析

我们都知道springmvc的核心控制器是

DispatcherServlet

我们一起来看下它的类图结构:


Tomcat与SpringMVC请求流程解析

HttpServletBean

在init方法中, 首先将Servlet配置的参数使用BeanWrapper设置到DispatcherServlet中, 然后调用initServletBean 
子类通过这个方法进行初始化

Tomcat与SpringMVC请求流程解析


FrameworkServlet 
入口方法是initServletBean, 里面核心方法有两句:初始化WebApplicationContext;初始化FrameworkServlet
initWebApplicationContext方法里获取Spring的根容器

rootContext,将webApplicationContext

设置到

ServletContext

Tomcat与SpringMVC请求流程解析

Tomcat与SpringMVC请求流程解析

DispathcerServlet

onRefresh是其入口方法, onRefresh中调用了initStrategies, 在initStrategies中调用了9个初始化方法 
首先从context(WebApplicationContext)找自定义的Bean, 找不到则getDefaultStrategy获取默认组件

Tomcat与SpringMVC请求流程解析


请求处理流程

由上一小节的分析知道,tomcat最终调用的是servlet.service方法,我们直接进入DispatcherServlet的

doService方法:

Tomcat与SpringMVC请求流程解析

实际处理请求的方法是

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

        // Determine handler for the current request.

        //返回一个HandlerExecutionChain

        mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
        // 拦截器prehandler的调用,实际项目里这个用的比较多
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

        // Actually invoke the handler.

        // handleradaptor处理handler,通过反射真正调用的对应的controller,

        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}

}



getHandler

Tomcat与SpringMVC请求流程解析

方法主要是从List<HandlerMapping> handlerMappings集合中遍历查找一个合适的处理器(Handler),返回的结果是一个HandlerExecutionChain。然后再根据HandlerExecutionChain里携带的Handler去获取HandlerAdapter


getHandlerAdapter

Tomcat与SpringMVC请求流程解析

方法主要是从List<HandlerAdapter> handlerAdapters集合中遍历查找一个合适的处理器适配器(HandlerAdapter),返回的结果是一个

HandlerAdapter。


processDispatchResult

方法主要根据方法执行完成后封装的ModelAndView,转发到对应页面.

Tomcat与SpringMVC请求流程解析

由此得到SpringMVC处理请求的整个流程

Tomcat与SpringMVC请求流程解析


 接下来我们重点来看下 

handle

方法:

Tomcat与SpringMVC请求流程解析

在基类AbstractHandlerMethodAdapter

Tomcat与SpringMVC请求流程解析

RequestMappingHandlerAdapter的
invokeHandlerMethod

Tomcat与SpringMVC请求流程解析

invokeAndHandle

Tomcat与SpringMVC请求流程解析

ServletInvocableHandlerMethod的
invokeAndHandle

Tomcat与SpringMVC请求流程解析

InvocableHandlerMethod

类里的三个方法:

@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {

Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}

/**
* Get the method argument values for the current request, checking the provided
* argument values and falling back to the configured argument resolvers.
*
<p>The resulting array will be passed into {@link #doInvoke}.
*
@since 5.1.2

*/

还记得在最后留给大家的

话题吗?参数是如何绑定的,详情就在

getMethodArgumentValues

方法:

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {

if (ObjectUtils.isEmpty(getMethodParameters())) {
return EMPTY_ARGS;
}
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);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled..
if (logger.isDebugEnabled()) {
String error = ex.getMessage();
if (error != null && !error.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, error));
}
}
throw ex;
}
}
return args;

}

HandlerMethodArgumentResolver接口的实现类HandlerMethodArgumentResolverComposite

resolveArgument

getArgumentResolver

Tomcat与SpringMVC请求流程解析

getArgumentResolver

该方法的逻辑就是先从

argumentResolver

缓存中找到能够执行参数绑定的

HandlerMethodArgumentResolver

如果找不到就从

HandlerMethodArgumentResolver

找,我这边解析得到的是

RequestResponseBodyMethodProcessor

Tomcat与SpringMVC请求流程解析

接下来重点来了

Tomcat与SpringMVC请求流程解析

这就是我们上一节

介绍的http报文转换器。


真正的执行无非就是通过反射invoke

/**
* Invoke the handler method with the given argument values.
*/
@Nullable
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, 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 {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}


接下来就是返回值的处理了,继续看

ServletInvocableHandlerMethod

Tomcat与SpringMVC请求流程解析


Tomcat与SpringMVC请求流程解析


Tomcat与SpringMVC请求流程解析

AbstractMessageConverterMethodProcessor

Tomcat与SpringMVC请求流程解析

最后在

AbstractJackson2HttpMessageConverter

通过json序列化把body写入OutputStream,对tomcat来说是

CoyoteOutputStream


至此,本篇的内容就全部结束了。

总结

今天主要跟大家分享了tomcat的架构及SpringMVC的请求流程,对其入参和出参做了源码分析,结合上一篇的把之前的疑惑终于解答了,这个过程当中也对json序列化的知识做了复习,有兴趣的小伙伴们可以自行调试源码试试。


仰望星空之一粒麦子 发起了一个读者讨论 你知道SpringMVC是怎么处理是否需要modelview来显示?


长按关注,欢迎一起探讨技术