1. spring整合springmvc早期是通过配置web.xml配置前端控制器、监听器
2. 基于Java Config的方式,就是利用SPI机制
什么是SPI机制?工程演示见GitHub
① Tomcat 作为服务者,提供 javax.servlet.ServletContainerInitializer 这个接口
② 应用程序只要实现这个接口,并且在对应目录下添加配置文件。在启动Tomcat时,Tomcat就会调用实现该接口的onStartup()方法
③ spring-web 项目实现SPI机制,详见 SpringServletContainerInitializer 这个类
3.SpringServletContainerInitializer 源码分析
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
public SpringServletContainerInitializer() {
}
public void onStartup(@Nullable 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)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).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);
}
}
}
}
① @HandlesTypes({WebApplicationInitializer.class}),这个注解的作用会自动扫描所有实现WebApplicationInitializer接口的子类,作为onStartup()方法入参,即对应的webAppInitializerClasses
② 如果子类不是抽象类的、也不是接口,就会实例化。然后调用onStartup()方法,所以程序只要实现Spring提供的WebApplicationInitializer接口,就会在tomcat启动时,调用子类的onStartup()方法
4.onStartup方法源码分析
① 应用程序实现WebApplicationInitializer接口,如果没有重写onStartup方法,就会调用父类AbstractDispatcherServletInitializer的
② AbstractDispatcherServletInitializer的onStartup方法,源码分析
- 再调用它父类AbstractContextLoaderInitializer的onStartup方法:
- 创建spring父容器,设置扫描的配置类(应用程序重写该方法,设置父容器扫描的配置类)
- 创建ContextLoaderListener监听器,对父容器进行封装
- 将监听器设置到servletContext
- 调用本类的方法,注册DispatcherServlet:
- 创建springmvc子容器,设置扫描的配置类(应用程序重写该方法,设置子容器扫描的配置类。在配置类上添加@EnableWebMvc,利用SpringBoot自动装配机制就会自动注册springmvc的相关组件)
- 创建 DispatcherServlet,对子容器进行封装
- 将DispatcherServle设置到servletContext
- 设置启动时立即加载,设置DispatcherServlet的过滤器