DispatcherServlet
is the brain of SpringMVC, it is responsible for the whole SpringMVC dispatching work, it is the most core class in SpringMVC, the whole top-level architecture design of SpringMVC is reflected here. So if you understand the source code of DispatcherServlet
, you will have a good idea of how SpringMVC works.
However, DispatcherServlet
inherits from FrameworkServlet, FrameworkServlet inherits from HttpServletBean, as follows.
So our analysis starts with the HttpServletBean
.
1. HttpServletBean
HttpServletBean
inherits from HttpServlet
, it is responsible for injecting the parameters in init-param
into the properties of the current Servlet
instance, and also provides the ability to add requiredProperties
to the subclasses, it should be noted that HttpServletBean
does not depend on the Spring
container.
As you know, HttpServlet initialization starts with the init method, so let’s start with the HttpServletBean’s init method.
|
|
In this method, all the configuration of the Servlet is first obtained and converted to PropertyValues
, and then the relevant properties of the target Servlet are modified through the BeanWrapper
, a tool provided in Spring that can be used to modify the properties of an object, like the following.
|
|
So the bw in front actually represents the current DispatcherServlet object.
When modifying the properties of the target Servlet through BeanWrapper, there is an initBeanWrapper method that is empty. The developer can implement this method in a subclass if needed and perform some initialization operations.
After the properties are configured, the initServletBean method is finally called to initialize the Servlet. However, this method is also an empty method, which is implemented by the subclass.
This is what HttpServletBean does, it is relatively simple, load the Servlet related properties and set them to the current Servlet object, then call the initServletBean method to continue to complete the Servlet initialization operation.
2. FrameworkServlet
From the previous introduction, we know that the entry method of FrameworkServlet
initialization is initServletBean, so let’s start from FrameworkServlet#initServletBean
method.
This method is originally quite long, but leaving aside printing logs, exception handling, etc., the remaining core code is actually just two lines.
initWebApplicationContext
method is used to initialize the WebApplicationContext.initFrameworkServlet
method is used to initialize the FrameworkServlet, but this method is an empty method, no specific implementation. Originally subclasses can override the method to do some initialization operations, but in fact, subclasses do not override the method, so we will ignore this method for the time being, not to analyze.
So the most important thing here is actually initWebApplicationContext
method, let’s look at it together.
|
|
The logic here is also relatively clear.
-
By default, Spring sets the container as an attribute of the ServletContext with the key
org.springframework.web.context.WebApplicationContext.ROOT
, so based on this key, you can callServletContext#getAttribute
method to get the rootContext. -
Get the WebApplicationContext instance, which is the process of assigning a value to the wac variable, there are three possibilities here.
- If you have already assigned a value to the webApplicationContext through the constructor method, you will directly assign it to the wac variable, and at the same time, if you need to set the parent on the set, need to refresh on the refresh. This approach is applicable to the environment after Servlet3.0, because from Servlet3.0 onwards, only to support direct calls to ServletContext.addServlet method to register Servlet, manual registration can use their own WebApplicationContext prepared in advance.
- If the first step does not succeed in assigning a value to wac, then call the findWebApplicationContext method to try to find the WebApplicationContext object in the ServletContext, and assign a value to wac when you find it.
- If the second step does not succeed in assigning a value to wac, then call the createWebApplicationContext method to create a WebApplicationContext object and assign it to wac. generally speaking, the WebApplicationContext is created in this way.
After these three steps, wac must have a value.
-
When the ContextRefreshedEvent event is not triggered, call the onRefresh method to finish refreshing the container.
Since the first and third ways of getting WebApplicationContext will eventually call configureAndRefreshWebApplicationContext method, then publish the event, and then mark the refreshEventReceived variable as true, so actually only the When the second way to get the wac instance, it will be refreshed here, see the analysis below.
-
Finally, wac will be saved to the ServletContext. Save will be based on the value of the publishContext variable to decide whether to save, publishContext can be configured in the web.xml when configuring the Servlet through init-param, save the purpose is to facilitate access.
Among the above steps, the createWebApplicationContext
method that creates the WebApplicationContext
object needs to be explained in detail, because this is how the WebApplicationContext
is usually created.
Let’s take a look at the related methods.
|
|
There are two methods involved here.
createWebApplicationContext
First get the creation type, and check the creation type, if there is no problem call instantiateClass method to complete the creation work. Then configure various properties for the created wac object. The configLocation is the path to the SpringMVC configuration file we configured in the web.xml file, the default file path is /WEB-INF/[servletName]-servlet.xml
.
configureAndRefreshWebApplicationContext
configureAndRefreshWebApplicationContext
method is also mainly to configure & refresh the WebApplicationContext
, in this method will call addApplicationListener to add a listener for wac, listening to the ContextRefreshedEvent event, when the event is received, the onApplicationEvent
method of the FrameworkServlet
will be called, and the onRefresh
method will be called in this method to finish refreshing, after refreshing, the refreshEventReceived
variable will be marked as After the refresh, the refreshEventReceived
variable will be marked as true.
This is the general logic of how the FrameworkServlet#initServletBean
method works. The onRefresh
method is involved here, but it is an empty method that is implemented in the subclass DispatcherServlet
, so let’s look at DispatcherServlet
next.
3. DispatcherServlet
Let’s cut the crap here and go straight to the onRefresh method, as follows.
|
|
initStrategies
is called in the onRefresh
method for initialization. initStrategies is actually very simple, it is the initialization of nine components. The initialization process of the nine is relatively similar, so let’s take the common view resolver initialization method initViewResolvers
as an example and look at the initialization process together.
|
|
The viewResolvers variable at the beginning is a collection into which the parsed view resolver objects will be placed.
The first step is to determine if the detectAllViewResolvers variable is true, and if it is, then it will look up all the view resolvers in the Spring container, assign the result to the viewResolvers, and then sort them. By default the value of the detectAllViewResolvers variable is true, but if needed, it can be configured in web.xml, like this.
|
|
If the value of detectAllViewResolvers
is false, then the Spring container will look for a view resolver named viewResolver, which is a separate view resolver.
In general, we don’t need to configure the value of detectAllViewResolvers
in web.xml
, we load as many view resolvers as there are.
As a simple example, we might configure a view resolver in the SpringMVC configuration file like this.
By default, the bean’s id is optional. If it does, it can take any value, because ultimately the view resolver will look for it by type, not by id. But if you change detectAllViewResolvers
to false
in web.xml
, then the value of the bean’s id is more important and must be viewResolver
.
If no ViewResolver
instance is found in the Spring container in either of these two ways (by type or by id), then the getDefaultStrategies
method will be called to get a default ViewResolver
instance. The default instance is obtained in the following way.
|
|
This code is actually relatively simple, it is to get the default view parser through reflection.
First assign a value to defaultStrategies
, the value of defaultStrategies
is actually loaded from the DispatcherServlet.properties
file, let’s look at the contents of this file.
As you can see, there are 8 default key-value pairs defined here, some with one value and some with multiple values. The initStrategies method in the previous section initializes nine components, but only eight are defined here by default, one MultipartResolver
is missing.
This is understandable, as not all projects have file uploads. And even if there are file uploads, it is not easy to determine which specific MultipartResolver
to use, so it is up to the developer to decide.
defaultStrategies actually loads these 8 key-value pairs, where the view resolver corresponds to org.springframework.web.servlet.view.InternalResourceViewResolver
. When no view resolver exists in the Spring container, the default view resolver is this.
This is the workflow of initViewResolvers
, the other 8 are similar to it, the only difference is initMultipartResolver
, as follows.
As you can see, it only looks up the bean instance based on the bean name, not the default MultipartResolver
.
Speaking of which, let’s talk about a small detail in the SpringMVC configuration.
|
|
The above configuration of the view parser and file upload parser, I wonder if you have noticed that the id of the view parser can be optional, while the id of the file upload parser must be multipartResolver, review our source code analysis above, you will know why!
4. Summary
Well, this is the initialization process of SpringMVC, which mainly involves HttpServletBean
, FrameworkServlet
and DispatcherServlet
instances. HttpServletBean
is mainly to load the various properties of the Servlet configuration and set to the Servlet. FrameworkServlet
is mainly to initialize the WebApplicationContext
. DispatcherServlet
initializes nine of its own components.
Reference: https://mp.weixin.qq.com/s/JImMPTGzHjGPRnRJlQlDhg