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 {
@Override
public 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 {
@Override
public void start(ServletContext servletContext) {
System.out.println("This is my TestWebApplicationInitializer start " + servletContext);
}
}
此时 Spring 就会帮我们完成扫描所有 TestWebApplicationInitializer 接口的实现类,并封装到 Set