文章目录
- 引文
- 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和 HandlerAdapter initHandlerMappings(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 {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("hushang");
return new ModelAndView();
}
}
实现了HttpRequestHandler接口的Bean对象
@Component("/test")
public class ZhouyuBeanNameController implements HttpRequestHandler {
@Override
public 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")
@ResponseBody
public String test(String username) {
return "hushang";
}
}
一个HandlerFunction对象(以下代码中有两个):
@ComponentScan("com.hushang")
@Configuration
public class AppConfig {
@Bean
public 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文件中配置的四种HandlerAdapter
if (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 beans
initControllerAdviceCache();
// 参数解析器
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) {
// 循环遍历ArgumentResolver
for (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 {
@Override
public 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 {
@Override
public 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;
}
// 找到最合适的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
...
// Actually invoke the handler.
// 具体执行handle
mv = 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());
// 保存结果的Map
MultiValueMap<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中找对应的文件part
Object 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));
// 如果参数类型是MultipartFile
if (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获取到Handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 找到最合适的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
...
// 前置拦截器
// 如果前置拦截器返回了false,就不会之后handle方法了,表示当前请求被拦截了
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
// 返回false就不进行后续处理了
return;
}
// 具体执行handle
mv = 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 {
// 循环遍历Interceptor
for (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);
}
}
}