从SpringMvc的中心处理器
DispatcherServlet
开始说起。源码主要是基于jdk1.8
,springboot 2.1.6.RELEASE
,添加一个spring-boot-starter-web
依赖。
一. DispatcherServlet结构图
二. 测试代码
首先我们先暴露一个接口
@RestController
public class IndexController {
@GetMapping("/hello")
public String hello(String name) {
return "hello: " + name;
}
}
调用http://localhost:8080/hello
:
1. 调用链
Debug一下: 查看一下调用链,如下:
1. HttpServlet
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
2. FrameworkServlet
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
3. HttpServlet
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
4. FrameworkServlet
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
5. DispatcherServlet
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception
6. DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception
我们查看一下具体的代码,
2. FrameworkServlet#processRequest
代码进行了一些删减
/**
* 处理此请求,不考虑结果而发布事件,实际事件处理由抽象执行
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// 设置了LocaleContextHolder和RequestContextHolder,使用ThreadLocal保存,所以在之后可以直接通过LocaleContextHolder,RequestContextHolder获取相关信息
initContextHolders(request, localeContext, requestAttributes);
try {
// todo下面分析
doService(request, response);
} catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
// 重置LocaleContextHolder和RequestContextHolder
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
// 发布相关事件
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
前一部分是将当前请求的Locale对象和请求的属性,通过LocaleContextHolder
和RequestContextHolder
设置到ThreadLocal
对象中,也就是分别将这两个对象和请求线程做了绑定。之后我们就可以从里面获取这些绑定的值使用。调用doService()
处理结束后,再通过LocaleContextHolder
和RequestContextHolder
恢复相应的绑定。接着发布了一个ServletRequestHandledEvent
事件。具体我们再看doService()
方法。
3. DispatcherServlet#doService
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
// 给request设置一些属性
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, 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);
}
try {
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
这里面有几个requet.setAttribute()
方法的调用,将之前在初始化流程中实例化的对象设置到request
的属性中,供下一步处理使用,这里有web上下文,本地化解析器等。然后继续调用doDispatch()
处理请求。
4. 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.
// 根据当前请求获取HandlerMapping,并得到HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 获取匹配的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
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;
}
}
// 执行拦截器的 preHandle()方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 调用handler,得到ModelAndView
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);
}
// 处理ModelAndView,最后再执行拦截器的 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 {
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);
}
}
}
}
到此终于找了处理流程的主线,我们看到了一系列SpringMvc源码分析1 中介绍的对象,以及处理流程。
这里会从DispatcherServlet
内维护的handlerMappings
中找到匹配的HandlerMapping
方法,并执行其getHandler()
方法,得到HandlerExecutionChain
,看一下具体的内部结构:
可以看到维护了handler
和拦截器列表。
接着HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
得到HandlerAdapter
,然后执行拦截器的postHandle()
方法。之后调用HandlerAdapter
的handle()
,处理controller
里面的逻辑,得到ModelAndView
,接着执行拦截器的postHandle()
方法。然后就会根据viewResolvers
得到具体的视图View
,最后就是渲染视图,返回给客户端。
三. 总结
到此,我们可以了解DispatcherServlet
大概的处理流程。其中
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
还有很多的处理逻辑,包括HandlerMethodArgumentResolver
,HandlerMethodReturnValueHandler
,HttpMessageConverter
等的处理,我们后面继续分析。