11

Spring WebApplicationInitializer provides a programatic way to configure the Spring DispatcherServlet and ContextLoaderListener in Servlet 3.0+ compliant servlet containers. But how does it work? How servlet container finds WebApplicationInitializer implementations, is it really load all classes from classpath?

Anton Ilinchik
  • 221
  • 2
  • 7
  • This is not a duplicate of the question on bootstrapping without a web.xml in servlet 3. This is about Spring's WebApplicationInitializer. Although Spring builds on the servlet 3 bootstrapping, it is not the same. – Catweazle Oct 31 '18 at 14:37

2 Answers2

13

I assume you have knowledge about java SPI and the way java.util.ServiceLoader a utility class used to load implementations. Else please read it.

But in brief for the this answer just understand : if there is one SPI, here javax.servlet.ServletContainerInitializer , then the provider should implement it and implementation must be declared in a META-INF/services/javax.servlet.ServletContainerInitializer file of the libraries jar file - Spring is such a provider and declares this in spring-web*.jar jar file and has an entry org.springframework.web.SpringServletContainerInitializer Then the service loader for ServletContainerInitializer can load the implementation. This loading is specific to ServletContainer implementation

I will explain this specific to tomcat7+ ServletContainer.

Tomcat has LifecycleListeners those will listen to lifecycle events like start,stop etc. org.apache.catalina.startup.ContextConfig is such a startup event listener for a ServletContext that configures the properties of that ServletContext, and the associated defined servlets.

So while tomcat initilizes a ServletContext for a web application, it will generate such an event which will notify the ContextConfig's even listening method. Then it will trigger its own processServletContainerInitializers method, which scan JARs for ServletContainerInitializer implementations. It does this via delegating this duty to WebappServiceLoader (a variation of Java's JAR ServiceLoader), who is actually responsible for the loading of ServletContainerInitializer implementaions from WEB-INF/lib jars.

So as conclusion the control flow would be like this.

  1. Tomcat initialize the ServletContext

  2. ContextConfig is notified with this context startup event

  3. service loading is delegated to WebappServiceLoader<ServletContainerInitializer>

  4. WebappServiceLoader scans in WEB-INF/lib jars for the file META-INF/services/javax.servlet.ServletContainerInitializer inorder to load the implementation

  5. once loaded return to step 3 and, ContextConfig will call implementation's (here SpringServletContainerInitializer) onStartup method which will do rest of the things.

HTH!

Tom Sebastian
  • 3,373
  • 5
  • 29
  • 54
  • The java doc for `SpringServletContainerInitializer` is very important to be read as well https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/SpringServletContainerInitializer.html – Muhammad Hewedy Mar 18 '19 at 06:22
  • I found this out the hard way by debugging into tomcat code... to find out why my spring boot app doesn't start in external tomcat by comparing it with an example app.. what seems to be the difference is that my app has no spring-web-xxx.jar, so no meta-inf/services/.... maven has pulled in spring-webmvc- and spring-boot-100-other-jars, but none of them has the SPI -- there's no documentation anywhere that says we need to have spring-web as a dependency is there? – Rhubarb Feb 16 '20 at 20:15
2

From docs:

Implementations of this SPI will be detected automatically by
SpringServletContainerInitializer , which itself is bootstrapped automatically by any Servlet 3.0 container. See SpringServletContainerInitializer or details on this bootstrapping mechanism.

and:

Mechanism of Operation

SpringServletContainerInitializer

This class will be loaded and instantiated and have its onStartup method invoked by any Servlet 3.0-compliant container during container startup assuming
that the spring-web module JAR is present on the classpath. This occurs through the JAR Services API {@link ServiceLoader#load(Class)} method detecting the spring-web module's META-INF/services/javax.servlet.ServletContainerInitializer
service provider configuration file.

See the http://download.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider JAR Services API documentation as well as section 8.2.4 of the Servlet 3.0 Final Draft specification for complete details.

It is all in docs basically it's part of Servlet specs to detect SpringServletContainerInitializer which implements ServletContainerInitializer so its all down to container doing its job detecting these classes.

mariubog
  • 1,498
  • 15
  • 16
  • I understand that servlet containers should find ServletContainerInitializer implementations. But how do they do it? For example tomcat or jetty, they should load all classes? I don`t see other solution. And if I have huge classpath then deployment would take a lot of time. – Anton Ilinchik Jan 25 '15 at 20:14
  • I do not think all classe are loaded eagerly by container, since servlet 3.0 container scans jar files bundled in the WEB-INF\lib at startup in order to find implementations of the `ServletContainerInitializer`, once it finds it, implementing class is loaded and than all bootstraping is done by calling onStartup()on the implementation – mariubog Jan 25 '15 at 21:23