SpringMVC中的DispatcherServlet结构和初始化详解
前言
SpringMVC中Spring容器的关系是通过监听方式启动的。
那么Spring(或者说SpringMVC)与Servlet的Web容器(如:Tomcat、jetty)的关系则是通过DispatcherServlet进行关联。
在Web容器看,DispatcherServlet就是同一普通的Servlet,从Spring看是与Web的关联,会将所有的请求转发到该控制器。
1、DispatcherServlet结构
从层级能看出其顶层实现了Servlet和ServletConfig接口,由于Web容器只认识Servlet,而Spring只认识Bean,所以DispatcherServlet既是请求转发和返回视图解析控制中心,更是web与Spring的适配器,从其父子结构层级就能看出,慢慢的从Servlet适配到Bean的过程。
1)、GenericServlet
GenericServlet实现了Servlet、ServletConfig接口,也实现了所有未实现的方法。主要是config相关,init和service方法。
2)、HttpServlet
HttpServlet主要定义了Http协议相关的请求方法,以及Last-Modified和If-Modified-Since相关参数的处理。
3)、HttpServletBean
HttpServletBean除了继承自HttpServlet,还实现了EnvironmentCapable和EnvironmentAware接口,处理Spring Environment相关,之前分析过。
4)、FrameworkServlet
FrameworkServlet实现了接口ApplicationContextAware,注入了ApplicationContext,以及contextConfigLocation配置。
5)、DispatcherServlet
DispatcherServlet作为整个MVC的控制器,那么请求该转发到哪个Controller,返回的model会该使用哪个视图返回(视图解析器进行解析)。
都需要在这里进行完成,所以初始化时需要使用到Spring MVC的九大件。
初始化时使用了策略模式初始化九大件
2、DispatcherServlet初始化(九大件的加载和初始化)
web容器启动时会加载配置的DispatcherServlet,则会调用其init方法,在GenericServlet中实现,如下:
@Override public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); }
设置了ServletConfig值,并且调用了自定义无参空方法init,在子类HttpServletBean中实现。
public final void init() throws ServletException { // 解析init-param参数,并封装成ServletConfigPropertyValues PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { throw ex; } } // Let subclasses do whatever initialization they like. initServletBean(); }
在初始化方法之前,允许init-param通过addRequiredProperty方法添加到requiredProperties属性中的配置,以Bean的形式进行加载。主要的方法初始化方法是调用本类的空方法initServletBean,在下层子类FrameworkServlet中实现
@Override protected final void initServletBean() throws ServletException { // 省略日志打印和,try catch部分的代码 this.webApplicationContext = initWebApplicationContext(); // 留给子类实现,子类DispatcherServlet没有进行实现 initFrameworkServlet(); }
protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // 确保Spring容器创建并且唯一,所以无论ContextLoaderListener是否启动和refresh wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. synchronized (this.onRefreshMonitor) { onRefresh(wac); } } if (this.publishContext) { // 将Spring的WebApplicationContext容器,设置到Web容器中 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; }
无论监听是否启动Spring容器执行refresh方法,这里都会进行检查启动。
并将启动的Spring容器,设置到web容器中,所以当启动完成后两个容器中就是你中有我,我中有你的状态。
Spring MVC的初始化会同步锁synchronized(onRefreshMonitor)保证下初始化,在子类层级DispatcherServlet中完成:
@Override protected void onRefresh(ApplicationContext context) { initStrategies(context); }
使用策略模式初始化九大件,为后续请求调用,视图解析等做准备。
protected void initStrategies(ApplicationContext context) { // 文件上传 initMultipartResolver(context); // i18n相关解析器 initLocaleResolver(context); // 主题解析器(一种主题就是一类网页风格) initThemeResolver(context); // 请求映射 initHandlerMappings(context); // 适配器,后面请求转发的Controller会专门分析 initHandlerAdapters(context); // 操作异常的解析处理(中途发送异常该调整到哪里) initHandlerExceptionResolvers(context); // 请求的Controller没有返回View时,该如何解析 initRequestToViewNameTranslator(context); // 视图解析器(ModelAndView中的view字符串怎么进行解析) initViewResolvers(context); // 请求属性(参数)管理器,主要用于重定向等情况时获取属性,比如post重定向到get initFlashMapManager(context); }
九大件的处理方式大致相同,只是定义的Bean名称和调用getBean的类型不同而已,比如上传文件,如下:
private void initMultipartResolver(ApplicationContext context) { // 省略日志打印和try catch的代码 this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); }
主要看初始化的什么类型,是在静态代码块的策略模式中加载初始化(DispatcherServlet.properties),如下:
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\ org.springframework.web.servlet.function.support.RouterFunctionMapping org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\ org.springframework.web.servlet.function.support.HandlerFunctionAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
到此这篇关于SpringMVC中的DispatcherServlet结构和初始化详解的文章就介绍到这了,更多相关DispatcherServlet结构和初始化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Java多线程环境下SimpleDateFormat类安全转换
这篇文章主要介绍了Java多线程环境下SimpleDateFormat类安全转换,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2020-02-02springboot web项目中 Set-Cookie 失败原因及解决办法
这篇文章主要介绍了springboot web项目中 Set-Cookie 失败原因及解决办法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2023-10-10Springboot基于maven打包分离lib及resource
这篇文章主要介绍了Springboot基于maven打包分离lib及resource,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下2020-10-10
最新评论