思路
将HandlerMapping进行初始化后,Spring就会根据请求路径找到对应的Controller方法,进行完Data Binding后执行方法处理请求,然后返回ModelAndView.
UML
RequestHandlerMapping
间接实现了HandlerMapping接口,其中比较关键的方法就是org.springframework.web.servlet.HandlerMapping#getHandler
HandlerMapping
public interface HandlerMapping {
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
HandleMapping接口声明了一个方法
getHandler
,返回HandlerExecutionChain
实例.
这里不直接返回Handler实例返回HandlerExecutionChain
主要是因为要经过一些拦截器,最后才给到handler去响应。
实现该接口方法的主要是由抽象类-AbstractHandlerMapping
去实现的.
HandlerExecutionChain
public class HandlerExecutionChain {
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
// handler实例
private final Object handler;
@Nullable
/**
* 拦截器数组
*/
private HandlerInterceptor[] interceptors;
@Nullable
private List interceptorList;
private int interceptorIndex = -1;
}
执行请求的过程-doDispatch
这里我们编写一个简单的controller,然后进行断点debug来看看Spring执行请求的调用链.
由于所有的请求都会经过DispatcherServlet
,通常开发者可以通过xml或者注解来达到这个目的,这里不展开说,有兴趣的可以查阅Spring官方文档或者其他的blog.
- 注解形式
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
@WebServlet("/*")
public class DispatcherServlet extends HttpServlet {
}
- web.xml
org.springframework.web.context.ContextLoaderListener
contextConfigLocation
/WEB-INF/root-context.xml
app1
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
/WEB-INF/app1-context.xml
1
app1
/app1/*
官方文档链接: 点我前往
经过层层的调用,最终会调用到org.springframework.web.servlet.DispatcherServlet#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;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// 判断当前请求对应的handler
// handler中包含了请求url,以及对应的controller
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 根据当前handler获取对应的适配器,adapter用于完成参数解析
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 处理GET、HEAD请求的Last-modified
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;
}
}
// 遍历所有定义的interceptor,执行preHandle方法
// 通常是一些拦截器的操作
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 调用目标Controller的方法
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
的一些关键步骤:
- handler
getHandler
可以看到,关于HanlerExecutionChain
的Bean有RequestMappingHandlerMapping
,我们只需要关注它可以,因为它提供的正是解析@RequestMapping注解
的能力.
RequestMappingHandlerMapping#getHandler
- org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 获取handler的具体逻辑,留给子类实现
Object handler = getHandlerInternal(request);
if (handler == null) {
// 如果获取到的handler为null,则采用默认的handler,即属性defaultHandler
handler = getDefaultHandler();
}
if (handler == null) {
// 如果连defaultHandler也为空,则直接返回空
return null;
}
// Bean name or resolved handler?
// 如果handler是beanName,则需要将beanName转成Bean.
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
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 (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
获取当前请求的路径所对应的Handler.
- org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// lookupPath就是请求路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();
try {
// 根据路径获取Handler,并封装成HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
// 根据HandlerMethod中的bean来实力化Handler,并添加到HanlderMethod里面
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
- org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List matches = new ArrayList<>();
// 通过lookupPath属性查找对应的RequestMappingInfo
List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
// 如果匹配,检查其他属性是否符合,如请求方法,参数,header等
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
// 如果没有匹配到,则遍历所有的处理方法进行通配符匹配
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
// 如果方法有多个匹配,不同的通配符等,则排序选择出最合适的一个
Comparator comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
// 设置request参数
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
// 返回匹配的url的处理的方法
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
this.mappingRegistry.getMappingsByUrl(lookupPath);
这步,正是通过org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register
注册的urlLookup
来进行搜索定位的到当前请求的url所对应的RequestMappingInfo
的.
获取到对应的Controller之后呢,还得继续定位到HandlerMethod.主要是匹配params、headers等参数
- org.springframework.web.servlet.mvc.method.RequestMappingInfo#getMatchingCondition
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
if (methods == null) {
return null;
}
ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
if (params == null) {
return null;
}
HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
if (headers == null) {
return null;
}
ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
if (consumes == null) {
return null;
}
ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
if (produces == null) {
return null;
}
PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
if (patterns == null) {
return null;
}
RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
if (custom == null) {
return null;
}
return new RequestMappingInfo(this.name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}
经过层层筛选,获取到了HandlerMethod,也就是此次请求所对应的Handler.
DispatcherServlet#getHandlerAdapter
获取到请求参数对应的Handler后,Spring MVC还需要对参数进行binding
.这就会用到适配器去执行这个操作.
其中,RequestMappingHandlerAdapter
就是常用的适配器之一,我们这里只分析它。
- org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#supports
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
由于获取的Controller对应的Handler是HandlerMethod的实现类,所以
RequestMappingHandlerAdapter
是支持解析HandlerMethod
的,正如它的javadoc上提示的一样:AbstractHandlerMethodAdapter扩展,支持@RequestMapping注释的HandlerMethods 。
可以通过setCustomArgumentResolvers和setCustomReturnValueHandlers添加对自定义参数和返回值类型的支持,或者重新配置所有参数和返回值类型,使用setArgumentResolvers和setReturnValueHandlers 。
OK,到了这里,我们主要明确的是,我们一般编写的Controller对应的适配器就是RequestMappingHandlerAdapter
.
HandlerExecutionChain#applyPostHandle
前面我们说到,为什么设计出HandlerExecutionChain
这个包装类,其实不仅仅存储了handler实例,还存储了拦截链,简单理解,是在执行方法前添加了勾子,正如我们平时用的Filter一样.
AbstractHandlerMethodAdapter#handle
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
继续step into.
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 {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
执行目标Controller的方法.返回ModelView实例.从方法里可以看到,会先对请求做一些校验,进而执行
invokeHandlerMethod
.
- org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 把handlerMethod封装成ServletInvocableHandlerMethod
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
// 设置参数解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 设置spring mvc请求controller的method返回值处理器
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;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
这里看几个重点:
- 参数解析器实例-
HandlerMethodArgumentResolvers
- 方法返回值处理器实例-
HandlerMethodReturnValueHandlers
- 参数名称发现器-
ParameterNameDiscoverer
.- 将这些对象装进
ServletInvocableHandlerMethod
后,执行方法.
- org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 在这里通过反射调用controller中的method
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 在这里通过返回值处理器进行二次处理
// 比如加了@RequestBody注解,那么在这里对结果进行序列化json再返回
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
- 反射调用controller中的method.
1.1.获取httpRequest中对应的参数.- 通过返回值处理器进行二次处理.比如序列化.
参数解析
- org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取controller方法的参数数组
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
// 创建一个参数数组,保存从request中解析出的方法参数
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
// 给每一个Controller方法实例参数初始化一个参数名称发现器
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 解析并绑定参数的核心逻辑
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
- 获取之前在HandlerMethod实例中保存的参数数组.
- 遍历参数数组,给每一个参数初始化一个参数名称发现器,目的是为了匹配各种的参数声明.
- 解析并绑定参数.
- org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 首先获取参数解析器,这里获取的逻辑是首先从argumentResolverCache缓存中
// 获取该MethodParameter匹配的HandlerMethodArgumentResolver
// 如果为空,遍历初始化定义的26个解析器实例
// 查找匹配的HandlerMethodArgumentResolver,然后添加至argumentResolver缓存中
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
关于参数解析,Spring提供了很多支持,比如支持
@RequestParam
、@PathVariable
等,我们今天来看一个默认的解析器-org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver
- org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
// 解析出参数名
Object resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
// 和请求中的参数名进行对照,获取请求参数对应的string类型的值
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
// 参数类型转换
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
- 获取参数名称,并解析成String类型.
- 根据参数名获取请求中对应的参数. (大致相当于
request.getParameterValues(name);
)- 按参数声明的类型进行转换.
ModelAndView
public class ModelAndView {
/** View instance or view name String. */
/**
* 请求视图路径
*/
@Nullable
private Object view;
/** Model Map. */
/**
* 请求结果集
*/
@Nullable
private ModelMap model;
/** Optional HTTP status for the response. */
@Nullable
private HttpStatus status;
/** Indicates whether or not this instance has been cleared with a call to {@link #clear()}. */
private boolean cleared = false;
}
ModelAndView主要包装view和model,其中,view代表的是请求视图路径,model代表的是请求结果集.
applyDefaultViewName-处理视图
如果指定了视图,那么要对返回结果进行处理,比如是JSP请求响应的String,往往需要加上
.jsp
的后缀.
applyPostHandle-后置处理
后置处理拦截器的激活
processDispatchResult-结果集渲染
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.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
- 如果方法执行产生了异常,那么执行异常包装器的渲染逻辑.
- 如果返回的ModelAndView为空,那么执行
render
.
MVC处理流程
这里还是贴出最经典的一个答案,答案还是自己总结.
摘自《Spring 5核心原理与30个类手写实战_谭勇德》