文章目录
- 引文
- Handler
- HandlerMapper
- HandlerAdapter
- @RequestMapping方法参数解析
- @RequestMapping方法返回值解析
- 文件上传流程
- 拦截器解析
SpringMVC启动流程如下
引文
我们在使用SpringMVC时,传统的方式是在webapp目录下定义一个web.xml文件,比如:
<web-app><servlet><servlet-name>app</servlet-name><servletclass>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>app</servlet-name><url-pattern>/app/*</url-pattern></servlet-mapping>
</web-app>
SpringMVC 的启动过程如下所示:
-
启动Tomcat
-
Tomcat读取到web.xml文件,创建
DispatcherServlet
对象,因为它的load-on-startup
配置为1,表示tomcat启动时创建 -
调用
DispatcherServlet
对象的init()
方法,因为说到底DispatcherServlet
它还是一个Servlet,还是遵守Servlet的生命周期的。在
init()
方法中会创建一个Spring容器,并且添加一个ContextRefreshListener监听器,该监听器会监听ContextRefreshedEvent事件(Spring容器创建完成就会发布这个事件)。也就是说spring容器启动完成后就会执行ContextRefreshListener中的onApplicationEvent事件,从而最终会执行到DespatcherServlet中的initStrategies(),这个方法会初始化更多内容:protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);// 其中比较重要的就是初始化 HandlerMapper和 HandlerAdapterinitHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context); }
我们现在就注重讲讲HandlerMapper和 HandlerAdapter
Handler
什么是Handler?它其实表示请求处理器,在SpringMVC中有四种Handler:
- 实现了Controller接口的Bean对象
- 实现了HttpRequestHandler接口的Bean对象
- 添加了@RequestMapper注解的方法
- 一个HandlerFunction对象
详情如下:
实现了Controller接口的Bean对象,这里Bean的名字必须是要以/
开头 不然不知道根据什么路径来映射到此方法
@Component("/test")
public class ZhouyuBeanNameController implements Controller {@Overridepublic ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {System.out.println("hushang");return new ModelAndView();}}
实现了HttpRequestHandler接口的Bean对象
@Component("/test")public class ZhouyuBeanNameController implements HttpRequestHandler {@Overridepublic void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("hushang");}
}
添加了@RequestMapping注解的方法
@RequestMapping
@Component
public class ZhouyuController {@RequestMapping(method = RequestMethod.GET, path = "/test")@ResponseBodypublic String test(String username) {return "hushang";}
}
一个HandlerFunction对象(以下代码中有两个):
@ComponentScan("com.hushang")
@Configuration
public class AppConfig {@Beanpublic RouterFunction<ServerResponse> person() {return route().GET("/app/person", request -> ServerResponse.status(HttpStatus.OK).body("Hello GET")).POST("/app/person", request -> ServerResponse.status(HttpStatus.OK).body("Hello POST")).build();}
}
HandlerMapper
HandlerMapper就是根据请求路径path去找到Handler,保存的就是路径和Handler之间的映射关系,可以理解为一个Map<path, Handler>
因为Handler有四种,所以SpringMVC中也有不同的HandlerMapper去查找不同的Handler。在SpringMVC中有一个DespatcherServlet.properties
文件中有保存,SpringMVC会读取此文件,将其中的HandlerMapper都取出来并遍历,再通过createBean()
方法进行创建化各个HandlerMapper,因为是bean,所以在创建过程中会经过BeanPostProcessor去找各个负责的Handler
- BeanNameUrlHandlerMapping:负责Controller接口和HttpRequestHandler接口
- RequestMappingHandlerMapping:负责@RequestMapper注解的方法
- RouterFunctionMapping:负责RouterFunction对象
这些HandlerMapper是Bean对象,所以也有Bean的生命周期,RequestMappingHandlerMapping
是在afterPropertiesSet()
方法中去找Handler的。
BeanNameUrlHandlerMapping的寻找流程:
-
找出Spring容器中所有的BeanName
-
判断BeanName是不是以 / 开头
-
如果是,则把它当成一个Handler,并把beanName作为Key,Bean对象作为Value存入HandlerMapper中
-
HandlerMapper就是一个Map
RequestMappingHandlerMapping的寻找流程:
-
找出Spring容器中所有的BeanType
-
判断BeanType是否有@Controller注解或@RequestMapping注解
-
判断成功则继续找加@RequestMapping注解的method
-
并解析@RequestMapping注解中的内容,比如method、path封装为一个RequestMappingInfo对象
-
最后把RequestMappingInfo对象作为Key,Method对象封装为HandlerMapper对象后作为value,存入registry中
先通过path找到RequestMappingInfo对象,进行注解一些信息的匹配,比如请求方式是否满足,在通过RequestMappingInfo作为key再去拿到具体要执行的Method
-
registry就是一个Map
RouterFunctionMapping的寻找流程会有些区别,但是大体是差不多的,相当于是一个path对应一个 HandlerFunction。
各个HandlerMapping除开负责寻找Handler并记录映射关系之外,自然还需要根据请求路径找到对应的Handler,在源码中这三个HandlerMapping有一个共同的父类AbstractHandlerMapping
AbstractHandlerMapping
实现了HandlerMapping
接口,并实现了getHandler(HttpServletRequest request)
方法。
AbstractHandlerMapping
会负责调用子类的getHandlerInternal(HttpServletRequest request)
方法从而找到请求对应的Handler,然后AbstractHandlerMapping
负责将Handler
和应用中所配置的 HandlerInterceptor
整合成为一个HandlerExecutionChain
对象。
所以寻找Handler的源码实现在各个HandlerMapping子类中的getHandlerInternal()中,根据请求路径找到Handler的过程并不复杂,因为路径和Handler的映射关系已经存在Map中了。
比较困难的点在于,当DispatcherServlet接收到一个请求时,该利用哪个HandlerMapping来寻找 Handler呢?看源码:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;
}
从源码中可以看出来,就是遍历,三个HandlerMapper一个一个的遍历 ,通过request对象找到了就返回
默认遍历的顺序是
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\org.springframework.web.servlet.function.support.RouterFunctionMapping
所以BeanNameUrlHandlerMapping的优先级最高,所以如果一个实现了Controller接口的Bean和@RequestMapping注解修饰的方法,他们两个的path都是/test,但是最终是Controller接口的会生效。
至此,就通过path找到了Handler,接下来就是要去执行相应的Handler了
HandlerAdapter
找到了Handler之后,接下来就该去执行了,比如执行下面这个test()
@RequestMapping(method = RequestMethod.GET, path = "/test")
@ResponseBody
public String test(String username) {return "hushang";
}
入口是在DispatchServlet类的doService()方法,再调用doDispatch(request, response)
方法。
目前有四种Handler,各个Handler的执行方式也是不一样的,如下所示
- 实现了Controller接口的Bean对象,执行的是Bean对象中的handleRequest()
- 实现了HttpRequestHandler接口的Bean对象,执行的是Bean对象中的handleRequest()
- 添加了@RequestMapping注解的方法,具体为一个HandlerMethod,执行的就是当前加了注解的方法
- 一个HandlerFunction对象,执行的是HandlerFunction对象中的handle()
按照我们现在的想法,处理的方式可能是如下所示
Object handler = mappedHandler.getHandler();
if (handler instanceof Controller) {((Controller)handler).handleRequest(request, response);
} else if (handler instanceof HttpRequestHandler) {((HttpRequestHandler)handler).handleRequest(request, response);
} else if (handler instanceof HandlerMethod) {((HandlerMethod)handler).getMethod().invoke(...);
} else if (handler instanceof HandlerFunction) {((HandlerFunction)handler).handle(...);
}
但是为了扩展性,SpringMVC是采用的适配模式,把不同的Handler适配成一个HandlerAdapter,后续再去执行HandlerAdapter的handle()方法,这样就执行不同种类的Handler对应的方法了
在DespatchServlet.properties文件中也有配置,针对不同的Handler也有不同的HandlerAdapter
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\org.springframework.web.servlet.function.support.HandlerFunctionAdapter
具体逻辑是
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {// 这里的this.handlerAdapters就是上面properties文件中配置的四种HandlerAdapterif (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) {// 这里会调用各种HandlerAdapter的supports()方法,如下所示,其实就是进行类型的判断 如果满足就返回true 这里就返回当前适配器if (adapter.supports(handler)) {return adapter;}}}throw new ServletException("如果一个HandlerAdapter都没有匹配上就会抛异常......");
}// 就拿SimpleControllerHandlerAdapter来举例,就是判断当前Handler是否实现了Controller接口,我们最常用的RequestMappingHandlerAdapter它的supports()方法在它的父类中
public boolean supports(Object handler) {return (handler instanceof Controller);
}
根据Handler适配出了对应的HandlerAdapter后,就执行具体HandlerAdapter对象的handle()方法 了,因为这四种最终都是实现了HandlerAdapter接口,所以这里也就是直接调用各种HandlerAdapter对象的handler()方法
比如SimpleControllerHandlerAdapter,就是强制转换后直接执行方法
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return ((Controller) handler).handleRequest(request, response);
}
其他两个HandlerAdapter也一样很简单,逻辑比较复杂的就是RequestMappingHandlerAdapter
中的handler()方法,它执行的是加了@RequestMapping的方法,而这种方法的写法可以是多种多样,SpringMVC需要根据方法的定义去解析Request对象,从请求中获取出对应的数据然后传递给方法,并执行。
现在的这几种HandlerAdapter也是Bean对象,就比如RequestMappingHandlerAdapter
,在创建它的时候也会去调用InitializingBean.afterPropertiesSet()
方法去创建各种方法参数解析器(HandlerMethodArgumentResolver)和返回值解析器(HandlerMethodReturnValueHandler)
@Override
public void afterPropertiesSet() {// Do this first, it may add ResponseBody advice beansinitControllerAdviceCache();// 参数解析器if (this.argumentResolvers == null) {// getDefaultArgumentResolvers()就会去创建很多的方法参数解析器List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}// 数据绑定器参数解析器if (this.initBinderArgumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}// 返回值解析器if (this.returnValueHandlers == null) {// getDefaultReturnValueHandlers()就会去创建很多的返回值解析器List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}
}
@RequestMapping方法参数解析
当SpringMVC接收到请求,并通过HandlerMapper找到对应的Method后,就该执行该方法了,不过在执行之前需要根据方法定义的形参,从请求中获取到对应的值,然后将数据传递给方法并执行。
一个HttpServletRequest通常有:
- request parameter
- reqeust attribute
- reqest session
- request header
- request body
- …
比如下面几个方法
// 对应的前端url中传的值
// 表示要从request parameter中获取key为username的值
public String test(String username) {return "hushang";
}// 表示要从request parameter中获取key为uname的value
public String test(@RequestParam("uname") String username) {return "hushang";
}// 对应的是后端拦截器中自己的处理,对处理后的数据 用httpServletRequest.setAttribute(name, value)存
// 表示要从request attribute中获取key为username的value
public String test(@RequestAttribute String username) {return "hushang";
}// 表示要从request session中获取key为username的value
public String test(@SessionAttribute String username) {return "hushang";
}// 表示要从request header中获取key为username的value
public String test(@RequestHeader String username) {return "hushang";
}// 表示获取整个请求体
public String test(@RequestBody String username) {return "hushang";
}
SpringMVC在解析方法参数时,就需要看参数到底是获取请求的哪些数据,源码中是通过HandlerMethodArgumentResolver
来实现的,比如:
- RequestParamMethodArgumentResolver:负责处理@RequestParam
- RequestHeaderMethodArgumentResolver:负责处理@RequestHeader
- SessionAttributeMethodArgumentResolver:负责处理@SessionAttribute
- RequestAttributeMethodArgumentResolver:负责处理@RequestAttribute
- RequestResponseBodyMethodProcessor:负责处理@RequestBody
- 还有很多其他的…
在判断到底需要由哪一个HandlerMethodArgumentResolver
来处理时,源码中就是直接遍历,然后分别调用他们的supportsParameter()
方法判断是否支持
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {// 循环遍历ArgumentResolverfor (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {// 判断各个ArgumentResolver是否支持解析当前参数,如果支持就break 并返回if (methodArgumentResolver.supportsParameter(parameter)) {result = methodArgumentResolver;this.argumentResolverCache.put(parameter, result);break;}}}return result;
}
当这里找到HandlerMethodArgumentResolver
并返回之后,就会调用他们的resolveArgument()
方法真正去获取值
@RequestMapping方法返回值解析
而方法的返回值也有不同的情况,比如返回值是String,如果加了@ResponseBody注解则将String返回给浏览器,如果没有加此注解则根据这个String找到对应的页面返回。
在SpringMVC中,是通过HandlerMethodReturnValueHandler
来处理返回值的
- RequestResponseBodyMethodProcessor:处理加了@ResponseBody注解的情况
- ViewNameMethodReturnValueHandler:处理没有加@ResponseBody注解并且返回值类型为String的情况
- ModelMethodProcessor:处理返回值是Model类型的情况
- 还有很多其他的…
这里就着重介绍RequestResponseBodyMethodProcessor
,因为它处理的是加了@ResponseBody注解的情况,也是我们用的最多的情况。
我们如果返回String那还好,直接返回给浏览器,如果返回的是Map或者是Object这种复杂对象该如何处理再返回给浏览器嘞?
处理这块,SpringMVC会利用HttpMessageConverter来处理,比如默认情况下,SpringMVC会有4个HttpMessageConverter:
- ByteArrayHttpMessageConverter:处理返回值为字节数组的情况,把字节数组返回给浏览器
- StringHttpMessageConverter:处理返回值为字符串的情况,把字符串按指定的编码序列号后返回给浏览器
- SourceHttpMessageConverter:处理返回值为XML对象的情况,比如把DOMSource对象返回给浏览器
- AllEncompassingFormHttpMessageConverter:处理返回值为MultiValueMap对象的情况
StringHttpMessageConverter的源码也比较简单:
protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {HttpHeaders headers = outputMessage.getHeaders();if (this.writeAcceptCharset && headers.get(HttpHeaders.ACCEPT_CHARSET) == null) {headers.setAcceptCharset(getAcceptedCharsets());}Charset charset = getContentTypeCharset(headers.getContentType());StreamUtils.copy(str, charset, outputMessage.getBody());
}
先看有没有设置Content-Type,如果没有设置则取默认的,默认为ISO-8859-1,所以默认情况下返 回中文会乱码,可以通过以下来中方式来解决:
@RequestMapping(method = RequestMethod.GET, path = "/test", produces = {"application/json;charset=UTF-8"})
......
@ComponentScan("com.hushang")
@Configuration
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {StringHttpMessageConverter messageConverter = new StringHttpMessageConverter();messageConverter.setDefaultCharset(StandardCharsets.UTF_8);converters.add(messageConverter);}
}
不过以上四个Converter是不能处理Map对象或User对象的,所以如果返回的是Map或User对象,那么得单独配置一个Converter,比如MappingJackson2HttpMessageConverter
,这个Converter比较强大,能把String、Map、User对象等等都能转化成JSON格式。
@ComponentScan("com.hushang")
@Configuration
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();messageConverter.setDefaultCharset(StandardCharsets.UTF_8);converters.add(messageConverter);}
}
文件上传流程
我们要在SpringMvc中使用文件上传,刚开始我们会配置一个bean
默认的multipartResolver是StandardServletMultipartResolver,我这里使用CommonsMultipartResolver
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
入口是DispatcherServlet.doDispatch()
,请求刚开始就是经过multipartResolver去判断请求参数是否是文件上传multipart类型,如果是则将所有的文件类型form表单对应的文件part保存至一个Map中
接下来等到RequestParamMethodArgumentResolver这个参数解析器去解析,根据controller方法中的参数名去从这个Map中取
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {...try {// 请求刚开始就检查请求参数是否有Multipart文件上传对象processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 进行映射mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 找到最合适的HandlerAdapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());...// Actually invoke the handler.// 具体执行handlemv = ha.handle(processedRequest, response, mappedHandler.getHandler());...}
}
接下来是checkMultipart()
方法
// 我们自己配置了一个multipartResolver的bean,各个具体的子类有各自的实现逻辑,这里就拿StandardServletMultipartResolver举例
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {// 判断是不是文件上传请求if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {if ...else {try {// 去解析文件上传请求return this.multipartResolver.resolveMultipart(request);}catch (MultipartException ex) {...}}}// If not returned before: return original request.return request;
}
// new一个StandardMultipartHttpServletRequest对象返回,注意这个类型的HttpServletRequest在下面的代码中会出现
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}// 在创建StandardMultipartHttpServletRequest对象时 构造方法中就会调用下面的方法
private void parseRequest(HttpServletRequest request) {try {Collection<Part> parts = request.getParts();this.multipartParameterNames = new LinkedHashSet<>(parts.size());// 保存结果的MapMultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size());// 遍历表单的每一个part,也就是form表单的每一行请求参数for (Part part : parts) {String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION);ContentDisposition disposition = ContentDisposition.parse(headerValue);String filename = disposition.getFilename();// 如果part是文件,那么就会有filename,文本类型就没有if (filename != null) {if (filename.startsWith("=?") && filename.endsWith("?=")) {filename = MimeDelegate.decode(filename);}// 文件类型的part添加进行集合files.add(part.getName(), new StandardMultipartFile(part, filename));}else {this.multipartParameterNames.add(part.getName());}}// 将找出来的这个map传给父类中的multipartFiles这个属性// 然后就轮到了RequestParamMethodArgumentResolver这个参数解析器去解析,从这个Map中取setMultipartFiles(files);}catch (Throwable ex) {handleParseFailure(ex);}
}
接下来等到RequestParamMethodArgumentResolver这个参数解析器去解析,根据controller方法中的参数名去从这个Map中取
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);if (servletRequest != null) {// 这里对文件类型的参数进行处理,去上面存入Map中找对应的文件partObject mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {return mpArg;}}Object arg = null;MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);if (multipartRequest != null) {List<MultipartFile> files = multipartRequest.getFiles(name);if (!files.isEmpty()) {arg = (files.size() == 1 ? files.get(0) : files);}}if (arg == null) {// *解析参数值 : request.getParameter方式String[] paramValues = request.getParameterValues(name);if (paramValues != null) {arg = (paramValues.length == 1 ? paramValues[0] : paramValues);}}return arg;
}
public static Object resolveMultipartArgument(String name, MethodParameter parameter, HttpServletRequest request)throws Exception {MultipartHttpServletRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);boolean isMultipart = (multipartRequest != null || isMultipartContent(request));// 如果参数类型是MultipartFileif (MultipartFile.class == parameter.getNestedParameterType()) {if (!isMultipart) {return null;}if (multipartRequest == null) {multipartRequest = new StandardMultipartHttpServletRequest(request);}// 文件上传请求刚进来时就经过文件上传类型判断,将所有的上传文件form表单中的part都存入了一个Map中// 根据方法参数中的name,去Map中找return multipartRequest.getFile(name);}......
}
拦截器解析
拦截器的具体实现是我们自定义一个类,实现下面的接口,
package org.springframework.web.servlet;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;public interface HandlerInterceptor {// 在执行handle之前执行,如果返回了false则表示当前请求被拦截了,不会执行后续的方法了// 即使该方法返回了false,最下面的afterCompletion()方法也会执行default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return true;}// 如果上面的preHandle()方法返回了false,或者是handle执行除了异常,该方法都不会执行// 在handle正常执行结束后执行该方法default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable ModelAndView modelAndView) throws Exception {}// 一次请求完成后,最终都会调用该方法default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception {}}
底层实现,入口还是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 {// 检查请求参数是否有Multipart文件上传对象processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 进行映射,通过handlerMapper获取到HandlermappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 找到最合适的HandlerAdapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());...// 前置拦截器// 如果前置拦截器返回了false,就不会之后handle方法了,表示当前请求被拦截了if (!mappedHandler.applyPreHandle(processedRequest, response)) {// 返回false就不进行后续处理了return;}// 具体执行handlemv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);//执行后置拦截器mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {...}// 渲染视图// 同时,这里最后还会执行拦截器的afterCompletion()方法processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {// 这里会执行拦截器的afterCompletion()方法triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {// 这里会执行拦截器的afterCompletion()方法triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {...}
}
// 执行前置拦截的方式
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {// 循环遍历Interceptorfor (int i = 0; i < this.interceptorList.size(); i++) {HandlerInterceptor interceptor = this.interceptorList.get(i);// 执行拦截器的preHandle()方法// 如果前置拦截器返回了false,就不会之后handle方法了,表示当前请求被拦截了if (!interceptor.preHandle(request, response, this.handler)) {// 但是还是会执行拦截器的afterCompletion()方法triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}return true;
}void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {// 执行各个拦截器的postHandle()方法,倒序的方式执行for (int i = this.interceptorList.size() - 1; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);interceptor.postHandle(request, response, this.handler, mv);}
}void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {// 执行拦截器的afterCompletion()方法,这里是倒序的方式执行for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);try {interceptor.afterCompletion(request, response, this.handler, ex);}catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}
}