servlet 容器如何找到 WebApplicationInitializer 实现 [重复]

Posted

技术标签:

【中文标题】servlet 容器如何找到 WebApplicationInitializer 实现 [重复]【英文标题】:How servlet container finds WebApplicationInitializer implementations [duplicate] 【发布时间】:2015-03-23 17:50:28 【问题描述】:

Spring WebApplicationInitializer 提供了一种编程方式来在 Servlet 3.0+ 兼容的 servlet 容器中配置 Spring DispatcherServlet 和 ContextLoaderListener。但它是如何工作的? servlet容器如何找到WebApplicationInitializer的实现,真的是从classpath加载所有类吗?

【问题讨论】:

这不是 servlet 3 中没有 web.xml 的引导问题的重复。这是关于 Spring 的 WebApplicationInitializer。尽管 Spring 建立在 servlet 3 引导之上,但并不相同。 【参考方案1】:

来自文档:

此 SPI 的实现将被自动检测到 SpringServletContainerInitializer ,它本身是自举的 由任何 Servlet 3.0 容器自动执行。看 SpringServletContainerInitializer 或有关此引导的详细信息 机制。

和:

运作机制

SpringServletContainerInitializer

这个类将被加载并 实例化并使其 onStartup 方法由任何调用 容器启动期间符合 Servlet 3.0 的容器假设 spring-web 模块 JAR 存在于类路径中。 这通过 JAR 服务 API @link ServiceLoader#load(Class) 方法检测 spring-web 模块的 META-INF/services/javax.servlet.ServletContainerInitializer 服务提供者配置文件。

查看 http://download.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider JAR 服务 API 文档以及 8.2.4 部分 Servlet 3.0 最终草案规范的完整详细信息。

这一切都在文档中,基本上它是检测 SpringServletContainerInitializer 的 Servlet 规范的一部分,它实现了 ServletContainerInitializer,所以这一切都归结为容器检测这些类的工作。

【讨论】:

我知道 servlet 容器应该找到 ServletContainerInitializer 实现。但是他们是怎么做到的呢?例如tomcat或jetty,它们应该加载所有类吗?我没有看到其他解决方案。如果我有巨大的类路径,那么部署将花费大量时间。 我不认为所有类都被容器急切地加载,因为 servlet 3.0 容器在启动时会扫描捆绑在 WEB-INF\lib 中的 jar 文件,以便找到 ServletContainerInitializer 的实现,一旦它找到它,加载实现类,然后通过在实现上调用 onStartup() 来完成所有引导【参考方案2】:

我假设您了解 java SPI 以及 java.util.ServiceLoader 一种用于加载实现的实用程序类的方式。否则please read it。

但是对于这个答案的简要说明:如果有一个 SPI,这里是 javax.servlet.ServletContainerInitializer , 那么提供者应该实现它,并且必须在库 jar 文件的 META-INF/services/javax.servlet.ServletContainerInitializer 文件中声明实现 - Spring 就是这样一个提供者,并且 在 spring-web*.jar jar 文件中声明它并有一个条目 org.springframework.web.SpringServletContainerInitializer 然后 ServletContainerInitializer 的服务加载器可以加载实现。此加载特定于 ServletContainer 实现

我将针对 tomcat7+ ServletContainer 进行解释。

Tomcat 有 LifecycleListeners,它们将监听生命周期事件,如启动、停止等。 org.apache.catalina.startup.ContextConfig 是这样一个 ServletContext 的启动事件侦听器,它配置该 ServletContext 的属性以及相关的已定义 servlet。

因此,当 tomcat 为 Web 应用程序初始化 ServletContext 时,它会生成这样一个事件,该事件将通知 ContextConfig 的偶数监听方法。然后就会触发 它自己的 processServletContainerInitializers 方法,它扫描 JAR 中的 ServletContainerInitializer 实现。 它通过将这项职责委托给 WebappServiceLoader(Java 的 JAR ServiceLoader 的一种变体)来实现这一点,后者实际上负责从 WEB-INF/ 加载 ServletContainerInitializer 实现。 lib 罐子。

所以作为结论,控制流程应该是这样的。

    Tomcat 初始化 ServletContext

    ContextConfig 收到此上下文启动事件的通知

    服务加载委托给 WebappServiceLoader<ServletContainerInitializer>

    WebappServiceLoader 在 WEB-INF/lib jar 中扫描 文件 META-INF/services/javax.servlet.ServletContainerInitializer 以加载实现

    一旦加载返回步骤 3,ContextConfig 将调用 实现的(这里SpringServletContainerInitializeronStartup 方法将完成其余的事情。

HTH!

【讨论】:

SpringServletContainerInitializer 的 java 文档也非常重要,值得阅读 docs.spring.io/spring/docs/current/javadoc-api/org/… 我通过调试 tomcat 代码发现了这一点...通过将它与示例应用程序进行比较来找出为什么我的 spring boot 应用程序没有在外部 tomcat 中启动.. 似乎不同之处在于我的应用程序没有 spring-web-xxx.jar,所以没有 meta-inf/services/.... maven 引入了 spring-webmvc- 和 spring-boot-100-other-jars,但没有其中有 SPI - 没有任何文档说我们需要将 spring-web 作为依赖项吗?

以上是关于servlet 容器如何找到 WebApplicationInitializer 实现 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

Servlet不是线程安全的。

servlet工作原理

java servlet

面试题: 各种 !=!=未看

Java 知识点

在使用 SpringMVC 时,Spring 容器是如何与 Servlet 容器进行交互的?