0
点赞
收藏
分享

微信扫一扫

SpringMVC运行流程分析之前置流程

关联博客:
​SpringMVC源码分析之策略对象初始化SpringMVC运行流程分析之前置流程SpringMVC运行流程分析之核心流程

前面我们分析了SpringMVC源码分析之策略对象初始化,那么接下来我们看一下SpringMVC的执行流程。

SpringMVC的核心流程主要是由DispatcherServlet处理的,DispatcherServlet也是springMVC的灵魂处理器。其本质是一个servlet,那么我们就按照servlet的观点去看一下。

① XXXServlet的请求确定

SpringMVC运行流程分析之前置流程_spring

如下是HttpServlet的service方法,其唯一作用就是对请求和响应对象进行了类型转换。

@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;

if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}

request = (HttpServletRequest) req;
response = (HttpServletResponse) res;

service(request, response);
}

而FrameworkServlet的service方法覆盖了父类的​​ protected void service(HttpServletRequest req, HttpServletResponse resp)​​​方法对PATCH 请求类型进行了拦截处理(因为其父类HttpServlet并没有拦截PATCH类型的请求,将会直接​​resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);​​)。

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
// 这里会调用父类HttpServlet的service方法
super.service(request, response);
}
}

​HttpServlet的service(HttpServletRequest req, HttpServletResponse resp)​​方法。

protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();

if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}

} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);

} else if (method.equals(METHOD_POST)) {
doPost(req, resp);

} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);

} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);

} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);

} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);

} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//

String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);

resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}

基本就是根据请求类型转向不同的方法,如下所示FrameworkServlet重写了一系列doXXX方法。也就是说HttpServlet的service方法判断请求后,会转发到FrameworkServlet的doXXX处理。
SpringMVC运行流程分析之前置流程_springmvc运行流程_02
而重写后doXXX方法核心本质是转发给了​​​processRequest(request, response);​​。

② FrameworkServlet的processRequest

这里其实就是为当前线程绑定local、request、response等对象。

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// 获取当前LocaleContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// 创建新的localeContext -new SimpleLocaleContext(request.getLocale())
LocaleContext localeContext = buildLocaleContext(request);

// 获取RequestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
// 如果previousAttributes 为null或者其是ServletRequestAttributes实例,则返回 new ServletRequestAttributes(request, response);
// 否则返回null
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

// 异步请求管理器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
// 注册请求回调拦截器
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

// 向LocaleContextHolder RequestContextHolder的ThreadLocal中
//放入 localeContext requestAttributes
initContextHolders(request, localeContext, requestAttributes);

try {
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}

finally {
// 恢复LocaleContext和RequestAttributes为原先的值,默认为null
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
// 发布事件
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}

获取的localeContext 如下:
SpringMVC运行流程分析之前置流程_spring_03
获取的requestAttributes 如下:
SpringMVC运行流程分析之前置流程_spring_04

③ FrameworkServlet的doService

如下所示其doService方法是个抽象方法由子类DispatcherServlet实现。

protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;

子类DispatcherServlet的doService方法。

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);

// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
// 在include请求存在的的情况下保留请求属性的快照,
//以便能够在include请求之后恢复原始属性。
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
// DEFAULT_STRATEGIES_PREFIX = org.springframework.web.servlet
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());

// 闪存属性处理
if (this.flashMapManager != null) {
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);
}
// 解析请求路径
RequestPath requestPath = null;
if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) {
requestPath = ServletRequestPathUtils.parseAndCache(request);
}

try {
// 核心分派方法
doDispatch(request, response);
}
finally {
// 非异步请求,且attributesSnapshot != null,那么将attributesSnapshot 恢复为请求属性
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
if (requestPath != null) {
//request.removeAttribute(PATH_ATTRIBUTE)
ServletRequestPathUtils.clearParsedRequestPath(request);
}
}
}

最终这里走到了核心入口方法​​doDispatch(request, response);​​,这也是很多资料中分析springmvc流程的第一步。

这里首先对include请求(也就是请求包含)进行了判断, 在include请求存在的的情况下保留请求属性的快照,以便能够在include请求之后恢复原始属性。。那么什么是include请求呢?如下方法所示,即请求属性里面包含​​javax.servlet.include.request_uri​​,换而言之即并非外部浏览器或其他客户端发出的顶级请求。

public static boolean isIncludeRequest(ServletRequest request) {
return (request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE) != null);
}

然后设置了一些核心请求属性对象,比如WebApplicationContext、localeResolver、themeResolver等。

闪存属性处理,本质是针对重定向请求参数传递。这块前面博文SpringMVC中重定向请求时传输参数原理分析与实践有过分析。


举报

相关推荐

0 条评论