DispatcherServlet
探究到DispatcherServlet这个MVC中最重要的一个前端请求调度器时,突然考虑到了它本身的线程安全性,由此引入对DispatcherServlet的了解
首先是一个大概的MVC执行框图
看代码是spring-webmvc-4.3.17版本,首先看一下DispatcherServlet的继承结构
DispatcherServlet直接继承于FrameworkServlet,然后是HttpServletBean,再往上是HttpServlet直到GenericServlet定义了最基本的servlet标准。
我们知道在spring的托管下,所有的servlet在IOC容器初始化的时候都已经加载完毕,包含这个最重要的DispatcherServlet,作为web服务入口的前端控制器,所需要的参数都在web.xml中进行配置,它会拦截匹配规则的请求。
DispatcherServlet初始化
在这个继承体系下,当ServletWrappingController.afterPropertiesSet()
初始化所有的Servlet时,会调用HttpServlet的init()方法进行初始化,实际调用的是DispatcherServlet.initStrategies()
方法
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); //文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;
initLocaleResolver(context); //本地化解析
initThemeResolver(context); //主题解析
initHandlerMappings(context); //通过HandlerMapping,将请求映射到处理器
initHandlerAdapters(context); //通过HandlerAdapter支持多种类型的处理器
initHandlerExceptionResolvers(context); //如果执行过程中遇到异常将交给HandlerExceptionResolver来解析
initRequestToViewNameTranslator(context); //直接解析请求到视图名
initViewResolvers(context); //通过ViewResolver解析逻辑视图名到具体视图实现
initFlashMapManager(context); //flash映射管理器
}
在初始化的时候,DispatcherServlet已经持有了所有url对应的Controller的映射关系,方便后续将request中请求的url分发给适合的Controller。
DispatcherServlet线程安全?
其实这是主要想关注的点,DispatcherServlet请求调度器是否是线程安全的呢?
在web容器中对DispatcherServlet初始化的时候是放在Synchronized修饰的同步代码块中实现的,可以保证Servlet创建和初始化的时候是对线程可见的,因此可以保证DispatcherServlet调用initStrategies方法是可以把成员变量刷新到主存中的。但是值得注意的是在DispatcherServlet中有一些变量是未被static final修饰的,这不会引起线程不安全么
/** Detect all HandlerMappings or just expect "handlerMapping" bean? */
private boolean detectAllHandlerMappings = true;
/** Detect all HandlerAdapters or just expect "handlerAdapter" bean? */
private boolean detectAllHandlerAdapters = true;
/** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */
private boolean detectAllHandlerExceptionResolvers = true;
/** Detect all ViewResolvers or just expect "viewResolver" bean? */
private boolean detectAllViewResolvers = true;
/** Throw a NoHandlerFoundException if no Handler was found to process this request? **/
private boolean throwExceptionIfNoHandlerFound = false;
/** Perform cleanup of request attributes after include request? */
private boolean cleanupAfterInclude = true;
/** MultipartResolver used by this servlet */
private MultipartResolver multipartResolver;
/** LocaleResolver used by this servlet */
private LocaleResolver localeResolver;
/** ThemeResolver used by this servlet */
private ThemeResolver themeResolver;
/** List of HandlerMappings used by this servlet */
private List<HandlerMapping> handlerMappings;
/** List of HandlerAdapters used by this servlet */
private List<HandlerAdapter> handlerAdapters;
/** List of HandlerExceptionResolvers used by this servlet */
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** RequestToViewNameTranslator used by this servlet */
private RequestToViewNameTranslator viewNameTranslator;
/** FlashMapManager used by this servlet */
private FlashMapManager flashMapManager;
/** List of ViewResolvers used by this servlet */
private List<ViewResolver> viewResolvers;
但仔细查看这些变量的使用处,发现都是在initStrategies()初始化时调用的init方法中进行了赋值,这样就可以保证未被安全修饰符修饰的变量始终是可见的,尽管它们未被修饰,但是并不会影响到DispatcherServlet的可见性。为什么要这么写?特别是那几个未被修改的Boolean类型的参数,尚未得知。
DispatcherServlet处理(分发)请求
按照上一篇blog中所述的在filter过滤后请求到达DispatcherServlet,大概的流程是
ServletHolder.handle() ->
FrameworkServlet.service() -> FrameworkServlet.processRequest()
DispatcherServlet.doService() -> DispatcherServlet.doDispatch()
其中,FrameworkServlet定义了Servlet的service抽象处理流程
/** FrameworkServlet
*/
@Override
protected void service(HttpServletRequest request, HttpServletRespo