Spring中關於的WebApplicationInitializer及其實現的分析


1. WebApplicationInitializer

隨着 JavaConfig配置方式逐步替代配置, WebApplicationInitializer能夠看作 Web.xml的替代,此接口在Web容器啓動的時候會記載這個接口的實現類,從而起到 Web.xml的做用,從而咱們能夠經過其自定義配置Web組件.

首先咱們進入這個接口,它僅有一個方法#onStartup方法,咱們並不容易看出其用意何在?而此方法又是什麼時候被調用的?java

public interface WebApplicationInitializer {
    void onStartup(ServletContext var1) throws ServletException;
}

可是在這個類所屬的包下有另外一個類- - -SpringServletContainerInitializerweb

@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    public SpringServletContainerInitializer() {
    }

    public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
        List<WebApplicationInitializer> initializers = new LinkedList();
        Iterator var4;
        if(webAppInitializerClasses != null) {
            var4 = webAppInitializerClasses.iterator();

            while(var4.hasNext()) {
                Class<?> waiClass = (Class)var4.next();
                if(!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        initializers.add((WebApplicationInitializer)waiClass.newInstance());
                    } catch (Throwable var7) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
                    }
                }
            }
        }

        if(initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
        } else {
            servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
            AnnotationAwareOrderComparator.sort(initializers);
            var4 = initializers.iterator();

            while(var4.hasNext()) {
                WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
                initializer.onStartup(servletContext);
            }
        }
    }
}
SpringServletContainerInitializer#onStartup方法中,首先判斷參數 WebAppInitializerClasses這個 Set集合是否爲空.若不爲空而且是 WebApplicationInitializer接口的實現,就將其轉存到另外一個集合中,以後對集合進行排序並遍歷調用其 #onStartup方法,從而咱們能夠知曉以前所說的 WebApplicationInitializer是如何在 Web流程中被調用的.

可是另外兩個問題又了:
1. SpringServletContainerInitializer#onStartup方法又是如何被調用的呢?
2.其類上註解 @HandlesType又有什麼做用呢?

1.segmentfault


官方聲明中指出:爲了支持無xml配置化,提供了 ServletContainerInitializer,經過 SPI機制,當啓動 Web容器的時候,會自動到相應的 jar包下找到 META-INF/servicesServletContainerInitializer的全路徑名的文件,並根據其內容的全路徑進行實例化.而咱們能夠發現文件中的名稱就是 SpringServletContainerInitializer,因此 SpringServletContainerInitializer做爲 ServletContainerInitializer的實現,經過 SPI機制在 Web容器加載的時候會被自動調用.

image.png

2.app


  • 而對於@HandlesType註解,其做用爲:讀取@HandlesType註解value的值,根據其值將掃描路徑下全部該值所表示的接口實現,並將其生成一個Set集合提供給#onStartup方法使用.
  • ApplicationContext內部啓動時會通知ServletContainerInitializer#onStartup方法,而這個方法的第一個參數Set集合就是註解Value值指定的Class集合,而在這裏即爲WebApplicationInitializer實現的集合
  • 在下列代碼中所示,在Tomcat全部容器的抽象類中ContainerMBean#addChild方法中,Tomcat在爲Host容器添加Context子容器時,會爲其分配一個ContextConfig類,其中經過#processServletContainerInitializer方法來獲取@HandlesType註解
public class ContainerMBean extends BaseModelMBean {
    public void addChild(String type, String name) throws MBeanException {
        //........
        try {
            if(contained instanceof StandardHost) {
                HostConfig config = new HostConfig();
                contained.addLifecycleListener(config);
            } else if(contained instanceof StandardContext) {
                ContextConfig config = new ContextConfig();
                contained.addLifecycleListener(config);
            }
        }
    }
}

-------------------------------------------------------------------------------------

public class ContextConfig implements LifecycleListener {
    protected void webConfig() {
            //.........
            if(this.ok) {
                this.processAnnotations(orderedFragments, webXml.isMetadataComplete(), javaClassCache);
            }
        }


    protected void processServletContainerInitializers() {
            //.........
             ServletContainerInitializer sci = (ServletContainerInitializer)var13.next();
            this.initializerClassMap.put(sci, new HashSet());

            HandlesTypes ht;
            try {
                ht = (HandlesTypes)sci.getClass().getAnnotation(HandlesTypes.class);
            } 
        }
}

2.AbStractAnnotationConfigDispatcherServletInitializer

  • 官方文檔聲明中指出:Spring MVC中咱們實際上是能夠建立多個DispatcherServlet的,只須要建立多個繼承自AbstractAnnotationConfigDispatcherServletInitializer的子類便可,而每一個DispatcherServlet都有本身的應用上下文(servlet application context),這個應用上下文只針對當前的DispatcherServlet有用,這也就是#getServletConfigClasses的做用,用來獲取這個DispatcherServlet的應用上下文的配置類
  • 而除了每一個DispatcherServlet配置類的應用上下文,還有一個公共的跟應用上下文(root application context),這個應用上下文的做用時爲了在多個DispatcherServlet之間共享,這也就是#getRootConfigClasses的做用,用於返回根應用上下文的配置類,Spring框架的機制會保證若是當前DispatcherServlet的應用上下文中沒有找到想要的Bean,就會去根應用上下文中找.
public class SpittrWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
    //將DispatcherServlet映射到"/"
    @Override
    protected String[] getServletMappings(){
        return new String[]{"/"};
    }

    //配置root applicationContext應用上下文的bean
    @Override
    protected Class<?>[] getRootConfigClasses(){
        return new Class<?>[]{RootConfig.class};
    }

    //配置servlet applicationContext應用上下文的bean
    @Override
    protected Class<?>[] getServletConfigClasses(){
        return new Class<?>[]{Webconfig.class};
    }
}

--------------------------------------------------------------------------------------------------------------------------------------------------------
@Configuration
@EnableWebMvc
@ComponentScan("spitter.web")
public class WebConfig implements WebMvcConfigurer{//Spring5中此接口方法均爲默認方法,替代以前適配器類
    //配置jsp視圖解析器.不然會使用默認的BeanNameViewResolver
    @Bean
    public viewResolver viewResolver(){
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF.views");
        resolver.setSuffix(".jsp");
        return resolver;
    }
    //配置靜態資源處理
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
        configurer.enable();
    }
}

--------------------------------------------------------------------------------------------------------------------------------------------------------
@Configuration
@EnableWebMvc
@ComponentScan(basePackage = {"spitter"},excludeFilters = {
    @Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)
})
public class RootConfig{
}

3.總結

AbStractAnnotationConfigDispatcherServletInitializer就是Spring給咱們經過javaConfig方式自定義DispatcherServlet組件的實現方法,若是須要了解其餘相關內容:框架

  1. Spring容器的相關知識能夠參考:Spring容器及其初始化
  2. SpringMvc自定義配置化相關知識能夠參考:
相關文章
相關標籤/搜索