接着上一篇。继续来看springMVC中最和我们开发中接近的一部分内容:

DispatcherServlet的逻辑处理

作者写到在DispatcherServlet类中存在doGet、doPost之类的方法,但是在我查看的这个spring版本中,并不是在这个类中,而是在其父类FrameworkServlet中,从FrameworkServlet开始看起!

org.springframework.web.servlet.FrameworkServlet类中:

 @Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { processRequest(request, response);
} @Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { processRequest(request, response);
} // 对于不同的方法,spring并没有做特殊处理,而是统一将程序再一次地引导至processRequest(request, response);中
org.springframework.web.servlet.FrameworkServlet类中
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { // 记录当前时间,用于计算web请求的处理时间
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(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();
} 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");
}
}
} publishRequestHandledEvent(request, response, startTime, failureCause);
}
}

函数中已经开始了对请求的处理,虽然把细节转移到了doService函数中实现,但是我们不难看出处理前后所做的工作
1、为了保证当前线程的LocaleContext、RequestAttributes可以在当前请求后还能恢复,提取当前线程的两个属性
2、根据当前request创建对应的LocaleContext、RequestAttributes并绑定到当前线程上
3、委托给doService方法进一步处理
4、请求处理结束后恢复线程到原始状态
5、请求处理结束后无论成功与否发布事件成功通知

继续看org.springframework.web.servlet.DispatcherServlet.doService(HttpServletRequest, HttpServletResponse)

 @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<>();
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()); 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);
} 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);
}
}
}
}

spring将已经初始化的功能辅助工具变量,比如,localeResolver、themeResolver、themeSource等设置在request属性中,看一下处理的方法:
org.springframework.web.servlet.DispatcherServlet.doDispatch(HttpServletRequest, HttpServletResponse):

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 {
// 如果是MultipartContext类型的request则转换request为MultipartHttpServletRequest类型的
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request); // Determine handler for the current request.
// 根据request信息寻找对应的handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果没有找到handler,则通过response反馈错误信息
noHandlerFound(processedRequest, response);
return;
} // Determine handler adapter for the current request.
// 根据当前的Handler寻找对应的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler.
// 如果当前handler支持last-modified头处理
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;
}
} // 拦截器的preHandler方法的调用
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
} // Actually invoke the handler.
// 真正的激活Handler并返回视图
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) {
return;
} // 视图名称转换应用于需要添加前缀后缀的情况
applyDefaultViewName(processedRequest, mv);
// 应用所有拦截器的postHandle方法
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);
}
}
}
} // doDispatch方法中展示了spring请求处理所涉及的主要逻辑,下面是逻辑处理全过程的详细分析:

1、MultipartContext类型的request处理

对于请求的处理,spring首先考虑的是对于Multipart的处理,如果是MultipartContext类型的request请求,则将request转化成MultipartHttpServletRequest类型

org.springframework.web.servlet.DispatcherServlet.checkMultipart(HttpServletRequest)中:

 protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
"this typically results from an additional MultipartFilter in web.xml");
}
else if (hasMultipartException(request)) {
logger.debug("Multipart resolution previously failed for current request - " +
"skipping re-resolution for undisturbed error rendering");
}
else {
try {
return this.multipartResolver.resolveMultipart(request);
}
catch (MultipartException ex) {
if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
logger.debug("Multipart resolution failed for error dispatch", ex);
// Keep processing error dispatch with regular request handle below
}
else {
throw ex;
}
}
}
}
// If not returned before: return original request.
return request;
}

2、根据request信息查找对应的Handler

在spring中最简单的映射处理器配置如下:

 <bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/userlist.htm">userController</prop>
</props>
</property>
</bean>

在spring加载的过程中,spring会将类型为SimpleUrlHandlerMapping的实例加载到this.handlerMappings中,按照常理推断,根据request提取对应的Handler,无非就是提取当前实例中的userController,但是userController为继承自AbstractController类型实例,与HandlerExecutionChain并无任何关联,看下这一步如何封装的:

 @Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}

getHandler方法的实现是在org.springframework.web.servlet.handler.AbstractHandlerMapping类中:
spring 5.0 的版本和作者的版本不一样,这个逻辑处理是在统一的AbstractHandlerMapping抽象出来的类中,

 @Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 根据request获取对应的Handler
Object handler = getHandlerInternal(request);
if (handler == null) {
// 如果没有找到对应的handler 则使用默认的
handler = getDefaultHandler();
}
if (handler == null) {
// 默认的也没有,则返回null
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 加入拦截器到执行链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}

作者推测此方法提供的功能就是根据URL 找到匹配的Controller,最后通过getHandlerExecutionChain对返回的Handler进行封装,详细分析一下这个方法:

(1)根据request获取对应的Handler
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping类中

 @Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// 截取用于匹配的URL路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 根据路径寻找handler
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if ("/".equals(lookupPath)) {
// 如果请求的路径仅仅是"/",那么使用RootHandler处理
rawHandler = getRootHandler();
}
if (rawHandler == null) {
// 无法使用handler 则使用默认的handler
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
// 根据beanName获取对应的bean
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
// 模板方法
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
if (handler != null && logger.isDebugEnabled()) {
logger.debug("Mapping [" + lookupPath + "] to " + handler);
}
else if (handler == null && logger.isTraceEnabled()) {
logger.trace("No handler mapping found for [" + lookupPath + "]");
}
return handler;
} @Nullable
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// Direct match?
// 直接匹配的情况处理
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
} // Pattern match?
// 通配符匹配的处理
List<String> matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
} String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
if (logger.isDebugEnabled()) {
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
}
bestMatch = matchingPatterns.get(0);
}
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath); // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isDebugEnabled()) {
logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
}
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
} // No handler found...
return null;
} // 根据URL获取对应的Handler的匹配规则代码实现起来虽然很长,但是并不难理解,考虑了直接匹配与通配符两种情况,其中提及的是buildPathExposingHandler,
// 他将Handler封装成HandlerExecutionChain类型 protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) { HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}

在函数中,我们看到了通过将Handler以参数的形式传入,并构建HandlerExecutionChain类型实例,加入了两个拦截器,链处理机制,是spring中非常常用的处理,是AOP中重要组成部分,可以方便的对目标对象进行扩展和拦截。

(2)加入拦截器到执行链
getHandlerExecutionChain函数最主要的目的是将配置中的对应的拦截器加入到执行链中,已保证这些拦截器可以有效的作用于目标对象

 protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}

3、没找到对应的handler的错误处理

每个请求都会对应着一个handler,因为每个请求都会在后台有相应的逻辑对应,而逻辑的实现就是在Handler中,所以一旦遇到没有找到Handler的情况(正常情况下,如果没有URL匹配的Handler,开发人员可以设置默认的Handler来处理请求,但是默认情况也没有设置,就会出现Handler为空的情况),就只能通过response向用户返回错误信息

org.springframework.web.servlet.DispatcherServlet.noHandlerFound(HttpServletRequest, HttpServletResponse):

 protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (pageNotFoundLogger.isWarnEnabled()) {
pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) +
"] in DispatcherServlet with name '" + getServletName() + "'");
}
if (this.throwExceptionIfNoHandlerFound) {
throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
new ServletServerHttpRequest(request).getHeaders());
}
else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}

4、 根据当前的Handler寻找对应的HandlerAdapter

org.springframework.web.servlet.DispatcherServlet.getHandlerAdapter(Object)中:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
} // 通过上面的函数,无非就是遍历所有的适配器来选择合适的适配器并返回它,而某个适配器是否适用于当前的Handler逻辑被封装在具体的适配器中,进一步查看SimpleControllerHandlerAdapter类中的supports方法 @Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
} // 分析到这里的话,SimpleControllerHandlerAdapter就是用来处理普通的web请求的,而且对于springMVC来说,我们会把逻辑封装在Controller的子类中,也就是开发人员自己定义的Controller类

5、缓存处理
在研究spring对缓存处理的功能支持前,我们先了解一个概念last-modified缓存机制
(1)在客户端第一次输入URL时,服务器端会返回内容和状态码200,表示请求成功,同时会添加一个"Last-Modified"的响应头,表示此文件在服务器上的最后更新时间
(2)客户端第二次请求URL时,客户端会向服务端发送请求头"If-Modified-Since",询问服务器该时间之后当前请求是否改变过,如果没有变化,则返回HTTP 304状态码

6、HandlerInterceptor的处理

spring API定义的servlet过滤器可以在servlet处理每个web请求的前后对它进行前置处理和后置处理,此外,有些时候,你可能只想处理由某些springMVC处理程序处理的web请求,
并在这些处理程序返回的模型属性被传递到视图之前,对他们进行一些操作

springMVC允许你通过处理拦截web请求,进行前置处理和后置处理。处理拦截是在spring的web应用程序上下文中配置的,因此他们可以利用各种容器特性。并引用容器中声明的各种bean,
处理拦截是针对特殊的处理程序映射进行注册的,因此,他只拦截通过这些处理程序映射的请求。每个处理拦截都必须实现HandlerInterceptor接口,他包含三个需要实现的回调方法
preHandle、postHandle、afterCompletion,第一个和第二个方法分别是在处理程序处理请求之前和之后被调用的,第二个方法还允许访问返回ModelAndView对象,因此可以在它里面操作模型属性

7、 逻辑处理
对于逻辑处理其实是通过适配器中 转调用Handler并返回视图的,看一下SimpleControllerHandlerAdapter中的实现:

org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter中

 @Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception { return ((Controller) handler).handleRequest(request, response);
} org.springframework.web.servlet.mvc.AbstractController中
@Override
@Nullable
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception { if (HttpMethod.OPTIONS.matches(request.getMethod())) {
response.setHeader("Allow", getAllowHeader());
return null;
} // Delegate to WebContentGenerator for checking and preparing.
checkRequest(request);
prepareResponse(response); // Execute handleRequestInternal in synchronized block if required.
// 如果需要session内的同步执行
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
// 调用客户的逻辑
return handleRequestInternal(request, response);
}
}
} // 调用客户的逻辑
return handleRequestInternal(request, response);
}

8、异常视图的处理

 // processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

 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);
}
} // Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
// 根据视图页面
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
} if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
} if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
} // processHandlerException方法中异常视图的处理
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception { // Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// We might still need view name translation for a plain error model...
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
} throw ex;
}

9、根据视图跳转页面

processDispatchResult方法中有一个方法是实现页面跳转的逻辑的,就是render(mv, request, response);看这个方法的逻辑实现:

org.springframework.web.servlet.DispatcherServlet类中:

 protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale); View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
// 解析视图名称
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
} // Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 页面跳转
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}

(1)解析视图名称
在上文中我们提到DispatcherServlet会根据ModelAndView选择合适的视图来进行渲染,而这一功能就是在resolveViewName中完成的

 protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception { if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
// 我们以org.springframework.web.servlet.view.AbstractCachingViewResolver.resolveViewName(String, Locale)为例子分析ViewResolver逻辑的解析过程, @Override
@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
// 不存在缓存的情况下直接
return createView(viewName, locale);
}
else {
// 直接从缓存中提取
Object cacheKey = getCacheKey(viewName, locale);
View view = this.viewAccessCache.get(cacheKey);
if (view == null) {
synchronized (this.viewCreationCache) {
view = this.viewCreationCache.get(cacheKey);
if (view == null) {
// Ask the subclass to create the View object.
view = createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
if (view != null) {
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
}
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
} // org.springframework.web.servlet.view.UrlBasedViewResolver中重写的createView方法
@Override
protected View createView(String viewName, Locale locale) throws Exception {
// If this resolver is not supposed to handle the given view,
// return null to pass on to the next resolver in the chain.
// 如果当前解析器不支持当前解析器 如viewName为空
if (!canHandle(viewName, locale)) {
return null;
} // Check for special "redirect:" prefix.
// 处理前缀为redirect:x的情况
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
RedirectView view = new RedirectView(redirectUrl,
isRedirectContextRelative(), isRedirectHttp10Compatible());
String[] hosts = getRedirectHosts();
if (hosts != null) {
view.setHosts(hosts);
}
return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);
} // Check for special "forward:" prefix.
// 处理前缀为forward:x的情况
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
return new InternalResourceView(forwardUrl);
} // Else fall back to superclass implementation: calling loadView.
return super.createView(viewName, locale);
} //org.springframework.web.servlet.view.AbstractCachingViewResolver父类中的createView
@Nullable
protected View createView(String viewName, Locale locale) throws Exception {
return loadView(viewName, locale);
} // org.springframework.web.servlet.view.UrlBasedViewResolver子类中重写loadView
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
AbstractUrlBasedView view = buildView(viewName);
View result = applyLifecycleMethods(viewName, view);
return (view.checkResource(locale) ? result : null);
} protected AbstractUrlBasedView buildView(String viewName) throws Exception {
Class<?> viewClass = getViewClass();
Assert.state(viewClass != null, "No view class"); AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
// 添加前缀以及后缀
view.setUrl(getPrefix() + viewName + getSuffix()); String contentType = getContentType();
if (contentType != null) {
// 设置ContentType
view.setContentType(contentType);
} view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap()); Boolean exposePathVariables = getExposePathVariables();
if (exposePathVariables != null) {
view.setExposePathVariables(exposePathVariables);
}
Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
if (exposeContextBeansAsAttributes != null) {
view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
}
String[] exposedContextBeanNames = getExposedContextBeanNames();
if (exposedContextBeanNames != null) {
view.setExposedContextBeanNames(exposedContextBeanNames);
} return view;
}

(2)页面跳转
当viewName解析到对应的view之后,就可以进一步的处理跳转逻辑了

org.springframework.web.servlet.view.AbstractView类重写了render方法

 @Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception { if (logger.isTraceEnabled()) {
logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
" and static attributes " + this.staticAttributes);
} Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
} 就是把一些需要用到的属性放到request中,createMergedOutputModel就是完成这个操作的
protected Map<String, Object> createMergedOutputModel(@Nullable Map<String, ?> model,
HttpServletRequest request, HttpServletResponse response) { @SuppressWarnings("unchecked")
Map<String, Object> pathVars = (this.exposePathVariables ?
(Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null); // Consolidate static and dynamic model attributes.
int size = this.staticAttributes.size();
size += (model != null ? model.size() : 0);
size += (pathVars != null ? pathVars.size() : 0); Map<String, Object> mergedModel = new LinkedHashMap<>(size);
mergedModel.putAll(this.staticAttributes);
if (pathVars != null) {
mergedModel.putAll(pathVars);
}
if (model != null) {
mergedModel.putAll(model);
} // Expose RequestContext?
if (this.requestContextAttribute != null) {
mergedModel.put(this.requestContextAttribute, createRequestContext(request, response, mergedModel));
} return mergedModel;
}

spring源码学习之springMVC(二)的更多相关文章

  1. Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作

    写在前面 上文 Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件主要讲Spring容器创建时通过XmlBeanDefinitionReader读 ...

  2. spring源码学习之springMVC(一)

    个人感觉<Spring技术内幕:深入解析Spring架构与设计原理(第2版)>这本书对spring的解读要优于<Spring源码深度解析(第2版)>这本书的,后者感觉就是再陈述 ...

  3. 【spring源码学习】springMVC之映射,拦截器解析,请求数据注入解析,DispatcherServlet执行过程

    [一]springMVC之url和bean映射原理和源码解析 映射基本过程 (1)springMVC配置映射,需要在xml配置文件中配置<mvc:annotation-driven >  ...

  4. Spring源码学习

    Spring源码学习--ClassPathXmlApplicationContext(一) spring源码学习--FileSystemXmlApplicationContext(二) spring源 ...

  5. Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签

    写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...

  6. spring源码学习之路---IOC初探(二)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...

  7. Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件

    写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...

  8. Spring源码学习笔记9——构造器注入及其循环依赖

    Spring源码学习笔记9--构造器注入及其循环依赖 一丶前言 前面我们分析了spring基于字段的和基于set方法注入的原理,但是没有分析第二常用的注入方式(构造器注入)(第一常用字段注入),并且在 ...

  9. Spring 源码学习笔记10——Spring AOP

    Spring 源码学习笔记10--Spring AOP 参考书籍<Spring技术内幕>Spring AOP的实现章节 书有点老,但是里面一些概念还是总结比较到位 源码基于Spring-a ...

随机推荐

  1. 如何快速合并多个TXT文本内容

    工作中有时候需要合并很多文本内容,例如一些推送清单之类,一个一个打开去复制粘贴的话,少量还行,如果txt文本数据量大(10+M以上)且文件数量多(成百上千),这种方式就显得很低效了.具体要求如下:   ...

  2. (一)phonegap自学---不会java也会写原生app

    移动设备如日中天的今天 微软的表现让我们这些.net程序员无不心寒!!幸好每次乱世出英豪,古有java能夸平台,.net跨语言,今有phonegap跨设备 phonegap是什么 狂点这 http:/ ...

  3. 关于set_multicycle_path的最后总结

    (1) –start/-end决定移动的距离以start_clock/end_clock为单元: (2) 对于-setup选项,移动距离是在默认关系的基础上移动(数值-1): (3) 默认往后, se ...

  4. Apache添加多端口及实现单ip多端口映射的方法

    这篇文章主要介绍了Apache添加多端口及实现单ip多端口映射的方法的相关资料,需要的朋友可以参考下(http://www.0831jl.com) 先给大家说下apache添加多端口的方法,具体步骤如 ...

  5. Ubuntu GitHub操作——使用仓库

    若你想更新github代码 在正式更新github仓库时,可以先 git status 查看一下分支master的状态 1.因为是更新代码,所以不用前面那么多步骤,直接添加所更新的文件到 分支mast ...

  6. Myeclipse 10使用hibernate生成注解(annotation)实体类

    以MySQL数据库为例,请在数据库里面建好对应的表. 1.配置数据库链接 打开Myelipse Database Explorer视图 Window-->Open Perspective--&g ...

  7. Docker系列(十五):Openshift 简介

    1.简单了解openshift相关组件 1.openshift是基于容器技术构建的一个云平台 2.kubernetes是容器编排组件 3.docker是容器引擎驱动组件 4.openshift在Pas ...

  8. 《DSP using MATLAB》Problem 7.35

    代码: %% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ %% Output In ...

  9. An invalid property 'jdbcType ' was found in mapping

    大概2种原因: 1 放进去的类型与字段的类型不匹配 2 比较变态,xml中=两边不能有空格! 错误示例如下:  #{plat,jdbcType = INTEGER}, 去掉空格后: #{plat,jd ...

  10. 01_jQuery初识

    1.为什么要学习jQuery?做同样的事情,jQuery写起来极其简练(write less , do more) 2.jQuery是什么?jQuery相当于Python的第三方模块,原生JS DOM ...