代码1
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 {
// 1. 检测是否文件上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// 2. 根据请求地址找到哪个类(Controller)能处理请求
mappedHandler = getHandler(processedRequest);
// 3. 如果没有找到处理器能处理请求就报404或者抛异常
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 4. 拿到能执行这个类的所有方法的适配器,因为有可能类中的方法是继承的,也可能是自定义的,所以会存在不同的适配器去执行相应的方法(类似反射工具)
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;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 5. 适配器执行目标方法,并返回一个ModelAndView,不管控制器的目标方法返回类型是什么
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 如果目标方法返回类型是void,则设置一个默认的视图名
applyDefaultViewName(processedRequest, mv);
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);
}
// 6. 根据方法最终执行完成封装的MAV,转发到页面,而且MAV中的数据可以从请求域中获取
处理返回结果,包括处理异常、渲染页面、发出完成后通知Interceptor的afterCompletion
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
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);
}
}
}
}
getHandler返回的是HandlerExecutionChain,Handler执行链,包括我们写的Controller和拦截器
1. 看下类定义
2. 看下方法内部是怎么根据请求地址找到目标处理器的
代码2
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request 包含请求地址
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@Nullable
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;
}
我们可以看到方法内部遍历了一个handlerMappings,它里面保存了每一个处理器能处理哪些请求的映射信息
可以看下List的内容,我们一般给Controller标注解,所以一般用到的是红框里面的HandlerMapping
可以看下这个这个HandlerMapping里有什么东东:
可以理解为,我们为Controller的目标方法标了注解@RequestMapping,容器启动的时候就会去查找,找到就把它保存在HandlerMapping的mappingLookup里面
挖我,是不是很清晰明了了,键是地址,值是方法,request中包含的地址过来与这个值匹配,就会执行目标方法。
上去看下代码2的这行
HandlerExecutionChain handler = mapping.getHandler(request);
3. 那么现在不得不看下HandlerMapping的getHandler方法了
这里要说一下HandlerMapping是一个接口,我们直接看它的实现类 AbstractHandlerMapping中该方法的定义,后面再讲细节
/**
* Look up a handler for the given request, falling back to the default
* handler if no specific one is found.
* @param request current HTTP request
* @return the corresponding handler instance, or the default handler
* @see #getHandlerInternal
*/
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
}
wc,看看注释发现了什么,找一个处理器来处理当前的请求,那我们可以自定义一个HandlerMapping吗?等会儿讲,
这里总结一下:getHandler最终拿到的是一个执行器链,这个执行器链里面包含了能处理请求的Handler(@Controller标注的类)和拦截器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
我们已经拿到了执行器链,mappedHandler.getHandler()从执行器链里拿到我们之前定义的处理器(也就是@Controller标注的类)
代码1中的注释讲过我们要拿处理器的适配器去执行处理器的目标方法,看下代码
代码3
/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
看到没,又是一个遍历,看哪个适配器支持处理这个处理器的目标方法,因为每个Adapter的support方法实现都不一样,支持就返回,看看这次handlerAdapters里面有哪些
再看下HandlerAdapter的实现类会发现只有第一个支持
看下它的supports方法:
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
//supportsInternal会一直返回true
}
这样就找到了适配器
总结一下: 我们先拿到执行器链中的处理器(handler),遍历所有的适配器看哪个适配器支持这个处理器,支持就返回这个适配器
看下代码1的这行
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
在另一篇中讲adapter的handle细节,现在知道它执行完返回一个ModelAndView,目标方法返回的数据都塞在里面
看代码1的这行
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
最终会执行
/**
* Handle the result of handler selection and handler invocation, which is
* either a ModelAndView or an Exception to be resolved to a ModelAndView.
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
这个也是DispatcherServlet的内部方法
看一下render
代码4
/**
* Render the given ModelAndView.
* This is the last stage in handling a request. It may involve resolving the view by name.
* @param mv the ModelAndView to render
* @param request current HTTP servlet request
* @param response current HTTP servlet response
* @throws ServletException if view is missing or cannot be resolved
* @throws Exception if there's a problem rendering the view
*/
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
// 1. 选择一个ViewResolver来解析视图
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 2. 渲染视图
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
可以看到该方法内部有两个关键点
我们首先来看第一个
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
看看这个方法的内部
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
看到没,还是遍历一个viewResolver的List看有没有能解析这个视图的视图解析器,有能解析就直接解析后返回,默认是这个InternalResourceViewResolver
返回这个View后接着看代码4的这行
view.render(mv.getModelInternal(), request, response);
它会走InternalResourceView的父类AbstractView的render方法
@Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("View " + formatViewName() +
", model " + (model != null ? model : Collections.emptyMap()) +
(this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
}
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
在后面调了InternalResourceView的renderMergedOutputModel渲染视图
总结dispatcherServlet会选择一个ViewSolver来解析并渲染视图
用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;
DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该请求对应的处理器(Handler)和拦截器(Interceptor)List
DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter;(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法)
提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
- HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
- 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
- 数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
- 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;
ViewResolver 结合Model和View,来渲染视图;
将渲染结果返回给客户端。