Tomcat与SpringMVC请求流程解析
努力是一种生活态度,与年龄无关。所以,无论什么时候,千万不可放纵自己,给自己找懒散和拖延的借口,对自己严格一点儿,时间长了,努力便成为一种心理习惯,一种生活方式!真正能激励你,温暖你,感动你的,不是励志语录心灵鸡汤, 也不是励志的故事, 而是充满正能量的你自己 !
生活中,有人给予帮助,那是幸运,没人给予帮助,那是命运。我们要学会在幸运青睐自己的时候学会感恩,在命运磨炼自己的时候学会坚韧。
前言
你是否曾好奇过,一个请求发起是怎么从我们的tomcat 服务器再到SpringMVC的具体的某一个controller?今天带大家来一览其架构细节,tomcat内部的架构分析会在下一节讨论,本篇主要探讨tomcat是如何和SpringMVC串联起来的。
Tomcat架构
如上图所示,tomcat整体的架构图。本篇重点介绍tomcat是如何跟springmvc衔接上的,在Wrapper中的StandardWrapperValve是tomcat处理消息的边界,在这个Value的invoke()方法中,消息被传递给了Servlet,自此进入了spring mvc的领域。源码分析:
调用其过滤链
重点方法:
有图可知,最终调用的是servlet.service方法,由此正式进入SpringMVC领域了。
SpringMVC源码分析
我们都知道springmvc的核心控制器是
DispatcherServlet
我们一起来看下它的类图结构:
HttpServletBean
在init方法中, 首先将Servlet配置的参数使用BeanWrapper设置到DispatcherServlet中, 然后调用initServletBean
子类通过这个方法进行初始化
FrameworkServlet
入口方法是initServletBean, 里面核心方法有两句:初始化WebApplicationContext;初始化FrameworkServlet
initWebApplicationContext方法里获取Spring的根容器
rootContext,将webApplicationContext
设置到
ServletContext
DispathcerServlet
onRefresh是其入口方法, onRefresh中调用了initStrategies, 在initStrategies中调用了9个初始化方法
首先从context(WebApplicationContext)找自定义的Bean, 找不到则getDefaultStrategy获取默认组件
请求处理流程
由上一小节的分析知道,tomcat最终调用的是servlet.service方法,我们直接进入DispatcherServlet的
doService方法:
实际处理请求的方法是
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
方法主要是从List<HandlerMapping> handlerMappings集合中遍历查找一个合适的处理器(Handler),返回的结果是一个HandlerExecutionChain。然后再根据HandlerExecutionChain里携带的Handler去获取HandlerAdapter
getHandlerAdapter
方法主要是从List<HandlerAdapter> handlerAdapters集合中遍历查找一个合适的处理器适配器(HandlerAdapter),返回的结果是一个
HandlerAdapter。
processDispatchResult
方法主要根据方法执行完成后封装的ModelAndView
,转发到对应页面.
由此得到SpringMVC
处理请求的整个流程。
接下来我们重点来看下
handle
方法:
在基类AbstractHandlerMethodAdapter
RequestMappingHandlerAdapter的
invokeHandlerMethod
invokeAndHandle
ServletInvocableHandlerMethod的
invokeAndHandle
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
getArgumentResolver
该方法的逻辑就是先从
argumentResolver
缓存中找到能够执行参数绑定的
HandlerMethodArgumentResolver,
如果找不到就从
HandlerMethodArgumentResolver
找,我这边解析得到的是
RequestResponseBodyMethodProcessor
接下来重点来了
这就是我们上一节
介绍的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
AbstractMessageConverterMethodProcessor
最后在
AbstractJackson2HttpMessageConverter
通过json序列化把body写入OutputStream,对tomcat来说是
CoyoteOutputStream
至此,本篇的内容就全部结束了。
总结
今天主要跟大家分享了tomcat的架构及SpringMVC的请求流程,对其入参和出参做了源码分析,结合上一篇的把之前的疑惑终于解答了,这个过程当中也对json序列化的知识做了复习,有兴趣的小伙伴们可以自行调试源码试试。
长按关注,欢迎一起探讨技术