SpringMvc-DispatcherServlet原理分

1.前置知识

  • 本文是针对springMVC请求执行流程来分析,并不会设计到如何使用
  • SpringMVC大体执行流程图,网上很多图,从执行大局看基本上是一致,本文是基于大图分析每一步是如何执行,处理什么来进行分析。
    下图来源于网络:

image.png

  • 几个主要的类说明:
    • DispatcherServlet:前端控制器,是SpringMVC全局请求分发器,一个请求匹配前端控制器 DispatcherServlet 的请求映射路径(在 web.xml中指定), WEB 容器将该请求转交给 DispatcherServlet 处理
    • DispatcherServlet 接收到请求后, 将根据 请求信息 交给 处理器映射器 (HandlerMapping)
    • HandlerMapping 根据用户的url请求 查找匹配该url的 Handler,并返回一个执行链
    • DispatcherServlet 再请求 处理器适配器(HandlerAdapter) 调用相应的 Handler 进行处理并返回 ModelAndView 给 DispatcherServlet
    • DispatcherServlet 将 ModelAndView 请求 ViewReslover(视图解析器)解析,返回具体 View
    • DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中)
  • 相关类说明:
    • DispatcherServlet 将页面响应给用户
    • DispatcherServlet:前端控制器
    • HandlerMapping:处理器映射器
    • Handler:处理器
    • HandlAdapter:处理器适配器
    • ViewResolver:视图解析器
    • View:视图

2.源码分析流程图

SpringMvc请求执行.png
上图是后续本文分析源码的流程

3.源码分析

3.1 SpringMVC入口分析

首先请求进来,会先经过一系列过滤器,最后到底FrameworkServlet的service方法,该类是DispatcherServlet的父类,也可以说是先到DispatcherServlet,下面看以下service方法到底会做那些操作:

  1. 判断请求类型是否是null、PATCH,如果是则直接进入processRequest方法,否则调用父类service即httpServlet

FrameworkServlet:

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//这里获取请求的类型,判断请求是否是null或者Patch如果是直接进入ProcessRequest方法,否则调用父类的service方法
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
//调用父类HttpServlet
super.service(request, response);
}
}

HttpServlet中的service:
HttpServlet的service主要操作:
1.判断请求类型,进行调用不同的请求类型的处理方法doGet、doPost等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
scss复制代码protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//获取请求方法
String method = req.getMethod();
//判断类型如果是get请求则调doGet
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
//判断类型如果是HEAD请求则调doHead
} 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);
}
省略部分....
}

具体的doPost、doGet等方法其实在FrameworkServlet中实现的,最终不同的请求类型调用不同方法做完处理之后都会调用processReqest方法
下面简单看FrameworkServlet中doGet和doPost中的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//最终调用processRequest方法
processRequest(request, response);
}

/**
* Delegate POST requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//最终调用processRequest方法
processRequest(request, response);
}

3.2 ProcessRequest方法分析

该方法主要三件事:

  1. 初始化web管理器以及注册回调拦截器
  2. 调用doService执行具体逻辑
  3. 通过事件推送吧结果输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
ini复制代码protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

long startTime = System.currentTimeMillis();
Throwable failureCause = null;

LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
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());

initContextHolders(request, localeContext, requestAttributes);

try {
//该方法主要操作在于doService方法
doService(request, response);
}
catch (ServletException | 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();
}
logResult(request, response, failureCause, asyncManager);
//执行完成后进行相关结果处理
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}

在调用的doService方法核心逻辑:
1.初始化一些可用的处理器以及视图对象
2.调用真正分发的doDispatch方法
下面直接看doDispatch方法:
doDispatch方法作用:

1.通过调用getHandler方法获取包含了HandlerMapping的HandlerExecutionChain对象

2.从HandlerExecutionChain对象中通过getHandler获取对应的handlerMethod,然后根据handlerMethod获取handlerAdapter

3.执行HandlerExecutionChain的所有拦截器的前置方法preHandle

4.通过handlerAdapter调用handle去执行具体处理方法,最终是调用到某个controller方法

5.执行handlerExecutionChain中的所有拦截器的后置处理方法applyPostHandle

6.调用processDispatchResult方法处理请求分发结果,主要是处理视图解析等操作

7.最后处理资源清理工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
ini复制代码protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
//并不是直接处理的handler,而是handler的执行链,里面会有符合条件的拦截器
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);

// 这个是核心操作,它主要根据请求url去匹配对应的handlerMapping,然后经过一系列处理获取到handler执行链
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}

// 此处调用主要是寻找handler的适配器
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;
}
}
//调用handler执行链的所有拦截器的前置处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// handler正在执行的方法,即某个controller中的某个方法,然后处理结果返回一个ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//执行handler执行链的所有拦截器的后置处理器
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {

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是如何获取到handler执行链的?
它其实是通过遍历DispatcherServlet中所有的HandlerMapping,然后通过handlerMapping中的getHandler获取handler执行链,但handlerMapping主要分为两种:

  • AbstractUrlHandlerMapping:应对xml中编写的url
  • AbstractHandlerMethodMapping 应对注解形式的controller

而handlerMapping中getHandler方法中主要是进行几个操作:

  • 调用getHandlerInternal方法,该方法是抽象方法,具体实现在对应的handlermapping子类中,主要分为两类:AbstractHandlerMethodMapping这个是基于@RequetMapping等注解形式的handlerMapping,AbstractUrlHandlerMapping类主要是处理可配置的url查找、程序映射到url的等操作比XML里配置一个/hello到对应实现类等可配置url处理。根据reuqest的url调用AbstractUrlHandlerMapping获取handler名称或者对应的handler的Bean名称,这种是基于可配置的url,另外一种是基于方法匹配即controller中定义的每个方法会调用AbstractHandlerMethodMapping
  • 调用getHhandlerInternal后获取到对应handler的bean名称或者实例对象,调用getHandlerExecutionChain构建成HandlerExecutionChain
  • 把获取到的handler放到handlerExecutionChain执行链上
  • 判断是否需要处理跨越问题,如果需要则根据跨域配置以及现有的handler执行链重新构建获取一个新的执行链
    然后HandlerMaping中的getHandler方法源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
ini复制代码public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//获取handler
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//根据handler和request获取到具体的handler执行链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
//跨域处理
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}

return executionChain;
}

下面分析一些基于注解形式的handler获取:
AbstractHandlerMethodMapping的getHandlerInternal方法作用:
1.通过UrlPathHelper工具类根据request获取(url)lookupPath即获取handler的key
2.获取mappingRegistry对象的读锁,避免并发问题还在注册Mapping映射时,出现在获取handler的情况(不知道有没有理解错误)
3.调用lookupHandlerMethod(lookupPath, request);获取handlerMethod对象,该对象包含了方法的参数、返回值、注解等。该方法主要是根据lookupPath即url从MappingRegistry中获取对应的RequetsMappingInfo信息,然后根据映射信息以及url等一些列操作获取到最佳匹配的handlerMethod
4.根据获取到的HandlerMethod对象创建对应的解析器Bean,然后返回
5.解锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
java复制代码@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//通过工具类获取的url这里是lookupPath
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
//获取读锁,避免还早注册,然后这边在读取
this.mappingRegistry.acquireReadLock();
try {
//通过lookupPath湖区哦到对应的handlerMethod,handlerMetho里包含handler
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
//
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
//然后释放锁
this.mappingRegistry.releaseReadLock();
}
}

接着看一下getHandlerExecutionChain是如何生成handler执行链的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
java复制代码protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
//遍历所有拦截器
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
//通过匹配的区别增加到拦截器的list里保存起来,然后返回
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}

handler执行链获取到之后,然后进行获取handler的适配器:
遍历所有的handlerAdapters找到对应的adapter,一般是RequestMappingHandlerAdapter

1
2
3
4
5
6
7
8
9
10
11
12
java复制代码protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
//如果支持则直接返回
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

接着就是执行拦截器的前置处理了,其实就是遍历该handler执行链上的所有拦截器的前置处理方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ini复制代码boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//遍历执行
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}

接着是handler执行handle方法做具体逻辑方法调用

1
2
3
4
5
6
7
8
java复制代码AbstractHandlerMethodAdapter:
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
调用子类的方法
return handleInternal(request, response, (HandlerMethod) handler);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
scss复制代码
RequestMappingHandlerAdapter:
1.调用invokeHandlerMethod方法执行处理

2.判断请求头有没有缓存控制,有则进行缓存处理
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ModelAndView mav;
checkRequest(request);

// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
//核心处理逻辑方法
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
//核心处理逻辑方法
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
//核心处理逻辑方法
mav = invokeHandlerMethod(request, response, handlerMethod);
}

if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {

prepareResponse(response);
}
}

return mav;
}

下面方法是具体handler执行并且返回ModelAndView,主要做了以下几个操作:
该方法是主要handler处理逻辑:
1.先handlerMethod获取到web数据绑定工厂
2.获取模型工厂
3.根据handlerMethod创建出ServletInvocableHandlerMethod对象,该对象还需要设置一下handlerMethod的参数解析器、返回值处理器、数据绑定工厂、参数名处理对象,是一个具有执行、解析、处理参数一个执行方法对象.
4.servletInvocableHandlerMethod对象调用invokeAndHandle方法执行,最底层是基于反射执行Method来处理的
5.执行完成后通过返回值处理器returnValueHandlers处理返回值,然后调用getModelAndView方法获取到ModelAndView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
ini复制代码@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//获取数据绑定工厂\模型解析以及参数解析器、返回值处理handler等等
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//具体执行请求方法,底层基于反射调用具体的业务逻辑方法去处理
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
//处理结果并且返回对应的ModelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}

返回对应的ModelAndView之后,接着就是执行对应拦截器的后置处理方法,
最后就是调用processDispatchResult方法处理结果,该方法大致做了以下几点:
该方法主要做两件事:

1.判断前面执行过程中有没有异常,则遍历调用DispatcherServlet中所有的异常解析处理器HandlerExceptionResolver的resolveException(所有的异常解析处理器都必须实现改接口或者实现AbstractHandlerExceptionResolver的抽象子类,平常自定义异常解析可以基于上面者两个类做扩展)

2.没有异常的通过调用render方法处理视图,分两种请情况,前面获得的modeAndViews是否指定了视图名称,如果是则通过视图解析器处理比如:BeanNameViewResolver\FreeMarkerViewResolver、InternalResourceViewResolver等,如果不是则从ModeAndView获取到对应的视图,里面有多种视图包括处理json的MappingJackson2JsonView以及模板引擎的FreeMarkerView,也可以自定义视图处理,可以通过实现AbstractView或者想简单扩展json视图可以通过继承AbstractJackson2View来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
less复制代码private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
//异常视图处理
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}

//这里是正常视图处理
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}

if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}

由于篇幅有限,其他一些方法可以通过流程图以及具体源码类去了解

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%