Spring 源码解析13——整合SpringMVC(二)
阅读此篇博客之前,请先阅读上一篇博客的内容:Spring 源码解析12——整合SpringMVC(一)
1、DispatcherServlet 的逻辑处理
根据之前的示例,我们知道在HttpServlet.class中分别提供了相应的服务方法,它们是doDelete()、doGet()、doOptions()、doPost()、doPut() 和 doTrace(),它会根据请求的不同形式将程序引导至对应的函数进行处理。这几个函数中最常用的函数无非就是doGet() 和 doPost(),那么我们就直接查看 DispatcherServlet 中对于这两个函数的逻辑实现(具体实现在FrameworkServlet)。DispatcherServlet.class和FrameworkServlet.class的UML图,如下:

FrameworkServlet.class::doGet()
FrameworkServlet.class::doPost()
@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)函数中
FrameworkServlet.class::processRequest()
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() 函数中实现,但是我们不难看出处理请求前后所做的准备与处理工作。
①、为了保证当前线程的LocaleContext 以及RequestAttributes 可以在当前请求后还能恢复,提取当前线程的两个属性。
②、根据当前 request创建对应的LocaleContext和 RequestAttributes,并绑定到当前线程。
③、委托给 doService() 函数进一步处理。
④、请求处理结束后恢复线程到原始状态。
⑤、请求处理结束后无论成功与否发布事件通知。
继续查看doService() 函数。
DispatcherServlet.class::doService()
@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);
}
}
}
}
我们猜想对请求处理至少应该包括一些诸如寻找 Handler 并页面跳转之类的逻辑处理,但是,在 doService()函数中我们并没有看到想看到的逻辑,相反却同样是一些准备工作,但是这些准备工作却是必不可少的。Spring将已经初始化的功能辅助工具变量,比如localeResolver、themeResolver 等设置在request属性中,而这些属性会在接下来的处理中派上用场。
经过层层的准备工作,终于在 doDispatch()函数中看到了完整的请求处理过程。
DispatcherServlet.class::doDispatch()
DispatcherServlet.class::applyDefaultViewName()
DispatcherServlet.class::triggerAfterCompletion()
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 {
//如果是MultipartContent类型的request则转换request为MultipartHttpServletRequest 类型的request
processedRequest = checkMultipart(request);
//根据request信息寻找对应的Handler
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
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支持1ast-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;
}
}
//拦截器的preHandle()函数调用
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);
}
}
}
}
//视图名称转换应用于需要添加前缀后缀的情况
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}
private void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, Exception ex) throws Exception {
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, ex);
}
throw ex;
}
HandlerExecutionChain.class::applyPreHandle()
HandlerExecutionChain.class::applyPostHandle()
//拦截器的preHandle()函数调用
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;
}
//所有拦截器的postHandle()函数的调用
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
doDispatch() 函数中展示了 Spring 请求处理所涉及的主要逻辑,
1.1、MultipartContent 类型的request 处理
对于请求的处理,Spring 首先考虑的是对于 Multipart 的处理,如果是 MultipartContent 类型的request,则转换request为MultipartHttpServletRequest 类型的 request。
DispatcherServlet.class::checkMultipart()
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;
}
1.2、根据 request信息寻找对应的 Handler
在 Spring 加载的过程中,Spring 会将类型为SimpleUrlHandlerMapping 的实例加载到this.handlerMappings中,按照常理推断,根据request 提取对应的 Handler,无非就是提取当前实例中的 userController,但是userController为继承自AbstractController 类型实例,与 HandlerExecutionChain 并无任何关联,那么这一步是如何封装的呢?
DispatcherServlet.class::getHandler()
@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;
}
在之前的内容我们提过,在系统启动时Spring会将所有的映射类型的bean注册到List<HandlerMapping> handlerMappings 变量中,所以此函数的目的就是遍历所有的 HandlerMapping,并调用其getHandler() 函数进行封装处理。getHandler()函数在AbstractHandlerMapping.class中实现。
AbstractHandlerMapping.class::getHandler()
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//根据request获取对应的handler
Object handler = getHandlerInternal(request);
if (handler == null) {
//如果没有对应request的handler,则使用默认的handler
handler = getDefaultHandler();
}
//如果也没有提供默认的 handler则无法继续处理返回nu11
if (handler == 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;
}
函数中首先会使用 getHandlerInternal() 函数根据request 信息获取对应的 Handler,如果以SimpleUrlHandlerMapping 为例分析,那么我们推断此步骤提供的功能很可能就是根据 URL, 找到匹配的 Controller 并返回,当然如果没有找到对应的 Controller 处理器那么程序会尝试去查找配置中的默认处理器,当然,当查找的 controller为String类型时,那就意味着返回的是配置的bean名称,需要根据 bean 名称査找对应的 bean,最后,还要通过 getHandlerExecutionChain() 函数对返回的 Handler 进行封装,以保证满足返回类型的匹配。下面会详细分析这个过程。
AbstractHandlerMapping.class、HandlerMapping.interface和SimpleUrlHandlerMapping.class的UML关系图,如下所示:

1.2.1、根据 request 查找对应的 Handler
AbstractUrlHandlerMapping.class::getHandlerInternal()
AbstractUrlHandlerMapping.class::lookupHandler()
@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?
//根据beanName获取对应的bean
if (rawHandler instanceof String) {
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类型。
AbstractUrlHandlerMapping.class::buildPathExposingHandler()
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 这样大番周折的目的。链处理机制是Spring中非常常用的处理方式,是AOP中的重要组成部分,可以方便地对目标对象进行扩展及拦截,这是非常优秀的设计。
AbstractHandlerMapping.class、HandlerMapping.interface和AbstractUrlHandlerMapping.class的UML关系图,如下所示:

1.2.2、加入拦截器到执行链
getHandlerExecutionChain() 函数最主要的目的是将配置中的对应拦截器加入到执行链中,以保证这些拦截器可以有效地作用于目标对象。
AbstractHandlerMapping.class::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;
}
1.3、没有找到对应的Handler的错误处理
每个请求都应该对应着一个 Handler,因为每个请求都会在后台有相应的逻辑对应,而逻辑的实现就是在 Handler中,所以一旦遇到没有找到 Handler 的情况(正常情况下如果没有 URL匹配的 Handler,开发人员可以设置默认的 Handler 来处理请求,但是如果默认请求也未设置就会出现 Handler 为空的情况 ),就只能通过 response 向用户返回错误信息。
DispatcherServlet.class::noHandlerFound()
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);
}
}
1.4、根据当前 Handler寻找对应的 HandlerAdapter
在 WebApplicationContext 的初始化过程中我们讨论了 HandlerAdapters 的初始化,了解了在默认情况下普通的 Web 请求会交给 SimpleControllerHandlerAdapter 去处理。下面我们以SimpleControllerHandlerAdapter为例来分析获取适配器的逻辑。
DispatcherServlet.class::getHandlerAdapter()
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.class::supports() 函数。
SimpleControllerHandlerAdapter.class::supports()
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
分析到这里,一切已经明了,SimpleControllerHandlerAdapter.class 就是用于处理普通的 Web请求的,而且对于 SpringMVC 来说,我们会把逻辑封装至Controller的子类中。Controller.interface和其实现类的UML关系图,如下所示:

1.5、缓存处理
在研究 Spring 对缓存处理的功能支持前,我们先了解一个概念:Last-Modifed 缓存机制。
①、在客户端第一次输入 URL 时,服务器端会返回内容和状态码 200,表示请求成功,同时会添加一个“Last-Modified”的响应头,表示此文件在服务器上的最后更新时间,例如“Last-Modified:Wed,14 Mar 2012 10:22:42 GMT”表示最后更新时间为(2012-03-14 10:22)。
②、客户端第二次请求此 URL时,客户端会向服务器发送请求头“If-Modified-Since”,询问服务器该时间之后当前请求内容是否有被修改过,如“If-Modified-Since:Wed,14 Mar 2012 10:22:42 GMT”,如果服务器端的内容没有变化,则自动返回 HTTP304 状态码(只有响应头,内容为空,这样就节省了网络带宽)。
Spring 提供的对 Last-Modified 机制的支持,只需要实现 LastModified 接口,如下所示:
package com.xxx.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import org.springframework.web.servlet.mvc.LastModified;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloWorldLastModifiedCacheController extends AbstractController implements LastModified {
private long lastModified;
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//点击后再次请求当前页面
httpServletResponse.getWriter().write("<a href=''>this</a>");
return null;
}
@Override
public long getLastModified(HttpServletRequest httpServletRequest) {
if (lastModified == 0L) {
//第一次或者逻辑有变化的时候,应该重新返回内容最新修改的时间戳
lastModified = System.currentTimeMillis();
}
return lastModified;
}
}
HelloWorldLastModifiedCacheController.classs 只需要实现LastModifed 接口的 getLastModified()函数,保证当内容发生改变时返回最新的修改时间即可。Spring 判断是否过期,通过判断请求的“If-Modified-Since”是否大于等于当前的 getLastModified 方法的时间戳,如果是,则认为没有修改。上面的controller 与普通的 controller 并无太大差别。
1.6、Handlerinterceptor 的处理(可以用来统计整个请求调用的时长)
Servlet API定义的 servlet 过滤器可以在 servlet处理每个 Web 请求的前后分别对它进行前置处理和后置处理。此外,有些时候,你可能只想处理由某些SpringMVC 处理程序处理的 Web请求,并在这些处理程序返回的模型属性被传递到视图之前,对它们进行一些操作。
SpringMVC 允许你通过处理拦截 Web请求,进行前置处理和后置处理。处理拦截是在Spring 的 Web 应用程序上下文中配置的,因此它们可以利用各种容器特性,并引用容器中声明的任何 bean。处理拦截是针对特殊的处理程序映射进行注册的,因此它只拦截通过这些处理程序映射的请求。每个处理拦截都必须实现Handlerlnterceptor.interface接口,它包含三个需要你实现的回调函数:preHandle()函数、postHandle()函数和 afterCompletion()函数。preHandle()函数和postHandle()函数分别是在处理程序处理请求之前和之后被调用的。postHandle()函数还允许访问返回的ModelAndView对象,因此可以在它里面操作模型属性。afterCompletion()函数是在所有请求处理完成之后被调用的(如视图呈现之后)
1.7、真正的激活handler并返回视图
Spring默认使用 SimpleControllerHandlerAdapter.class 进行处理,我们进入 SimpleControllerHandlerAdapter.class的handle()函数如下:
SimpleControllerHandlerAdapter.class::handle()
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
AbstractController.class::handleRequest()
@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);
}
1.8、异常视图的处理
有时候系统运行过程中出现异常,而我们并不希望就此中断对用户的服务,而是至少告知客户当前系统在处理逻辑的过程中出现了异常,甚至告知他们因为什么原因导致的。Spring中的异常处理机制会帮我们完成这个工作。其实,这里Spring主要的工作就是将逻辑引导至HandlerExceptionResolver.class 的resolveException()函数。
DispatcherServlet.class::processHandlerException()
public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION";
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
@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;
}
1.9、处理页面跳转(根据视图跳转页面)
DispatcherServlet.class::render()
//处理页面跳转
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.9.1、解析视图名称
在上文中我们提到 DispatcherServlet 会根据 ModelAndView 选择合适的视图来进行渲染而这一功能就是在 resolveViewName() 函数中完成的。
DispatcherServlet.class::resolveViewName()
@Nullable
private List<ViewResolver> viewResolvers;
@Nullable
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.InternalResourceViewResolver.class 为例来分析ViewResolver 逻辑的解析过程,其中resolveViewName() 函数的实现是在其父类 AbstractCachingViewResolver.class 中完成的。
AbstractCachingViewResolver.class::resolveViewName()
@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);
}
}
在InternalResourceViewResolver.class的父类UrlBasedViewResolver.class中重写了createView() 函数。
UrlBasedViewResolver.class::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:xx 的情况
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:的情况
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);
}
AbstractCachingViewResolver.class::createView()
@Nullable
protected View createView(String viewName, Locale locale) throws Exception {
return loadView(viewName, locale);
}
UrlBasedViewResolver.class::loadView()
UrlBasedViewResolver.class::buildView()
@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;
}
UrlBasedViewResolver.class、AbstractCachingViewResolver.class、ViewResolver.interface和InternalResourceViewResolver.class的UML关系图,如下所示:

通读以上代码,我们发现对于InternalResourceViewResolver.class 所提供的解析功能主要考虑到了3个方面的处理:
①、基于效率的考虑,提供了缓存的支持。
②、提供了对redirect:xx和forward:xx前缀的支持。
③、添加了前缀及后缀,并向 View中加入了必需的属性设置。
1.9.2、页面跳转
当通过 viewName 解析到对应的 View后,就可以进一步地处理跳转逻辑了。
AbstractView.class::render()
AbstractView.class::abstract renderMergedOutputModel()(最终实现在InternalResourceView.class中)
@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);
}
protected abstract void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
在引导示例中,我们了解到对于 ModelView 的使用,可以将一些属性直接放人其中,然后在页面上直接通过JSTL语法或者原始的request 获取。这是一个很方便也很神奇的功能,但是实现却并不复杂,无非是把我们将要用到的属性放入request中,以便在其他地方可以直接调用,而解析这些属性的工作就是在 createMergedOutputModel() 函数中完成的。
AbstractView.class::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;
}
InternalResourceView.class::renderMergedOutputModel()
@Override
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Expose the model object as request attributes.
//将model中的数据以属性的方式设置到request中
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(request, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
"]: Check that the corresponding file exists within your web application archive!");
}
// If already included or response already committed, perform include, else forward.
if (useInclude(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.include(request, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.forward(request, response);
}
}
AbstractView.class和InternalResourceView.class的UML关系图,如下所示:


浙公网安备 33010602011771号