Spring MVC URL映射 学习(下)
接收到一个新的请求之后,spring就会去根据请求的URL信息选择具体的代码块去执行操作,如图就是接收到一个新的请求调用图,从Tomcat开始直到把请求分发到spring中,最后到了doDispatch方法
image
本篇学习笔记主要就是讲一个新的请求被分发到spring中spring如何处理,至于如何扫描包中的controller,得到URL配置信息可以看Spring MVC URL映射 学习(上),而本篇主要介绍了
- 获取执行链
- 404页面设置
- 获取适配器
- LastModified
- 内容方法调用(invoke)
- 视图渲染
让我们更加清楚的知道spring中一般的方法是如何确定调用的具体方法的,适配不同的模板引擎,达到渲染的地步,再者有时候又是API一般只需要返回json结构的数据,其背后的原理是如何实现的,以及我们在使用过程中如何避免出现的各种问题。
doDispatch 方法
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;
// 初始化设置模板为null
Exception dispatchException = null;
// 初始化异常为null
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 文件上传的相关设置和操作
mappedHandler = getHandler(processedRequest);
// 根据request获取对应的请求执行链
if (mappedHandler == null || mappedHandler.getHandler() == null) {
// 如果没有对应的handler对于,则应该是定义为404,通过noHandlerFound确认
noHandlerFound(processedRequest, response);
return;
}
// 通过handler获得合适的handler适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
// 是get方法或者head方法
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
// 如果handler 是LastModified类,则获取其lastModified值,否则返回-1
// lastModify 是spring添加了缓存机制,当重复请求同样的内容,返回403,而不会返回真正的内容,具体可看下面的LastModified机制这一小节
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 前置的handler预处理,就是获取执行链的拦截器,对请求进行拦截处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
// 如果拦截器拦截成功,返回false,直接结束了
// 当然在这其中拦截器肯定需要特定返回自身的内容到response中,便于展示在页面上
// 不过从页面角度出发并没有非常实质性的拦截器处理,这点存疑?
return;
}
// 真正的调用各自的执行方法,返回ModelAndView后续在invoke这一小节细说
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 如果mv没有包含有效的视图,则从dispatch的viewNameTranslator属性上获取对应的默认视图
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 拦截器的后置处理
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 到这里了可能存在异常,类似的500请求处理就是在这里完成操作的
// 同时包含了数据回填到response中的操作
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);
}
}
}
}
获取执行链
AbstractHandlerMapping 类
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
// 这个handlerMappings就是在扫描URL得到的URL容器信息
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
如图所示就是
image
getHandler 方法
AbstractHandlerMapping 类
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
// 调用子类实现的具体方法,获得执行链对象
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
// 到这里意味着真的找不到可用的执行链对象
return null;
}
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
// 重新生成一个执行链对象,可能会和handler是同一个对象
if (CorsUtils.isCorsRequest(request)) {
// 查看request头部信息是否包含了Origin信息,如果有则判断成功
// cors跨域设置
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
// 获取全局的跨域配置
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
// 结合当前handler和全局配置组合成一个新的跨域配置
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
// 重新生成可用的执行链对象
}
return executionChain;
}
AbstractUrlHandlerMapping类
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 从request中获取到URL信息
Object handler = lookupHandler(lookupPath, request);
// 得到执行链路的对象,一般为HandlerExecutionChain
if (handler == null) {
// 如果没有找到,则匹配一些特殊的请求,尽可能的匹配清楚
Object rawHandler = null;
if ("/".equals(lookupPath)) {
// 根路径的handler
rawHandler = getRootHandler();
}
if (rawHandler == null) {
// 默认路径的handler
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// 根路径或者默认路径中的一个不为null
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = getApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
// 同样的套路得到新的执行链路对象信息
}
}
if (handler != null && logger.isDebugEnabled()) {
logger.debug("Mapping [" + lookupPath + "] to " + handler);
// 打个日志,表示匹配到了相关的bean信息
}
else if (handler == null && logger.isTraceEnabled()) {
logger.trace("No handler mapping found for [" + lookupPath + "]");
}
return handler;
}
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 从hanlerMap中获取该URL对应的controller
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
//
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// 没有完全匹配,采取模糊匹配,正则还是?
// 题外话,python的tornado和Django都是采用的正则匹配的
List<String> matchingPatterns = new ArrayList<String>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
// getPathMatcher() 返回的就是AntPathMatcher对象
// 同样也是按照getPathMatcher的匹配规则去匹配的
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern +"/");
}
}
}
// 总之最后会得到一个list,包含了匹配中的URL信息
String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
Collections.sort(matchingPatterns, patternComparator);
// 模糊匹配的集合不为空,则按照urlPath定义的排序规则去重排序
if (logger.isDebugEnabled()) {
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
}
bestMatch = matchingPatterns.get(0);
// 第一个是最佳的匹配路径
}
if (bestMatch != null) {
// 接下来的一切都是依照这个bestMatch不为null,否则就直接返回null,表示没有合适的匹配handler
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
// 如果容器中不存在该handler
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
// 对最佳的URL路径处理之后,还是不在对应的handler,则提示没有具体的handler信息
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// 包含了对应的handler信息了
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
// 如果是string,则通过spring的容器获取对应的bean(一般是controller)
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
//
Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
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);
}
// 生成执行链路对象HandlerExecutionChain
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// 什么都没有发现,返回null
return null;
}
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, Map<String, String> uriTemplateVariables) {
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
// 创建执行链路的对象chain
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
// 添加PathExposingHandlerInterceptor拦截器
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}
如图就是调用buildPathExposingHandler之后返回的执行链对象chain
image
404状态码
在没找到合适的handler执行链的时候,就会进入该方法(其实就是404了)
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 {
// HttpServletResponse.SC_NOT_FOUND 就是404
// 调用的是Tomcat本身的404设置页面返回的,可以在web.xml 中配置自定义的404页面
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
如图就是采用了jdk14Logger打印出来的数据,当然了现实项目中更多的应用log4j、log4j2
image
获取适配器
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
// handlerAdapters的值就是在initStrategies()方法中获取到的
// 默认的适配器是三个
// org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter
// org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
// org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
// 如果细看适配器的初始化过程会发现,spring会优先获取用户自定义的适配器,如果没有才会默认使用这三个适配器
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的适配器,则提示没有适配器
}
接下来看看三种适配器到底是如何判断对象是符合自身的要求的
HttpRequestHandlerAdapter
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
// 是否是HttpRequestHandler对象
}
SimpleControllerHandlerAdapter
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
// 是否是Controller对象
}
AnnotationMethodHandlerAdapter
@Override
public boolean supports(Object handler) {
return getMethodResolver(handler).hasHandlerMethods();
// 获取到方法处理器,再判断其是否为空
}
private ServletHandlerMethodResolver getMethodResolver(Object handler) {
Class<?> handlerClass = ClassUtils.getUserClass(handler);
ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
if (resolver == null) {
synchronized (this.methodResolverCache) {
resolver = this.methodResolverCache.get(handlerClass);
if (resolver == null) {
resolver = new ServletHandlerMethodResolver(handlerClass);
this.methodResolverCache.put(handlerClass, resolver);
}
}
}
return resolver;
}
LastModified 机制
组合成为一个ServletWebRequest对象后就调用该方法,确认lastModified
public boolean checkNotModified(String etag, long lastModifiedTimestamp) {
HttpServletResponse response = getResponse();
if (this.notModified || !isStatusOK(response)) {
// 不需要modified机制或者 response为null或者无效更或者状态码不是200
return this.notModified;
}
if (validateIfUnmodifiedSince(lastModifiedTimestamp)) {
// 其对象会存储该请求的时间,验证是否符合lastModified的条件
if (this.notModified) {
response.setStatus(HttpStatus.PRECONDITION_FAILED.value());
}
return this.notModified;
}
boolean validated = validateIfNoneMatch(etag);
// 默认传过来的etag是null,返回false
if (!validated) {
validateIfModifiedSince(lastModifiedTimestamp);
// 看是否是第一次来,如果是就需要刷新其时间
}
boolean isHttpGetOrHead = SAFE_METHODS.contains(getRequest().getMethod());
// 是否为get或者head请求,并且变更response的头信息
if (this.notModified) {
response.setStatus(isHttpGetOrHead ?
HttpStatus.NOT_MODIFIED.value() : HttpStatus.PRECONDITION_FAILED.value());
}
if (isHttpGetOrHead) {
if(lastModifiedTimestamp > 0 && isHeaderAbsent(response, LAST_MODIFIED)) {
response.setDateHeader(LAST_MODIFIED, lastModifiedTimestamp);
}
if (StringUtils.hasLength(etag) && isHeaderAbsent(response, ETAG)) {
response.setHeader(ETAG, padEtagIfNecessary(etag));
}
}
// 如果符合其机制,就返回true,否则返回false
return this.notModified;
}
invoke调用
只是取了这么个名字,在获取到了合适的执行链,有了合适的适配器,有没有被拦截器处理,最后来到了真正调用方法的地方了。
上文可知,这是由各自的适配器调用handler方法来获取真正的内容了。
在没看源码前,其实我们应该能够猜到一些细节,例如
- 上面说的bean的name是\开头的被SimpleControllerHandlerAdapter命中,可是在该方法中并没有明确定义执行的方法,所以controller接口必须有一些方法要被实现,然后由适配器调用实现类达到获取内容的目的
- 普通的加了requestMapping被AnnotationMethodHandlerAdapter命中,则应该是通过反射获取所有的可行的方法,然后依次筛选,直到匹配上了合适的方法,再invoke调用返回其内容
SimpleControllerHandlerAdapter
HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter是一样的,就只解释一种了。
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
// 果然不出我们期望的,就是controller接口实现的方法handleRequest去完成真正的调用
// 如下面的demo,就是调用该方法,不过此方法意味着一个类只有一个URL映射
// 不像注解那样一个类中可以包含多种URL映射
}
public class NameController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView modelAndView=new ModelAndView();
modelAndView.setViewName("/page/404");
return modelAndView;
}
}
AnnotationMethodHandlerAdapter
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
Class<?> clazz = ClassUtils.getUserClass(handler);
Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
if (annotatedWithSessionAttributes == null) {
annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
}
if (annotatedWithSessionAttributes) {
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
}
else {
checkAndPrepare(request, response, true);
}
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandlerMethod(request, response, handler);
}
}
}
// 前面各种操作来到了inVokeMethod
return invokeHandlerMethod(request, response, handler);
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
// 处理request,得到对应的需要被执行的方法
// 在这个函数中还有类URL注解信息和方法注解信息的拼接匹配过程
// 返回的对象就是一个Method对象
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ExtendedModelMap implicitModel = new BindingAwareModelMap();
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
// 这个是真正的调用过程,其中还包含了请求参数的处理等各种过程
// 返回的结果可以是字符串也可以是mv,看具体的方法是如何实现的
ModelAndView mav =
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
// 获取具体的视图类,如果是具体业务返回,视图为null,具体的数据已经通过response的body回填返回了
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
// 视图属性更新
return mav;
}
image
public ModelAndView getModelAndView(Method handlerMethod, Class<?> handlerType, Object returnValue,
ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {
ResponseStatus responseStatus = AnnotatedElementUtils.findMergedAnnotation(handlerMethod, ResponseStatus.class);
// 查看该方法是否加了ResponseStatus注解,主要是为了替换状态码
if (responseStatus != null) {
HttpStatus statusCode = responseStatus.code();
String reason = responseStatus.reason();
// 该执行方法存在该ResponseStatus的注解,获取状态码和原因
// 不过该具体使用的时候,只能是HttpStatus枚举类中的选项,默认是500错误
if (!StringUtils.hasText(reason)) {
// 没有具体原因,就只设置状态码,否则就返回错误码
webRequest.getResponse().setStatus(statusCode.value());
}
else {
webRequest.getResponse().sendError(statusCode.value(), reason);
}
// 设置当前的请求属性,名字为模板和状态,值就是状态码
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, statusCode);
this.responseArgumentUsed = true;
}
// 如果存在自定义的ModelAndView解析
if (customModelAndViewResolvers != null) {
for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {
ModelAndView mav = mavResolver.resolveModelAndView(
handlerMethod, handlerType, returnValue, implicitModel, webRequest);
// 遍历自定义ModelAndView解析器,选择合适的mv,并且不是初始化的视图类
// 这个是由用户自定义的实现类,并且切记记得适配器的设置,别没弄好导致无合适的适配器的错误
// ModelAndView UNRESOLVED = new ModelAndView();
if (mav != ModelAndViewResolver.UNRESOLVED) {
return mav;
}
}
}
// 返回的类型是HttpEntity、string、ModelAndView、Model、View、Map
if (returnValue instanceof HttpEntity) {
// 返回值是HttpEntity类型的数据,不需要合适的视图
// 更多的是返回json数据
handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest);
return null;
}
else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
// 方法包含了ResponseBody注解,直接把returnValue 塞入response
handleResponseBody(returnValue, webRequest);
return null;
}
else if (returnValue instanceof ModelAndView) {
ModelAndView mav = (ModelAndView) returnValue;
mav.getModelMap().mergeAttributes(implicitModel);
return mav;
}
else if (returnValue instanceof Model) {
return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
}
else if (returnValue instanceof View) {
return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
}
else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
return new ModelAndView().addAllObjects(implicitModel);
}
else if (returnValue instanceof Map) {
return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map<String, ?>) returnValue);
}
else if (returnValue instanceof String) {
return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
}
else if (returnValue == null) {
// Either returned null or was 'void' return.
if (this.responseArgumentUsed || webRequest.isNotModified()) {
return null;
}
else {
// Assuming view name translation...
return new ModelAndView().addAllObjects(implicitModel);
}
}
else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
// Assume a single model attribute...
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
return new ModelAndView().addAllObjects(implicitModel);
}
else {
throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
}
}
在这其中的代码快customModelAndViewResolvers,更多的细节可以看看Spring Controller层记录日志配置的实践demo
视图渲染
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, 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);
// 异常处理返回的mv,异常mv配置存放在handlerExceptionResolvers
errorView = (mv != null);
}
}
// 有可用的mv
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
// render就是渲染,回填数据到response中去
if (errorView) {
// 如果有非ModelAndViewDefiningException,则清除请求的属性信息
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
// 没有可用的视图信息
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
渲染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.resolveLocale(request);
response.setLocale(locale);
View view;
if (mv.isReference()) {
// mv的视图名字是string类型,则需要找到真正的视图
// 需要使用到local字段信息
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
// 没有找到有效的视图,报错提示
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// 否则就被认为是View对象,直接获取就可以了
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) {
// 从mv中获取http状态码
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;
}
}
其中的resolve
View 渲染
View本身是一个接口类,spring根据不同的业务实现了多个不同的View实体类,包含了html、pdf、Excel等。
AbstractView 抽象视图类
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
由各自的子类去实现renderMergedOutputModel方法
image