如题,整理了一下SpringMVC的工作流程。
先看图:
再看文字:
- 客户端(浏览器)发送请求,直接请求到 DispatcherServlet。
- DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。
- 解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由 HandlerAdapter 适配器处理。
- HandlerAdapter 会根据 Handler来调用真正的处理器来处理请求,并处理相应的业务逻辑。
- 处理器处理完业务后,会返回一个 ModelAndView 对象,Model 是返回的数据对象,View 是个逻辑上的 View。
- ViewResolver 会根据逻辑 View 查找实际的 View。
- DispaterServlet 把返回的 Model 传给 View(视图渲染)。
- 把 View 返回给请求者(浏览器)
再看源码(主要看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 {
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//1. 检查是否为文件上传请求
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//2. 根据当前的请求地址找到哪个类能来处理
//拿到方法的执行链(方法+拦截器=执行链),包含拦截器
mappedHandler = this.getHandler(processedRequest);
//3. 如果没有找到哪个处理器(控制器)能处理这个请求,就404或抛异常
if (mappedHandler == null || mappedHandler.getHandler() == null) {
this.noHandlerFound(processedRequest, response);
return;
}
//4. 根据当前处理器类拿到能执行这个类的所有方法的适配器(反射工具)
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 (this.logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
this.logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
//执行所有拦截器的preHandler,有一个拦截器返回false,目标方法就不执行,直接跳到afterCompletion
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
//处理器Controller(控制器Handler)的方法被调用
//5. 适配器执行目标方法:将目标方法执行完成后的返回值作为视图名,保存到ModelAndView中。也就是说目标方法无论怎么写,适配器执行完成后都会将信息封装成ModelAndView。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
//若目标方法执行完没有返回视图名,设置一个默认的视图名
this.applyDefaultViewName(request, mv);
//目标方法只要正常,postHandler就会执行,有异常,都会来到afterCompletion
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var28) {
dispatchException = var28;
}
//转发到目标页面
//6. 根据方法最终完成后封装的ModelAndView,转发到对应页面,而且ModelAndView中的数据可以从请求域中获取。
//如果页面渲染有异常,会来到afterCompletion
this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception var29) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var29);
} catch (Error var30) {
this.triggerAfterCompletionWithError(processedRequest, response, mappedHandler, var30);
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
} else {
if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
}
两个细节:
① getHandler()是怎么根据当前请求找到能处理它的类的?
getHandler()会返回目标处理器类的执行链,HandlerMapping保存了每一个处理器能处理哪些方法的映射信息,在HandlerMapping中找到这个请求的映射信息,获取到目标处理器类。
② getHandlerAdapter()是怎么找到能执行这个类的所有方法的适配器的?
AnnotationMethodHandlerAdapter能解析注解方法的适配器,根据当前处理器类,找到当前类的HandlerAdapter(适配器)