How does Spring initialize under the hood ? — Part 1
Who will take responsibility to create Spring Container ?. To answer this question, we will dive deeply into to the most ancient package: java.util
, to reveal the Secret of Spring Framework.
To simplify and keep you on track with what i did, i have removed some unused method as well as some lines of code, of course, it will not affect to our demo. So, if you want to know a full version of these class. Just open your IDE and explore it by yourself. Believe me, it is absolutely amazing to know how they structure a project by using interface and abstract class.
I appreciate every comments which build this topic up. It would be my honor when this topic brings to you some valuable knowledge. And if you observe something interesting about Spring, just inbox or comment below, people are getting smarter by learning, sharing and communicating. It is always the best to learn from each other.
My Contact
- LinkedIn: Phan Hoang Minh Luan
- Facebook: Phan Hoang Minh Luan
- Email: phanhoangminhluan@gmail.com
Warm Up
Main Discussion
Java SPI (Java Service Provider Interface) and ServiceLoader
SPI and ServiceLoader
are two important keywords for creating extensible application. You can enhance your (legacy) application without influences to your original code base. And it uses technique Dependency Injection but for internal purpose like initializing Spring Framework, anything happened behind the scene.
At first, in this section, I will explain about Java SPI first because class ServiceLoader
use SPI to instantiate object.
Java SPI
There are 2 terminologies related to SPI.
- Service Provider Interface: is an interface or abstract class which act as endpoint to the service.
- Service Provider:
- Is specific implementation of the SPI.
- Is configured and identified through a provider configuration file which we put in the resource directoryMETA-INF/services
in file jar. The configuration file name is the fully-qualified name of the SPI and his content is the fully-qualified name of the SPI implementation (Service Provider).
- The only requirement enforced here is that Service Provider classes must have a zero-argument constructor so that they may be instantiated during lookup.
java.util.ServiceLoader
ServiceLoader
class helps you to find, load user service providers by searching on application’s classpath. Thanks to the Runtime’s Service Provider Lookup Mechanism, ServiceLoader
will find any jar file whose folder tree is META-INF/services. File’s name in that folder ( is interface) and file’s content (is implementation of interface) will be used to instantiate object.
For example, in the previous picture, there is a file named javax.servlet.ServletContainerInitializer
(SPI) and the file’s content is org.springframework.web.SpringServletContainerInitializer
(Service Provider). It means when we run application, SpringServletContainerInitializer
will be loaded to initialize Spring Container. And you know, Spring Container is core component of Spring Framework.
We will discuss about ServletContainerInitializer
and SpringServletContainerInitializer
below.
javax.servlet.ServletContainerInitializer
Definition from Oracle Document: interface ServletContainerInitializer
is to allow a library/runtime to be notified of a web application’s startup phase and perform any required programmatic registration of servlet, filter and listener.
For further details, ServletContainerInitializer
takes a vital role. Its method onStartup(Set<Class<?>>, ServletContext)
will be invoked before init()
method of Servlet or contextInitialized()
of ServletListener
. So it is the reason why it can perform any programmatic registration related to initialize another classes.
Implementation of this interface may be annotated with (in this case) @HandlesTypes(WebApplicationInitializer.class)
. The role of @HandlesTypes
is to look for in the application’s classpath to find any class which is a subclass of WebApplicationInitializer
. Then theses class will be passed as parameter of method onStartUp(Set<Class<?>>, ServletContext)
Moreover, any class implement ServletContainerInitializer must be declared by JAR file resource located inside the META-INF/services
directory. I have explained the reason on above section.
org.springframework.web.SpringServletContainerIntializer
It is very important to annotated @HandlesTypes()
. Without it, console will notify “No Spring WebApplicationInitializer types detected on classpath” and onStartup(Set<Class<?>> , ServletContext)
method will over.
In this method:
First, onStartUp()
method receive 2 params:
- Set<Class<?>> webApplicationIntializerClasses: a collection of sub classes whose parent is
WebApplicationInitializer
(because this method is annotated with@HandlesTypes(WebApplicationInitializer.class))
- ServletContext: Root Application Context (in Spring) of this application
Then, a LinkedList of WebApplicationInitializer
is instantiate for storing classes which are going to instantiate and invoke onStartup(ServletContext)
.
webApplicationIntializerClasses
will be traversed. A suitable class is:
- Not an interface:
!waiClass.isInterface()
- Not a abstract class:
!Modifier.isAbstract(waiClass.getModifiers())
- A subclass of WebApplicationInitializer:
WebApplicationInitializer.class.isAssignableFrom(waiClass)
If a class passes all of these above conditions, it will be instantiate through ReflectionUtils.accessibleConstructor(waiClass).newInstance()
before add to linked list.
For further details about ReflectionUtils.accessibleConstructor(waiClass).newInstance()
. It will access to constructor of waiClass and then new with keyword .newInstance()
. That is the reason why Service Provider can not have argument constructor.
Then, a LinkedList will be sorted based on @Order()
annotation. If you want to set initial order of these WebApplicationInitializer. You can decorate @Order()
at these class.
Finally, the LinkedList will be traversed and each item will invoked onStartup(ServletContext)
.
Let swing back to my previous topic. Did you remember there is a method named onStartup(ServletContext)
of WebApplicationInitializer
?.
Now, the truth have been revealed, finally, a linked list of WebApplicationInitializer
will be traversed and each of instance will invoke onStartup(ServletContext)
method and pass ServletContext
as parameter.
Explanation
At first, ServiceLoader
will scan all of exist jar file on application classpath based on Runtime’s Service Provider Lookup Mechanism, i will assume that it found spring-web.jar
whose directory form is META-INF/services/javax.servlet.ServletContainerInitializer
. Then, ServiceLoader
with static method load(Class<S> service
) will load interface ServletContainerInitializer
with its implementation and onStartup(Set<Class<?>> c, ServletContext)
is invoked. And then, onStartup(ServletContext)
of subclasses of WebApplicationInitializer
will be invoked in order.