Spring 去除 web.xml,使用 Java 配置 Servlet 的原理,SPI

本贴最后更新于 2076 天前,其中的信息可能已经水流花落

Spring 去除 web.xml,使用 Java 配置 Servlet 的原理,SPI

1. spring 使用 java 配置 Servlet 代码如下

import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; import javax.servlet.ServletContext; import javax.servlet.ServletRegistration; public class WebServletConfiguration implements WebApplicationInitializer { @Override public void onStartup(ServletContext ctx) { AnnotationConfigWebApplicationContext webCtx = new AnnotationConfigWebApplicationContext(); // 注册context webCtx.register(ApplicationConfig.class); // 设置context webCtx.setServletContext(ctx); // 定义Servlet ServletRegistration.Dynamic servlet = ctx.addServlet("spring", new DispatcherServlet(webCtx)); servlet.setLoadOnStartup(1); servlet.addMapping("/"); } }

这段代码配置了一个名为 spring 的 Servlet, 所有的请求(/)都映射到这个 Servlet 中。

2. spring 的源码

在上一点中,可以看到关键的代码是现实了 WebApplicationInitializer 这个接口,很容易的找到这个接口唯一被引用的地方,即 org.springframework.web.SpringServletContainerInitializer

// HandlesTypes注解再这里的作用是tomcat回去扫描所有的WebApplicationInitializer的实现类,包括抽象类。 @HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override // webAppInitializerClasses参数返回的就是所有的WebApplicationInitializer的实现类 public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List<WebApplicationInitializer> initializers = new LinkedList<>(); if (webAppInitializerClasses != null) { for (Class<?> waiClass : webAppInitializerClasses) { // Be defensive: Some servlet containers provide us with invalid classes, // no matter what @HandlesTypes says... if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { initializers.add((WebApplicationInitializer) ReflectionUtils.accessibleConstructor(waiClass).newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } } } } if (initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); return; } servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); // 传入servletContext给WebApplicationInitializer的实现类,并调用onStartup方法,此方法又定义Servlet for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } } }

3. tomcat 源码

继续根据实现的接口 ServletContainerInitializer 追溯到 tomcat 源码中。
org.apache.catalina.startup.ContextConfig#processServletContainerInitializers 方法中对 ServletContainerInitializer 的实现类进行了扫描,也就是扫描到了 SpringServletContainerInitializer

/** * Scan JARs for ServletContainerInitializer implementations. */ protected void processServletContainerInitializers() { List<ServletContainerInitializer> detectedScis; try { WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context); detectedScis = loader.load(ServletContainerInitializer.class); } catch (IOException e) { log.error(sm.getString( "contextConfig.servletContainerInitializerFail", context.getName()), e); ok = false; return; } // 省略一堆源码 }

到这里需要知道 SPI(Service Provider Interface),简单的说就是一个服务发现的解决方案,这个方案有 JDK 的实现和 tomcat 的实现,上面源码中的 WebappServiceLoader 就是 tomcat 的实现。
SPI 中不得不说的配置文件就是 spring-web 包下 META-INFO/services 目录下的 javax.servlet.ServletContainerInitializer 文件,内容如下

org.springframework.web.SpringServletContainerInitializer

原理是,文件名称是服务发现的接口全路径名称,内容是接口实现类的全路径名称。SPI 会创建这个实现类的实例,并通过接口调用 org.springframework.web.SpringServletContainerInitializer#onStartup 方法。

4.总结

  1. 整个过程就不再追溯到 tomcat 的再往上的源码了,我们已经可以了解到 tomcat 和 spring 是怎么样实现了不使用 web.xml 配置 Servlet 的了。
  2. 如果有继续想要了解 JDK 的 SPI 实现,可以看下 java.util.ServiceLoader
  3. 如果看完 spring 的这个实现还不是很懂,可以看 logback 日志框架的 ch.qos.logback.classic.servlet.LogbackServletContainerInitializer 实现。
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    946 引用 • 1460 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...