概述
使用过SpringMVC的都知道DispatcherServlet,下面介绍下该Servlet的启动与初始化。作为Servlet,DispatcherServlet的启动与Serlvet的启动过程是相联系的。在Serlvet的初始化过程程中,Serlvet的init方法会被调用,以进行初始化。DispatcherServlet的基类HttpServletBean中的这个初始化过程源码如下:
public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // 获取Servlet的初始化参数,对Bean属性进行配置 try { PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); 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) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } // 调用子类的initServletBean方法进行具体的初始化工作 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } } // initServletBean这个初始化工作位于FrameworkServlet类中 protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); // 这里初始化上下文 try { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } } protected WebApplicationContext initWebApplicationContext() { // 调用WebApplicationContextUtils来得到根上下文,它保存在ServletContext中 // 使用它作为当前MVC上下文的双亲上下文 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { if (cwac.getParent() == null) { cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { wac = findWebApplicationContext(); } if (wac == null) { wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { onRefresh(wac); } // 把当前建立的上下文存到ServletContext中,使用的属性名是跟当前Servlet名相关的 if (this.publishContext) { String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
在初始化开始时,需要读取配置在ServletContext中的Bean属性参数,这些属性参数设置在web.xml的Web容器初始化参数中。
接着会执行DispatcherServlet持有的IOC容器的初始化过程,在这个过程中,一个新的上下文会被建立起来,这个DispatcherServlet持有的上下文被设置为根上下文的子上下文。可以这么理解,根上下文是和web应用相对应的一个上下文,而DispatcherServlet持有的上下文是和Servlet对应的一个上下文。在一个web应用中可以容纳多个Servlet存在;对应的,对于应用在web容器中的上下文体系,一个根上下文可以作为许多Serlvet上下文的双亲上下文。对这点的理解有助于在web环境中IOC容器的Bean设置和检索有所帮助,因为在向IOC容器getBean时,IOC容器会先向其双亲上下文去getBean,换句话说就是在根上下文中定义的Bean是可以被各个Servlet持有的上下文得到和共享的。DispatcherServlet持有的上下文被建立后,也需要和其他 IOC容器一样完成初始化操作,这个初始化操作就是通过refresh方法完成的,最后DispatcherServlet再给自己持有的上下文命名并设置到web窗口的上下文中(即ServletContext),这个名称和在web.xml中设置的DispatcherServlet的Servlet名称有关,进而保证这个上下文在web环境上下文体系中的唯一性。
上面代码执行后,这个Servlet的上下文就建立起来了,具体取得根上下文的过程是在WebApplicationContextUtils中实现的,源码如下:
public static WebApplicationContext getWebApplicationContext(ServletContext sc) { // ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE这个属性代表的根上下文 // 在ContextLoaderListener初始化的过程中被建立,并设置到ServletContext中 return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); }
这个根上下文是ContextLoader设置到ServletContext中的,使用属性ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,同时对这个IOC容器的Bean配置文件,ContextLoader也进行了设置,默认的位置是在/WEB-INF/applicationContext.xml文件中,由于这个根上下文是DispatcherServlet建立的上下文的双亲上下文,所以根上下文中管理的bean是可以被DispatcherServlet的上下文使用的,反过来则不行,通过getBean向IOC容器获取bean时,会先到其双亲IOC容器尝试获取。
回到FrameworkServlet继续看DispatcherServlet的上下文是怎样建立的,源码如下:
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { Class<?> contextClass = getContextClass(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name '" + getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "'" + ", using parent context [" + parent + "]"); } if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } // 实例化需要的具体上下文对象,并为这个上下文对象设置属性 // 这里使用的是DEFAULT_CONTEXT_CLASS,这个DEFAULT_CONTEXT_CLASS被设置为XmlWebApplicationContext.class, // 所以在DispatcherServlet中使用的IOC容器是XmlWebApplicationContext ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); // 设置双亲上下文(也就是根上下文) wac.setParent(parent); wac.setConfigLocation(getContextConfigLocation()); configureAndRefreshWebApplicationContext(wac); return wac; }
建立DispatcherServlet的上下文,需要把根上下文作为参数传递给它,再使用反射实例化上下文对象,并为它设置参数,按默认配置的话这个上下文对象就是XmlWebApplicationContext对象,这个类型是在DEFAULT_CONTEXT_CLASS参数中设置好并允许BeanUtils使用的,实例化结束之后,还需要为这个上下文对象设置好一些基本的配置,这些配置包括它的双亲上下文、Bean定义配置的文件位置等,完成配置后就通过调用IOC容器的refresh方法来完成IOC容器的最终初始化。
通过上面web容器一系列的操作后,在这个上下文体系建立和初始化完毕的基础上,Spring MVC就可以发挥作用了。此时DispatcherServlet就持有一个以自己的Servlet名称命名的IOC容器,这个IOC容器建立后,意味着DispatcherServlet拥有自己的Bean定义空间,这为使用各个独立的XML文件来配置MVC中各个Bean创建了条件,初始化完成后,Spring MVC的具体实现和普通的Spring应用程序的实现并没有太多差别,在DispatcherServlet的初始化过程中,以对HandlerMapping的初始化调用作为触发点,可以看下图Spring MVC模块初始化的方法调用关系图,
这个调用关系最初由HttpServletBean的init方法触发,这个HttpServletBean是HttpServlet的子类,接着会在HttpServletBean的子类FrameworkServlet中对IOC容器完成初始化,在这个初始化方法中会调用DispatcherServlet的initStrategies方法,在这个initStrategies方法中,启动整个Spring MVC框架的初始化工作。
从上面的方法调用关系图也可以看出对MVC的初始化是在DispatcherServlet的initStrategies中完成的,包括对各种MVC框架的实现元素,比如国际化支持LocalResolver、视图生成的ViewResolver等的初始化工作,源码如下:
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
上面各个初始化方法的名称应该比较好理解,这里以常见的HandlerMapping为例来说明initHandlerMappings()实现,Mapping映射的作用就是为HTTP请求找到相应的Controller控制器,HandlerMappings完成对MVC中Controller的定义和配置,DispatcherServlet中HandlerMappings初始化过程源码如下:
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; // 这里导入所有的HandlerMapping Bean,这些Bean可以在当前的DispatcherServlet的IOC // 容器中,也可能在其双亲上下文中,这个detectAllHandlerMappings的默认值设为true, // 即默认地从所有的IOC容器中获取 if (this.detectAllHandlerMappings) { Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values()); OrderComparator.sort(this.handlerMappings); } } else { try { // 可以根据名称从当前的IOC容器中通过getBean获取HandlerMapping HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { } } // 如果没找到HandlerMappings,那么需要为Servlet设置默认的HandlerMappings, // 这些默认的值可以设置在DispatcherServlet.properties中 if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
在HandlerMapping初始化的过程中,把在Bean配置文件中配置好的handlerMapping从IOC容器中取得。经过上面的读取过程,handlerMappings变量就已经获取了在BeanDefinition中配置好的映射关系。其他的初始化过程和handlerMappings比较类似,都是从IOC容器中读入配置,所以说MVC初始化过程是建立在IOC容器已经初始化完成的基础之上的。执行完其他的各个初始化操作后,整个初始化过程就基本完成了。
转载于:https://www.cnblogs.com/weknow619/p/7376125.html
最后
以上就是现实猫咪为你收集整理的【Spring】DispatcherServlet的启动和初始化的全部内容,希望文章能够帮你解决【Spring】DispatcherServlet的启动和初始化所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复