首页 > 其他分享 >SpringMVC请求处理流程

SpringMVC请求处理流程

时间:2023-01-06 14:37:16浏览次数:32  
标签:请求 SpringMVC 流程 request response ex mappedHandler processedRequest


​​Spring原码解析—SpringMVC请求处理流程​​

我们都知道SpringMVC的核心就是DispatcherServlet,它负责接收请求和调度,在开始原码解析前先来看看SpringMVC请求的一个处理流程图:



SpringMVC请求处理流程_http


1)客户端发送请求,DispatcherServlet接收请求

2)DispatcherServlet将请求交给DefaultAnnotationHandlerMapping找到对应的映射方法

3)通过对应的DefaultAnnotationHandlerAdapter invoke特定的方法

4)调用自己的Controller和相应的方法,给Model设置数据和返回视图

5)DefaultAnnotationHandlerAdapter组装ModelAndView并返回给DispatcherServlet

6)通过InternalResourceViewResolver解析

7)响应客户端


对请求流程有了一个初步的了解后,接下来我们开始原码分析:

确定请求入口:

因为DispatcherServlet本质上是一个Servlet所有它一定有doPost()和doGet()方法,而DispatcherServlet又继承FrameworkServlet,通过分析我们在FrameworkServlet中找到请求处理方法,如图:



SpringMVC请求处理流程_http_02


而doPost()和doGet()方法又会调用pocessRequest()这个方法,如图:



SpringMVC请求处理流程_http_03


因此我们在processRequest这个方法上打上断点。

接下来开启debug并发送请求,来到断点处processRequest方法,其原码如下:

//处理请求
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//获取LocaleContextHolder中原来保存的LocaleContext
//刚开始一般是null
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//获取当前请求的LocaleContext
LocaleContext localeContext = buildLocaleContext(request);
//获取原有的属性
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//获取当前请求的属性
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
//异步操作
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//将当前请求的LocaleContext和ServletRequestAttributes设置到LocaleContextHolder和RequestContextHolder中
initContextHolders(request, localeContext, requestAttributes);

try {

//前面都可以不太了解,重点是这个方法,执行完前面的操作后,会将请求统一交给这个方法处理逻辑
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}

finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
//这里执行了request后有一个requestActive状态值会转变为false,执行前是true

}

if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
//发布ServletRequestHandlerEvent消息,这个请求是否执行成功都会发布消息的
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}

我们在来看一下doService这个方法,这个方法被DispatcherServlet覆写了,所有调用的是DispatcherServlet的实现:

DispatcherServlet.java

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}

// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.

//对请求的属性做备份
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}

//设置一些有用的属性
//这些不是我们关注的重点 我们只需要搞懂整个流程,不要被代码迷惑
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

try {
//实际上,执行完一系列操作它又会调用这个方法,我们跟踪进入
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}

在看原码的时候,我们首先不应该关注太多的细节,而应掌握它的主要流程。

我们接着看一下doDispatch()这个方法:

DispatcherServlet.java

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.
//通过我们请求的路径 拿到需要访问的自定义的Controller里的指定的方法 包含请求和拦截器
mappedHandler = getHandler(processedRequest); //-----》HandlerExecutionChain -》mappedHandler
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
//通过handler拿到对应的handlerAdapter 我们这里是RequestMappingHandlerAdapter
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 (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// Actually invoke the handler.
//通过反射调用 我们那个方法 并通过RequestMappingHandlerAdapter组装ModelAndView 也就是将return的String与Model组装
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);
}
}
}
}

在执行完processDispatchResult后就差不多完成了我们的流程

我们看一下这个方法:



SpringMVC请求处理流程_java_04


最后在看一下publishRequestHandledEvent这个事件发布方法的内容。

private void publishRequestHandledEvent(
HttpServletRequest request, HttpServletResponse response, long startTime, Throwable failureCause) {

if (this.publishEvents) {
// Whether or not we succeeded, publish an event.
long processingTime = System.currentTimeMillis() - startTime;
int statusCode = (responseGetStatusAvailable ? response.getStatus() : -1);
this.webApplicationContext.publishEvent(
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause, statusCode));
}
}

在publishRequestHandledEvent方法执行后,如果有自定义实现ApplicationListener,那么就会调用里面的onApplicationEvent方法,如图:

import org.springframework.web.context.support.ServletRequestHandledEvent;

@Component
public class ServletReqestHandledEventListener implements ApplicationListener<ServletRequestHandledEvent> {
final static Logger logger = LoggerFactory.getLogger("SRHEServlet");

@Override
public void onApplicationEvent(ServletRequestHandledEvent servletRequestHandledEvent) {
logger.info(servletRequestHandledEvent.getDescription()+"自定义ApplicationListener");
}
}

当请求结束后,控制台打印:



SpringMVC请求处理流程_java_05


它可以用来记录日志,并且只需要把自己需要做的事情写到onApplicationEvent里面就可以了。
总结:processRequest -> doService -> doDispatch -> preHandle..post..after ...
->processDispatchResult ->publishRequestHandledEvent



SpringMVC请求处理流程_http_06


标签:请求,SpringMVC,流程,request,response,ex,mappedHandler,processedRequest
From: https://blog.51cto.com/u_15932265/5993513

相关文章