在《探索SpringMVC-web上下文》中,我们介绍了DispatcherServlet的上下文的初始化。然后为了让大家对DispatcherServlet的各个组件有所了解,我们花了很多的时间来介绍各大组件。现在我们来看看DispatcherServlet是如何使用这些组件完成功能的。
图释:黄色部分是javax.servlet包的,绿色部分是org.springframework的。而红色是DispatcherServlet的九大组件。
HttpServletBean
为this(当前HttpServlet对象)提供属性绑定,基于ServletConfig获取属性值。其核心能力来自BeanWrapImpl,这个在之前讲RequestMappingHandlerAdapter参数解析时也提到过,他是Spring重要的底层支撑组件。在init()
方法中执行该操作。并扩展出来initServletBean()
让子类执行自己的初始化。
FrameworkServlet
Spring的抽象框架Servlet类,为架设SpringMVC处理框架做准备。他会继承Spring上下文,从而为子类提供从上下文获取各种对象的能力。
他干了两个重要的工作
initServletBean()
初始化了上下文、并且调用模板方法onRefresh(ApplicationContext context)
。processRequest
方法,并调用抽象doService
处理请求。因为只有统一了入口,才有可能提供统一的处理能力。而processRequest
方法会维护localeContext、RequestAttributes,同时还会发布ServletRequestHandledEvent时间。DispatcherServlet
SpringMVC的核心,意为将请求分发到处理的处理器进行处理。在onRefresh
方法中从上下文获取到九大组件,从而真正使得DispatcherServlet具备请求处理条件。
实现doService方法,为了便于Handler和View使用框架组件,将ApplicationContext、ThemeResolver、LocaleResolver设置为request的Attribuite。然后调用到关键的doDispatch方法处理请求。后面会重点讲该方法。
这里复习一下,之前的内容:
DispatcherServlet是基于Servlet的声明周期方法
init
来进行初始化的。初始时,会刷新上下文,并且会通过ApplicationContext初始化DispatcherServlet所依赖的九大组件。
在《探索SpringMVC-九大组件》中,我们知道onRefresh方法会调用initStrategies,初始化策略。而该方法就会初始化DispatcherServlet的各个组件。
从设计模式看,因为每个组件都有各种各样的实现,因此使用的策略模式。那么初始化组件,就是初始化策略。这应该也是其方法命名的缘由。
其初始化也比较简单,就是从ApplicationContext中直接获取对应的组件。如果ApplicationContext中没有,则使用默认的。这个默认的就配置在DispatcherServlet.properties中。
最常用的配置就是@EnableWebMvc。他会引入DelegatingWebMvcConfiguration配置,就是这个类声明了各个组件。SpringMVC还提供了WebMvcConfigurer接口,便于大家进行定制。
这些配置都是在上下文刷新时,被加载到容器中的。
前面讲结构的时候,我们提到doDispatch就是处理请求的关键方法。现在我们来看看他是怎么处理请求的。
先说明,我们不会分析异步请求。
这里我将该方法的核心逻辑代码提炼出来
try {
// 检查请求是否为multipart request。是则通过MultipartResolver进行包装
processedRequest = checkMultipart(request);
// 1. 确定处理当前请求的Handler
mappedHandler = getHandler(processedRequest);
// 2. 确定handler的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 3.1 调用拦截器的前置方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 3.2 调用handler处理请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 3.3 调用拦截器的后置方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 如果mv为空,则调用RequestToViewNameTranslator获取默认的viewName
applyDefaultViewName(processedRequest, mv);
// 4. 处理分发后的结果:异常、响应视图。
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));
}
下面我们来具体分析这每个步骤的细节:
HandlerInterceptor#preHandle
方法。如果该方法返回false,还需要调用HandlerInterceptor#afterCompletion
。因为该方法返回false会阻止请求处理。而HandlerInterceptor#afterCompletion
不管请求正常出完成还是异常退出,都需要被调用。HandlerAdapter#handle
方法处理请求HandlerInterceptor#postHandle
方法。render
方法会遍历视图解析器解析视图,不为空则返回view,并调用View#render
方法响应页面。有上面的步骤,我们可以看到,一个常规的请求是如何被处理的。以及HandlerMapping、HandlerAdapter、HandlerExceptionResolver、ViewResolver这几个关键组件是如何被调用串联的。他们就是流水线式的干活。这里没有提到另外的几个组件,原因是为了重点给大家分析DispatcherServlet的核心处理逻辑。这里给大家稍微提一下:
终于把专栏完成了,这是第一个完整完成的专栏,如果有不对的地方,欢迎大家多提意见,一起探讨。《探索SpringMVC》
祝大家新年快乐。
第一篇:
探索SpringMVC-web上下文
上一篇:
探索SpringMVC-组件之ViewResolver