Servlet 3.0 中,servlet 3.0 的一个新规范(ServletContainerInitializer)
Tomcat 的 SPI
tomcat 也遵守了了这个规范,所以当 tomcat 启动完成后,会调用实现了 ServletContainerInitializer 接口的 onStartup 方法,来通知标识我已经启动完成了
在 META-INF.services 下创建 javax.servlet.ServletContainerInitializer 文件,并在文件中写明实现了 ServletContainerInitializer 接口的实现类的全限定名

Spring MVC 的 SPI
Spring MVC 也定义了一个 org.springframework.web.SpringServletContainerInitializer 类,实现了 servlet 3.0 的这个新规范
在 META-INF.services 下创建 javax.servlet.ServletContainerInitializer 文件,并在文件中写明实现了 ServletContainerInitializer 接口的实现类的全限定名

@HandlesTypes(WebApplicationInitializer.class)public class SpringServletContainerInitializer implements ServletContainerInitializer {@Overridepublic void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)throws ServletException {// ...}}
然后会扫描找到所有实现了 WebApplicationInitializer 接口的实现类,依次调用其 onStartup 方法
自定义 SPI
所以利用这个机制,我们也可以实现 SPI

// SPI 机制:javax.servlet.ServletContainerInitializer 中会定义这个实现类// @HandlesTypes 会找到指定接口的实现类,封装到 webAppInitializerClasses 入参中@HandlesTypes(TestWebApplicationInitializer.class)public class MyServletContainerInitializer implements ServletContainerInitializer {public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {for (Class<?> webAppInitializerClass : webAppInitializerClasses) {try {// 调用接口方法TestWebApplicationInitializer obj = (TestWebApplicationInitializer) webAppInitializerClass.getDeclaredConstructor().newInstance();obj.start(servletContext);} catch (Exception e) {e.printStackTrace();}}}}public interface TestWebApplicationInitializer {void start(ServletContext servletContext);}public class TestWebApplicationInitializerImpl implements TestWebApplicationInitializer {@Overridepublic void start(ServletContext servletContext) {System.out.println("This is my TestWebApplicationInitializer start " + servletContext);}}
此时 Spring 就会帮我们完成扫描所有 TestWebApplicationInitializer 接口的实现类,并封装到 Set
