Servlet 3.0 中,servlet 3.0 的一个新规范(ServletContainerInitializer)

Tomcat 的 SPI

tomcat 也遵守了了这个规范,所以当 tomcat 启动完成后,会调用实现了 ServletContainerInitializer 接口的 onStartup 方法,来通知标识我已经启动完成了

在 META-INF.services 下创建 javax.servlet.ServletContainerInitializer 文件,并在文件中写明实现了 ServletContainerInitializer 接口的实现类的全限定名

image.png

Spring MVC 的 SPI

Spring MVC 也定义了一个 org.springframework.web.SpringServletContainerInitializer 类,实现了 servlet 3.0 的这个新规范

在 META-INF.services 下创建 javax.servlet.ServletContainerInitializer 文件,并在文件中写明实现了 ServletContainerInitializer 接口的实现类的全限定名

image.png

  1. @HandlesTypes(WebApplicationInitializer.class)
  2. public class SpringServletContainerInitializer implements ServletContainerInitializer {
  3. @Override
  4. public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
  5. throws ServletException {
  6. // ...
  7. }
  8. }

然后会扫描找到所有实现了 WebApplicationInitializer 接口的实现类,依次调用其 onStartup 方法

自定义 SPI

所以利用这个机制,我们也可以实现 SPI

image.png

  1. // SPI 机制:javax.servlet.ServletContainerInitializer 中会定义这个实现类
  2. // @HandlesTypes 会找到指定接口的实现类,封装到 webAppInitializerClasses 入参中
  3. @HandlesTypes(TestWebApplicationInitializer.class)
  4. public class MyServletContainerInitializer implements ServletContainerInitializer {
  5. public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
  6. for (Class<?> webAppInitializerClass : webAppInitializerClasses) {
  7. try {
  8. // 调用接口方法
  9. TestWebApplicationInitializer obj = (TestWebApplicationInitializer) webAppInitializerClass.getDeclaredConstructor().newInstance();
  10. obj.start(servletContext);
  11. } catch (Exception e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. }
  16. }
  17. public interface TestWebApplicationInitializer {
  18. void start(ServletContext servletContext);
  19. }
  20. public class TestWebApplicationInitializerImpl implements TestWebApplicationInitializer {
  21. @Override
  22. public void start(ServletContext servletContext) {
  23. System.out.println("This is my TestWebApplicationInitializer start " + servletContext);
  24. }
  25. }

此时 Spring 就会帮我们完成扫描所有 TestWebApplicationInitializer 接口的实现类,并封装到 Set> webAppInitializerClasses 中去