Spring-web源碼解析之Initializer

基於4.1.7.RELEASE

Spring-WebApplicationInitializer

AbstractContextLoaderInitializer / AbstractDispatcherServletInitializer / AbstractAnnotationConfigDispatcherServletInitializer

後3個類都是實現了WebApplicationInitializer接口,WebApplicationInitializer接口主要的作用是提供在Servlet 3.0+環境中對於ServletContext的可編程實現,可以跟web.xml混合使用,該接口的實現類會被SpringServletContainerInitializer自動調用,而SpringServletContainerInitializer則會被任意3.0+的Servlet自動啓用,可以使用@Order註解來定義WebApplicationInitializer的執行順序。

值得注意的一點是在tomcat的7.0.14(包含)以下版本時, tomcat會把url :“/”跟DefaultServlet綁定在一起且無法重寫綁定關係。


需要注意的方法:

@Override
public void onStartup(ServletContext servletContext) throws ServletException {
   registerContextLoaderListener(servletContext);
}
在初始化Web Application的時候被調用,用於配置相關的ServletContext
在AbstractContextLoaderInitializer裏,這裏調用了registerContextLoaderListener方法,向其子類提供的WebApplicationContext註冊了一個ContextLoaderListener
protected void registerContextLoaderListener(ServletContext servletContext) {
   WebApplicationContext rootAppContext = createRootApplicationContext();
   if (rootAppContext != null) {
      servletContext.addListener(new ContextLoaderListener(rootAppContext));
   }
}

而在AbstractDispatcherServletInitializer類中,onStartup則被重寫,調用了本類的registerDispatcherServlet方法,註冊了一個默認名字爲dispatcher的DispatcherServlet,並且對Servlet進行了一些初始化工作。

protected void registerDispatcherServlet(ServletContext servletContext) {
   String servletName = getServletName();
   WebApplicationContext servletAppContext = createServletApplicationContext();

   DispatcherServlet dispatcherServlet = new DispatcherServlet(servletAppContext);
   ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
   
   registration.setLoadOnStartup(1);
   registration.addMapping(getServletMappings());
   registration.setAsyncSupported(isAsyncSupported());

   Filter[] filters = getServletFilters();
   if (!ObjectUtils.isEmpty(filters)) {
      for (Filter filter : filters) {
         registerServletFilter(servletContext, filter);
      }
   }

   customizeRegistration(registration);
}

由代碼中可以看出,在註冊完dispatchServlet之後,設置啓動優先級爲1表明該Servlet隨容器啓動而初始化,添加Servlet的URL mapping即web.xml中的url-pattern選項,註冊Servlet的filters並且調用了customizeRegistration方法來實現自定義配置ServletContext。其中

getServletName(), customizeRegistration() 可由實現類自己決定是否重寫

createServletApplicationContext(),createRootApplicationContext()(繼承自AbstractContextLoaderInitializer),getServletMappings()子類必須實現

有一個有意思的現象是在registerServletFilter中添加Filter時

int counter = -1;
while (counter == -1 || registration == null) {
   counter++;
   registration = servletContext.addFilter(filterName + "#" + counter, filter);
   Assert.isTrue(counter < 100,
         "Failed to register filter '" + filter + "'." +
         "Could the same Filter instance have been registered already?");
}

這表明,同一個名稱的filter最多隻能註冊100個。

由於AbstractAnnotationConfigDispatcherServletInitializer繼承了AbstractDispatcherServletInitializer,所以它滿足了上面要求的createServletApplicationContext(),createRootApplicationContext()兩個方法實現,而由於它是一個抽象類,getServletMappings()則被交給它的子類去實現。這兩個方法的實現大同小異

Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
   AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
   rootAppContext.register(configClasses);
   return rootAppContext;
}else {
   return null;
}

如上所示,其不同在意獲取configClasses的不同之處,一個是獲取用於Servlet Application Context的配置,另一個是用於root Application Context的配置,無論是用在哪裏,都是由其子類通過註解@Configuration和@Component來標識的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章