1. 入口: SpringServletContainerInitializer :
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer
原理及过程
- 根据Servlet3+的规定,Servlet容器将自动扫描并加载实现了ServletContainerInitializer接口的类,然后调用onStartup方法,并且会传入@HandlesTypes注解参数指定的类型(Class实例);
- SpringMVC定义了WebApplicationInitializer接口,并在@HandlesTypes注解中声明,因此Servlet容器会扫描到所有WebApplicationInitializer类形,并在onStartup方法中传入该类集合及ServletContext;
- onStartup方法中,过滤掉接口和抽象类后,调用该Class的newInstance方法生成实例,并放入集合;
- 排序后,依次调用WebApplicationInitializer实例的onStartup方法,并传入ServletContext;
- SpringMVC为WebApplicationInitializer提供了很多抽象实现类,用户使用时,只需继承其中之一即可,或者也可以自己从头实现,并在onStartup方法中完成DispatcherServlet的注册或自己的初始化动作等等;
- AbstractAnnotationConfigDispatcherServletInitializer是其中比较常用且好用的一个,用户只需继承它,且实现三个抽象方法,提供根容器配置类集合、ServletConfig配置类集合、HTTP访问映射根路径;
2. 初始化器: WebApplicationInitializer
继承结构
AbstractAnnotationConfigDispatcherServletInitializer-> AbstractDispatcherServletInitializer-> AbstractContextLoaderInitializer---> WebApplicationInitializer
- AbstractContextLoaderInitializer:
- 直接实现WebApplicationInitializer,onStartup方法中只调用了registerContextLoaderListener,注册一个上下文监听器;
- 创建一个根容器,WebApplicationContext的实例,创建方法是模板方法,具体由子类实现;
- 创建一个监听器ContextLoaderListener实例,传入根容器;
- 注:IOC根容器的真正初始化就在监听器中,contextInitialized->initWebApplicationContext->configureAndRefreshWebApplicationContext,最终会调用容器的refresh()方法完成初始化[见spring-web-4.3.3,源码ContextLoader类,444行,wac.refresh();]
- listener.setContextInitializers什么都没干,因为getRootApplicationContextInitializers()返回null;
- 将监听器添加到ServletContext中;
- AbstractDispatcherServletInitializer
- 重写onStartup方法,除调用父类onStartup方法外,添加一行调用:registerDispatcherServlet,用于注册DispatcherServlet;
- 创建DispatcherServlet,返回类型为父类FrameworkServlet;
- 创建注册器并设置映射路径(模板方法,由用户实现的子类提供)、立即加载、是否异步等,下面是代码;
- 获取并注册Filter(截止4.3.3,getServletFilters()总是返回空);
- 最后调用模板方法 customizeRegistration(ServletRegistration.Dynamic registration), 注:该方法并未实现;
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
- AbstractAnnotationConfigDispatcherServletInitializer
实现的模板方法createRootApplicationContext
、createServletApplicationContext
,用于创建IOC的根容器及WEB容器(子容器),并提供新的模板方法protected abstract Class<?>[] getRootConfigClasses();
、protected abstract Class<?>[] getServletConfigClasses();
,让用户实现,以提供配置类和ServletConfig配置类- createRootApplicationContext: 通过模板方法getRootConfigClasses获取配置类,然后new一个AnnotationConfigWebApplicationContext实例,然后调用其register方法完成配置类的注册,最后返回该实例;
- createServletApplicationContext: 直接new一个AnnotationConfigWebApplicationContext对象做为子容器,然后通过模板方法getServletConfigClasses()获取配置类数组,遍历数组,注册到子容器中等待初始化;
- 最后,用户继承AbstractAnnotationConfigDispatcherServletInitializer,实现三个模板方法,提供配置类、http访问映射根路径等;
总结
springMVC的初始化有两条线:
- ContextLoaderListener监听器,在其初始化方法中实现根容器的初始化;
- DispatcherServlet中的init方法,最终实现子容器的初始化;
回想之前web.xml中配置spring及springmvc,先配置一个监听器,再配置一个Servlet,即springmvc的唯一实现DispatcherServlet; 注解+javaConfig的方式也是一样的,只不过借助于servlet3+的特性,省去了web.xml的配置,并在框架代码中实现的监听器及Servlet的注册,用户只需要提供配置类,在配置类中标注注解以及定义Bean即可完成之前由web.xml+spring.xml实现的配置,简化了开发,并且更方便扩展,比如可以编写读取远程配置的代码,并在配置类中提供调用方法,然后标注一个@Bean即可实现集中化、动态化的配置;