Spring MVC
在 Spring MVC 模型(图转载自Spring官方)中,请求由 Front Controller (即 Dispatch Servlet)进行处理, Dispatch Servlet 在收到请求后会做一些前置的处理,然后把这个请求代理给对应的 Controller 处理类, Controller 处理类处理完以后会返回一个 model 给 Dispatch Servlet,Dispatch Servlet 会做一些后置的处理。Dispatch Servlet 处理完后会把 model 交给视图解析器,让视图解析器解析出对应的视图,然后将解析后的视图交给 Dispatch Servlet, Dispatch Servlet 再做一些视图解析完成后的处理,最后将其打包到 Response 里面并返回给客户端。这就是 Spring MVC 对于请求的一个大致处理流程。
接下来详细看看 Dispatch Servlet 的工作原理,这个类的核心方法是 doService() 方法。这个方法首先对 Request 中的一些 attributes 进行赋值,然后调用 doDispatch 方法。
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
this.logRequest(request);
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap();
Enumeration attrNames = request.getAttributeNames();
label104:
while(true) {
String attrName;
do {
if (!attrNames.hasMoreElements()) {
break label104;
}
attrName = (String)attrNames.nextElement();
} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
// 对 request 中的 attributes 进行赋值
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
RequestPath previousRequestPath = null;
if (this.parseRequestPath) {
previousRequestPath = (RequestPath)request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
ServletRequestPathUtils.parseAndCache(request);
}
// 调用 doDispatch 方法
try {
this.doDispatch(request, response);
} finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
this.restoreAttributesAfterInclude(request, attributesSnapshot);
}
ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
}
}
在 doDispatch 方法中,首先会处理 multipart 类型的请求,如果请求是 multipart 的话,会获得一个解析之后的 multipart 请求,否则不作处理,直接返回原来的请求。然后根据请求的信息去寻找对应的 handler,寻找 handler 的过程实际上就是对 Dispatch Servlet 中预先注册好的各类 HandlerMapping 做一个遍历查找,找到处理相关 URI 的方法后(如带有@RequestMapping的注解)就会返回一个 handler。取得 handler 后会对请求做 handler 的预处理,预处理结束后就会做一个实际的 handler 的调用。handler 的方法执行完以后会针对返回的 modelAndView 来找到对应的视图名,然后做后续的视图呈现的动作。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
// 处理 multipart 类型的请求
processedRequest = this.checkMultipart(request);
// 获得一个解析后的 multipart 请求或原来的请求
multipartRequestParsed = processedRequest != request;
// 寻找对应的 handler
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
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;
}
}
// 根据 handler 对请求做预处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 调用 handler 的方法来处理请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 根据返回的 modelAndView 对象找到对应的视图名
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
// 开始视图呈现的动作
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
doDispatch 方法为我们提供了切入点来实现 AOP,即上面代码中这几行,分别提供了请求处理前、请求处理后、视图呈现后的切入点。
// 请求处理前切入点
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 请求处理后切入点
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 视图呈现后切入点
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
看看请求处理前的切入点的实现原理,首先取得所有的 HandlerInterceptor 并做一个遍历,分别调用每个interceptor 的 preHandle 方法,如果任意一个 preHandle 方法返回 false 则直接触发 triggerAfterCompletion,即直接执行视图呈现后的处理动作,并返回 false。doDispatch 方法收到 false 以后就会直接 return,不执行后续的操作。如果一切顺利,applyPreHandle 方法应该返回 true。另外两个 postHandle 和 afterCompletion 的处理同理。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 取得所有的 HandlerInterceptor 并做一个遍历,分别调用每个interceptor 的 preHandle 方法
for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
return true;
}
在 Spring Boot 中实现请求拦截可以通过应用程序类实现 WebMvcConfigurer 接口,重写接口内的 addInterceptors 方法来添加 Interceptor,用 addPathPatterns 方法指定需要拦截的URI。
public class ExampleApplication implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加拦截器
registry.addInterceptor(new ExampleInterceptor())
// 添加需要拦截的URI
.addPathPatterns("/example1/**").addPathPatterns("/example2/**");
}
}
自己实现的请求拦截器需要实现 HandlerInterceptor 接口,并重写其中 preHandle、postHandle 和 afterCompletion 的三个方法。
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
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 {
}
}