Today, I will try to analyze the initialization process of Spring MVC from the source level, and uncover the real veil of Spring MVC together. Maybe we have learned to use Spring MVC, or the principle of Spring MVC has been able to recite it backwards in theory. This may require you to have some basic knowledge of Java EE before you start. For example, we need to learn Java EE’s Servlet specification first, since the Spring MVC framework implementation follows the Servlet specification at the bottom.

Before starting the source code analysis, we may need a simple case project, not to panic, the small editor has arranged:

Sample project download address: github.com/SmallerCode…

So let’s get started!

First, pre-knowledge

As you all know, when we use Spring MVC we usually do the following configuration in the web.xml file:

web.xml

<? xml version="1.0" encoding="UTF-8"? > <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <! -- Context parameter, <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext.xml </param-value> </context-param> <! - the listener configuration - > < listener > < listener - class > org. Springframework. Web. Context. ContextLoaderListener < / listener - class > </listener> <! <servlet> <servlet-name> </servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>Copy the code

The above configuration is summarized as follows:

  • 1. Configure the Spring Web Context listener, which is also the starting point for Spring MVC. Why
  • 2. Front-end controllerDispatcherServletThe controller is the entry and handler for Spring MVC to handle various requests

When we deploy the Spring MVC application to Tomcat, if you configure a DispatcherServlet without any context-param and listener parameters, Tomcat will not initialize the Spring Web context at startup. In other words, Tomcat doesn’t initialize the Spring framework because you don’t tell them where to put the Spring configuration file and how to load it. So the listener does this for us, so why configure the listener and then tell Tomcat how to load it? Because the listener is a listener component that implements the servlet technical specification, Tomcat will first load whether servlet listeners exist in web.xml and start them if they exist. ContextLoaderListener is a spring framework wrapper around a servlet listener, and is essentially a servlet listener, so it will be executed. ContextConfigLocation, contextClass, contextClass, ContextLoaderListener, contextConfigLocation, contextClass, contextConfigLocation, contextClass The same goes for the initialization parameter in the servlet tag, which tells the Web server to initialize the Spring WebApplicationContext at startup.

The general process of Tomcat loading Spring MVC application is described above. Next, the startup principle will be analyzed from the source code.

Spring MVC Web context startup source code analysis

If we remove the

1
from the web. XML file above, tomcat will only initialize the Spring Web context by default. The applicationContext. XML file is loaded. The applicationContext-mVC.xml configuration file is not loaded.

1
means that the DispatcherServlet is delayed to initialize until it is used (i.e. when the request is being processed).

We already know that Spring Web is packaged based on the Servlet standard, so it is obvious that the WebApplicationContextweb context should be initialized the same way the servlet is initialized. Let’s first look at the ContextLoaderListener source.

Public Class ContextLoaderListener extends ContextLoader implements ServletContextListener {// Initialize method @override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); Public void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); }}Copy the code

The ContextLoaderListener class implements the ServletContextListener, which is essentially a servlet listener. Tomcat will load the Servlet listener component first. And call the contextInitialized method, contextInitialized method invokes the initWebApplicationContext method initializes the Spring web context, see the fresh, original Spring MVC entry right here, Ha ha ~ ~ ~ hurry to enter initWebApplicationContext method and have a look!

InitWebApplicationContext () method:

// Create a Web context, XmlWebApplicationContext by defaultif (this.context == null) {
    this.context = createWebApplicationContext(servletContext);
}

if(this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; // If the container has not been refreshed yetif(! cwac.isActive()) {if(cwac.getParent() == null) { ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } / / configuration and refresh container configureAndRefreshWebApplicationContext (cwac, servletContext); }}Copy the code

The above method does only two things:

  • 1. If the Spring Web container is not already created, create a new Spring Web container that is the root container on which the Servlet Spring Web container described in section 3 below is created
  • 2. Configure and refresh the container

The above code comments when it comes to default to create the context of the container is XmlWebApplicationContext, why not other web context? Why not either of the following contexts?

After we can go in with createWebApplicationContext can be found from a group called ContextLoader. By default the properties file loading configuration, the contents of the file as follows:

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
Copy the code

Concrete implementation is as follows:

protected Class<? > determineContextClass(ServletContext ServletContext) {// Custom context, Otherwise, the default create XmlWebApplicationContext String contextClassName = servletContext. GetInitParameter (CONTEXT_CLASS_PARAM);if(contextClassName ! = null) { try {return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
        }
        catch (ClassNotFoundException ex) {
        	throw new ApplicationContextException(
        			"Failed to load custom context class [" + contextClassName + "]", ex); }}else{// Load the class name from the properties file, Namely org. Springframework. Web. Context. Support. The XmlWebApplicationContext contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try {return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
        }
        catch (ClassNotFoundException ex) {
        	throw new ApplicationContextException(
        			"Failed to load default context class [" + contextClassName + "]", ex); }}}Copy the code

As you can see above, we can also customize the Spring Web context, so how to specify our custom context? The contextClass parameter and the contextConfigLocation parameter are important. The contextConfigLocation parameter is contextConfigLocation. We follow up configureAndRefreshWebApplicationContext can see, the diagram below:

Conclusion:

The Spring MVC startup process roughly starts with a ContextLoaderListener, which is a servlet listener that can be found and loaded by the Web container. After initializing the ContextLoaderListener, ContextConfigLocation and contextClass. If you do not specify contextClass, The default created spring web container type for the XmlWebApplicationContext, the final step is according to your configuration contextConfigLocation file path to the configuration and refresh the container.

Three,DispatcherServletInitialization of the controller

Ok, we simply analyzed the above Spring MVC container initialization of the source code, we will never forget, we created by default container types as the XmlWebApplicationContext, of course, we will not forget, in the web. XML, we also have a important configuration, That’s the DispatcherServlet. Let’s examine the initialization process of DispatcherServlet.

The DispatcherServlet is a servlet that handles requests. It’s the heart of Spring MVC. All requests go through it and it tells you what to do next. Before we continue, we should all follow the common sense that ——-, whether listeners or servlets, are servlet specification components that web servers can find and load.

Let’s take a look at the DispatcherServlet inheritance:

DispatcherServlet inherits the HttpServlet class. HttpServlet is a servlet specification specifically for handling HTTP requests. This is why Spring MVC uses DispatcherServlet as a unified request entry.

Since the life cycle of a servlet is init()->service()->destory(), how does a DispatcherServlet initialize? Looking at the inheritance diagram above, let’s dive into the HttpServletBean.

Sure enough, there is an init() method in the HttpServletBean class. HttpServletBean is an abstract class, and init() looks like this:

As you can see, final methods are not inherited by subclasses, i.e. subclasses do not have the same init() method, which is the initialization entry of the DispatcherServlet.

Next we follow the initServletBean() method on the FrameworkServlet:

In the method, you will initialize a Web container that is different from the one in section 1. Remember that this new Spring Web container is specifically for dispactherServlet, and that this new container is created based on the ROOT container in Section 1. The initialization parameters we configured in the

tag are added to the new container.

At this point, initialization of the DispatcherSevlet is complete, which sounds a little deceiving, but it really is, and the above analysis only revolves around a method called init() that all servlet initializations call.

Conclusion:

Initialization of the dispactherServlet does two things. The first thing is to create a Web container specifically for the dispactherServlet based on the root Web container, the XmlWebApplicationContext we created in section 1. The second thing you need to do is load the configuration you did for dispactherServlet in the web.xml file into the new container.

What process does each request call go through

This is the heart of the dispatcherServlet controller, because the Web framework is all about accepting requests, processing requests, and responding to requests. Of course, dispactherservlets that simply accept processing and respond to requests are too weak, so spring designers have added many new features, There are interceptors, message converters, request processing mappers, and various resolvers, so Spring MVC is very powerful.

The dispatcherServlet class does not do source analysis because it is a fixed execution step. A request comes in and goes something like this:

Accept requests —–> Whether there are various processors Handler ——-> Whether there are message converters HandlerAdapter ——–> Respond to requests

Each of these steps, if there is a component, will execute your configured component and then respond to the request. So if you want to debug a request, you can just break the doDispatch method of the dispatcherServlet class and you’ll see that the process is pretty much the same.

Fourth, the latter

The project in this article is based on the traditional web. XML loading Web project, of course in Spring MVC we can also configure it completely based on annotations. We can realize WebApplicationInitializer to create your own web starter, can also be created through the use of the inheritance AbstractAnnotationConfigDispatcherServletInitializer corresponding spring Web container (including the above said to the root of the container and servlet web container), the final step again through inheritance WebMvcConfigurationSupport custom configurations (related interceptors, beans, etc.)

Thank you for reading and I look forward to working with you. Feel free to comment below