javascript
【Spring】DispatcherServlet的启动和初始化
使用過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容器是XmlWebApplicationContextConfigurableWebApplicationContext 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獲取HandlerMappingHandlerMapping 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的启动和初始化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux内存(手动释放cache)
- 下一篇: PostgreSQL 客户端乱码问题