注:本系列源码分析基于spring 5.2.2.RELEASE,本文的分析基于 annotation 注解方式,gitee仓库链接:gitee.com/funcy/sprin….
本文是springmvc请求执行流程的第二篇文章,在上一篇文章中,我们分析了DispatcherServlet#doDispatch方法,总结出请求执行分为如下步骤:
- 获取对应的
HandlerExecutionChain, 获取的HandlerExecutionChain中包含真正地处理器(Controller中的方法)和一组HandlerInterceptor拦截器; - 获取对应的
handlerAdapter,该对象用来运行handler(xxx)方法; - 执行spring的拦截器, 运行
HandlerInterceptor#preHandle方法; - 处理请求,也就是通过上面获取到的
handlerAdapter来调用handle(xxx)方法; - 执行spring的拦截器,运行
HandlerInterceptor#postHandle方法; - 处理返回结果,这里会渲染视图,以及执行spring拦截器的
HandlerInterceptor#afterCompletion。
书接上回,本文将继续分析接下来的步骤。
8. 视图渲染:DispatcherServlet#processDispatchResult
我们再回到DispatcherServlet#doDispatch:
DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
try {
...
try {
...
// 4.通过上面获取到的handlerAdapter来调用handle
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 5.如果函数调用没有返回视图则使用默认的
applyDefaultViewName(processedRequest, mv);
// 6. 执行拦截器,运行 HandlerInterceptor#postHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (...) {
...
}
// 7. 处理返回结果,在这个方法里会执行 HandlerInterceptor.afterCompletion
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (...) {
...
}
}
执行完拦截器的HandlerInterceptor#postHandle 方法后 ,接着就是激动人心的视图渲染操作了,代码为DispatcherServlet#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) {
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
// 异常视图处理
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
if (mv != null && !mv.wasCleared()) {
// 页面渲染
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
return;
}
if (mappedHandler != null) {
// 执行拦截器方法:HandlerInterceptor.afterCompletion
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
这个方法做了三件事:
- 异常处理:如果前面的执行中出现了异常,在这个方法里会查找项目中配置的处理器进行处理;
- 视图渲染:渲染返回的视图
- 执行拦截器方法:
HandlerInterceptor#afterCompletion
接下来我们逐个分析这几个步骤。
8.1 异常处理
我们进入DispatcherServlet#processHandlerException方法:
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
// 获取异常解析器
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
}
这个方法主要是获取异常解析器,执行resolver.resolveException(...)后,再转化为对应的ModelAndView,然后返回。可见,HandlerExceptionResolver#resolveException才是异常处理的关键,我们来看看这个方法:
public interface HandlerExceptionResolver {
/**
* 具体的异常解析操作
*/
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex);
}
HandlerExceptionResolver是一个接口,内部只有一个方法:resolveException(...),具体的异常处理就在这个方法里实现。经过调试发现,当前的handlerExceptionResolver只有一个:
当前的HandlerExceptionResolver只有HandlerExceptionResolverComposite,再看看HandlerExceptionResolverComposite#resolveException方法:
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, @Nullable Object handler, Exception ex) {
if (this.resolvers != null) {
// 遍历所有的 handlerExceptionResolver
for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
// 这个方法就是异常处理,如果不能处理当前异常,就返回null
ModelAndView mav = handlerExceptionResolver.resolveException(
request, response, handler, ex);
if (mav != null) {
return mav;
}
}
}
return null;
}
这个方法先是遍历所有的 handlerExceptionResolver,逐个执行resolveException(...)方法。经过调试,发现HandlerExceptionResolverComposite的resolvers有三个:
对这三个类说明如下:
ExceptionHandlerExceptionResolver: 自定义异常处理,我们定义的@ExceptionHandler异常处理方法,都是由这个ExceptionResolver来处理,也是我们接下来要重点分析的类;ResponseStatusExceptionResolver: 处理ResponseStatusException的ExceptionResolver;DefaultHandlerExceptionResolver: spring提供的默认的ExceptionResolver,可以处理像HttpRequestMethodNotSupportedException/HttpMediaTypeNotSupportedException/HttpMediaTypeNotAcceptableException的异常;
我们继续,进入handlerExceptionResolver.resolveException(...)方法:
AbstractHandlerExceptionResolver#resolveException
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, @Nullable Object handler, Exception ex) {
// 判断能否使用当前 handler, 一般为true
if (shouldApplyTo(request, handler)) {
prepareResponse(ex, response);
// 具体的异常处理逻辑,下面继续分析
ModelAndView result = doResolveException(request, response, handler, ex);
if (result != null) {
logException(ex, request);
}
return result;
}
else {
return null;
}
}
继续跟进doResolveException(request, response, handler, ex)方法,一直到ExceptionHandlerExceptionResolver#doResolveHandlerMethodException:
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
// 获取exception处理方法
ServletInvocableHandlerMethod exceptionHandlerMethod =
getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
// 设置参数解析器,及返回结果处理器
if (this.argumentResolvers != null) {
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
try {
Throwable cause = exception.getCause();
// 执行处理方法,执行逻辑同 执行 Controller 的方法
if (cause != null) {
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer,
exception, cause, handlerMethod);
}
else {
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
}
}
catch (Throwable invocationEx) {
return null;
}
// 返回 ModelAndView
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
}
else {
ModelMap model = mavContainer.getModel();
HttpStatus status = mavContainer.getStatus();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
mav.setViewName(mavContainer.getViewName());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}
}
这个方法就是用来执行异常处理方法了。一开始,先是获取能处理当前异常的方法,然后设置参数解析器、返回结果处理器,接着就开始执行异常处理方法,执行逻辑同执行Contoller的方法是一样的,最后就是处理返回视图了。
这里有两点需要说明下:
- 获取能处理当前异常的方法:这个是异常处理的最核心部分,关于springmvc是如何找到自定义异常处理方法的,接下来会分析;
- 异常处理方法的执行:这一块的执行逻辑同执行Contoller的方法是一样的,但与执行Contoller方法不同的是,这个方法多传 了两个参数:
handlerMethod/exception,这两个参数会通过参数解析器映射到自定义的异常处理方法上,示例如下:// contoller @RestController @RequestMapping("/test") public class TestController { @RequestMapping("/{name}") @RequestMapping("/{name}") public String hello(@PathVariable("name") String name) { if(true) { throw new RuntimeException("test"); } return "hello " + name + "!"; } } // exceptionHandler @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(RuntimeException.class) public void handler(HandlerMethod handlerMethod, Exception exception) { // 一些处理操作 System.out.println(exception); System.out.println(handlerMethod); return "error"; } }
获取异常处理方法
接下来,我们就来看看springmvc是如何找到Exception对应的处理方法的,跟进ExceptionHandlerExceptionResolver#getExceptionHandlerMethod方法:
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
@Nullable HandlerMethod handlerMethod, Exception exception) {
Class<?> handlerType = null;
if (handlerMethod != null) {
// 获取 handlerMethod 的类型,其实就是Controller的Class
handlerType = handlerMethod.getBeanType();
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
resolver = new ExceptionHandlerMethodResolver(handlerType);
// 保存到缓存
this.exceptionHandlerCache.put(handlerType, resolver);
}
// 在这里获取到异常处理方法的
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
}
if (Proxy.isProxyClass(handlerType)) {
handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
}
}
// 遍历缓存
for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry
: this.exceptionHandlerAdviceCache.entrySet()) {
ControllerAdviceBean advice = entry.getKey();
if (advice.isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
// 在这里获取到异常处理方法的
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
}
}
}
return null;
}
该方法的主要操作注释中已阐明,这个类其实并没有做什么实质性操作,它也是调用Method method = resolver.resolveMethod(exception)来获取异常处理方法的,我们继续跟进去,一直跟到了ExceptionHandlerMethodResolver#getMappedMethod方法:
// 保存异常处理方法的map
// key:该方法所能处理的异常,value:异常处理方法
private final Map<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16);
// 根据异常类型获取异常处理方法
@Nullable
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
List<Class<? extends Throwable>> matches = new ArrayList<>();
// 遍历所有的异常处理方法
for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
// mappedException为exceptionType,或其父类
if (mappedException.isAssignableFrom(exceptionType)) {
matches.add(mappedException);
}
}
// 如果存在多个,则先比较,找到最接近的处理方法,返回
if (!matches.isEmpty()) {
matches.sort(new ExceptionDepthComparator(exceptionType));
return this.mappedMethods.get(matches.get(0));
}
else {
return null;
}
}
可以看到,这个方法才是最终获取异常处理方法的地方,该方法先是遍历了项目中配置的所有异常方法,然后逐一判断所能处理的异常是否为传入的异常,或其父类。这里有个问题:如果在这一步得到了多个异常处理方法,那该如何处理呢?
比如MyException 继承了 RuntimeException,项目中配置了两个异常处理方法,可处理的异常分别为:Exception, RuntimeException,从上面的匹配规则来看,这两个方法都能处理 MyException 异常(因为Exception, RuntimeException都是MyException的父类),但究竟使用哪个呢?这个就是 ExceptionDepthComparator 的比较规则了:
public class ExceptionDepthComparator implements Comparator<Class<? extends Throwable>> {
/**
* 异常比较规则
*/
@Override
public int compare(Class<? extends Throwable> o1, Class<? extends Throwable> o2) {
int depth1 = getDepth(o1, this.targetException, 0);
int depth2 = getDepth(o2, this.targetException, 0);
return (depth1 - depth2);
}
/**
* 获取当前异常与目标异常的深度
*/
private int getDepth(Class<?> declaredException, Class<?> exceptionToMatch, int depth) {
if (exceptionToMatch.equals(declaredException)) {
// Found it!
return depth;
}
// If we've gone as far as we can go and haven't found it...
if (exceptionToMatch == Throwable.class) {
return Integer.MAX_VALUE;
}
return getDepth(declaredException, exceptionToMatch.getSuperclass(), depth + 1);
}
}
从ExceptionDepthComparator规则来看,springmvc定义了一个“异常深度”的概念,优先使用深度小的异常处理方法。
那么,什么是异常深度呢?可以理解为衡量两个异常之前的继承层次标准,比如,MyException继承了RuntimeException那么,MyException 与 RuntimeException的深度为1,与Exception的深度为2(RuntimeException是Exception的子类),与Throwable的深度为Integer.MAX_VALUE.
初始化异常处理方法
经过千辛万苦,我们终于获得了异常处理方法,也知道所有的异常处理方法都保存在ExceptionHandlerMethodResolver的mappedMethods属性中,那么这些异常处理方法是什么时候put进去的呢?
要回答这个问题,需要回顾下前面文章提到的WebMvcConfigurationSupport的功能了,这虽然是个很长的故事,但我们长话短说,简单介绍下springmvc初始化异常方法的流程(其实就是解析@ExceptionHandler注解的流程).
在WebMvcConfigurationSupport中,通过WebMvcConfigurationSupport#handlerExceptionResolver方法上的@Bean注解,向项目中引入了异常处理器,自此开启了异常处理的新世界大门,调用链如下:
// 通过 @Bean 向spring中引入 ExceptionResolver
WebMvcConfigurationSupport#handlerExceptionResolver
// 添加默认的异常处理器
|-WebMvcConfigurationSupport#addDefaultHandlerExceptionResolvers
|-ExceptionHandlerExceptionResolver#afterPropertiesSet
// 在这个方法里,会查找当前项目所有包含 @ControllerAdvice 注解的类
|-ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache
// 在这个方法里,会查找当前类(包含@ControllerAdvice 注解)里所有包含
// @ExceptionHandler注解的方法
|-ExceptionHandlerMethodResolver#ExceptionHandlerMethodResolver
// 将包含 @ExceptionHandler注解的方法 添加到
// ExceptionHandlerMethodResolver的mappedMethods属性中
|-ExceptionHandlerMethodResolver#addExceptionMapping
具体的细节就不分析了,有兴趣的小伙伴可根据以上的调用链自行分析。
8.2 视图渲染
我们再回到DispatcherServlet#processDispatchResult方法,查看异常视图渲染的处理。视图渲染方法为DispatcherServlet#render
protected void render(ModelAndView mv, HttpServletRequest request,
HttpServletResponse response) throws Exception {
Locale locale = (this.localeResolver != null
? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 1.解析视图名
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
// 视图不存在,返回异常,到这里才会判断视图是否存在
throw new ServletException(...);
}
}
else {
view = mv.getView();
if (view == null) {
throw new ServletException(...);
}
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 2.渲染视图
view.render(mv.getModelInternal(), request, response);
}
catch (...) {
...
throw ex;
}
}
这个方法会进行两个操作:
- 解析视图
- 渲染视图
我们逐一来分析这两个操作。
解析视图
解析视图的操作在DispatcherServlet#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) {
// 逐一进行解析,如果不能解析,则返回null
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
嗯,还是一样的套路,遍历所有的视图解析器,然后逐一判断能否解析传入的视图名。
这些视图解析器是在哪里配置的呢?了解springmvc的小伙伴应该对视图解析器 的配置并不陌生,我们可以实现WebMvcConfigurer接口,然后重写WebMvcConfigurer#configureViewResolvers即可,像下面这样:
@Component
public class MvcConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".html");
registry.viewResolver(viewResolver);
}
}
spring内部也为我们内置了一些视图解析器,如下:
像FreeMarker、Thymeleaf等spring都提供解析器。另外,如果这些解析器不满足工作要求,还可以自行扩展,只须实现ViewResolver接口即可。
我们这里来看看InternalResourceViewResolver类的解析流程,进入InternalResourceViewResolver#resolveViewName方法(由于InternalResourceViewResolver是AbstractCachingViewResolver的子类,实际进入的是AbstractCachingViewResolver的resolveViewName方法):
AbstractCachingViewResolver#resolveViewName
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
// 不使用缓存,直接创建
return createView(viewName, locale);
}
else {
// 省略从缓存中获取的操作
...
// 创建视图
view = createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
// 省略保存到缓存中的操作
...
return (view != UNRESOLVED_VIEW ? view : null);
}
}
这个方法比较简单,仅是调用了createView(...)方法来创建视图,让我们进入该方法:
UrlBasedViewResolver#createView
protected View createView(String viewName, Locale locale) throws Exception {
// 从名称判断能否处理当前视图
// 在对象创建时,我们可以通过 setViewNames 方法,设置该解析器所能解析的视图名称,
// 如 'my*', '*Report' 和 '*Repo*' 等。
if (!canHandle(viewName, locale)) {
return null;
}
// 检查视图是否以 redirect: 开头
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);
}
// 执行spring容器的初始化方法
return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);
}
// 检查视图是否以 forward: 开头
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
// 得到对应的跳转链接
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
InternalResourceView view = new InternalResourceView(forwardUrl);
return applyLifecycleMethods(FORWARD_URL_PREFIX, view);
}
return super.createView(viewName, locale);
}
这个方法先是判断是否为redirect或forward跳转,若是,则生成对应的视图,返回;否则调用父类的方法创建视图。
我们跟进super.createView方法,一步步跟进,最终到了UrlBasedViewResolver#buildView方法:
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
Class<?> viewClass = getViewClass();
Assert.state(viewClass != null, "No view class");
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
// 处理视图的url,这里的getPrefix()、getSuffix(),就是我们在配置视图解析器时设置的
view.setUrl(getPrefix() + viewName + getSuffix());
// 设置属性,以下属性都是在可以在配置视图时设置的
view.setAttributesMap(getAttributesMap());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
String requestContextAttribute = getRequestContextAttribute();
if (requestContextAttribute != null) {
view.setRequestContextAttribute(requestContextAttribute);
}
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;
}
可以看到,最终的视图创建就是在这个方法完成的。得到了视图后 ,接下来就是渲染视图了。
渲染视图
得到视图后,接下来就是渲染视图了:
// 渲染视图,view 就是视图解析完成后,最终得到的对象
view.render(mv.getModelInternal(), request, response);
上面的创建视图中,最终得到的视图为InternalResourceView,我们来看看它是如何进行视图渲染的。进入InternalResourceView#render方法:
这个方法其实并没有做什么,继续跟进renderMergedOutputModel方法:
InternalResourceView#renderMergedOutputModel
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
exposeModelAsRequestAttributes(model, request);
exposeHelpers(request);
// 处理渲染前的准备,在这个方法里会验证requst里的uri与要渲染的uri是否相等,若相等则表示出现了循环请求
String dispatcherPath = prepareForRendering(request, response);
// 接下来就是servert的操作了,我们在处理跳转时,也是这么处理的:
// RequestDispatcher dispatcher = request.getRequestDispatcher("/a.jsp");
// dispatcher.forward(request, response);
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!");
}
// jsp 的 include 指令
if (useInclude(request, response)) {
response.setContentType(getContentType());
rd.include(request, response);
}
else {
// 使用forward进行跳转
rd.forward(request, response);
}
}
可以看到,最终的渲染实际就是一个servlet的forward跳转操作,会再一次跳转到springmvc中,这又是另一个视图解析器所做的工作了。
8.3 执行拦截器方法
让我们回到DispatcherServlet#processDispatchResult方法,处理完视图渲染后,就到了执行拦截方法了:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
...
if (mappedHandler != null) {
// 执行拦截器方法:HandlerInterceptor.afterCompletion
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
进入HandlerExecutionChain#triggerAfterCompletion方法:
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response,
@NullableException ex) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
...
}
}
}
}
这个方法的执行同前面拦截方法的执行很类似,先是找到项目中配置的所有interceptor,然后逐个执行triggerAfterCompletion,这里就不多说了。
9. 总结
为了分析springmvc的请求流程,本文花了三篇来介绍,主要分析了handler的获取、handler的执行以及视图解析等内容。
本文原文链接:my.oschina.net/funcy/blog/… ,限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。
本系列的其他文章