1 Spring MVC WEB configuration

The Spring Framework itself does not have Web functionality. Spring MVC extends ApplicationContext with the WebApplicationContext class to have Web functionality. So how does Spring MVC create an IoC container in a Web environment? What is the structure of the IoC container in a Web environment? How does the Spring IoC container start in a Web environment?

Taking Tomcat as an example, to use Spirng MVC in a Web container, four configurations must be carried out:

  1. Modify web.xml to add servlet definition;
  2. Write a servletname-servlet. XML configuration (servletName is the value of servlet-name when DispactherServlet is configured in web.xm);
  3. ContextConfigLocation Initializes the parameter and configures ContextLoaderListerner.

The web.xml configuration is as follows:

    <! Servlet definition: front-end processor, class that accepts HTTP requests and forwards requests
    <servlet>
        <servlet-name>court</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <! -- court-servlet. XML: Define bean in WebAppliactionContext -->
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:court-servlet.xml</param-value>
        </init-param>
        <load-on-startup>0</load-on-startup>
    </servlet>
             
    <servlet-mapping>
        <servlet-name>court</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
 
    <! The Spring IoC container needs to read the path of the XML file that defines the Bean (DAO/Service) that defines the non-Web layer.
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/court-service.xml</param-value>
    </context-param>
 
    <! ContextLoaderListerner: The Spring MVC startup class in the Web container that is responsible for the initialization of the Spring IoC container in the Web context.
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
Copy the code

In the web.xml configuration file, there are two main configurations: ContextLoaderListener and DispatcherServlet. There are also two parts to the spring configuration file: context-param and init-param in the DispatcherServlet. So, what is the difference between the configuration of the two parts? What do they do?

In Spring MVC, the Spring Context exists as a parent-child inheritance structure. There is a ROOT Context in the Web environment. This Context is the ROOT Context of the entire application and the parent Context of other contexts. Spring MVC also holds a separate Context that is a subcontext of the ROOT Context.

How is this Context structure implemented in Spring MVC? So let’s start with the ROOT Context, which is configured in ContextLoaderListener, ContextLoaderListener reads the configuration file specified by contextConfigLocation in context-param and creates the ROOT context.

The Spring MVC startup process is roughly divided into two processes:

  1. ContextLoaderListener initializes, instantiates the IoC container, and registers the container instance with the ServletContext;
  2. DispatcherServlet initialization;

Load and initialize the Spring root context in the Web container

The Web container calls the contextInitialized method to initialize the ContextLoaderListener, in which, ContextLoaderListener by calling the inherited from ContextLoader initWebApplicationContext methods instantiate the Spring Ioc container.

  1. Take a look at how WebApplicationContext extends ApplicationContext to add support for the Web environment. The WebApplicationContext interface is defined as follows:
    public interface WebApplicationContext extends ApplicationContext {
        // The name of the root context in ServletContext
        String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
        // Get the Web container's ServletContext
        ServletContext getServletContext(a);
    }
Copy the code
  1. Java, ContextLoaderListener, ContextLoaderListener, ContextLoaderListener, ContextLoaderListener
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        / / PS: ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext. Class. GetName () + "ROOT" the name of the ROOT context
        / / PS: by default, the location and name of the configuration file is: DEFAULT_CONFIG_LOCATION = "XML/WEB - INF/applicationContext."
        // There can be only one root context in the entire Web application
        if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) ! =null) {
            throw new IllegalStateException("Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in your web.xml!");
        }

        Log logger = LogFactory.getLog(ContextLoader.class);
        servletContext.log("Initializing Spring root WebApplicationContext");
        if (logger.isInfoEnabled()) {
            logger.info("Root WebApplicationContext: initialization started");
        }
        long startTime = System.currentTimeMillis();

        try {
            // Store context in local instance variable, to guarantee that
            // it is available on ServletContext shutdown.
            if (this.context == null) {
                // The WebApplicationContext is created here
                this.context = createWebApplicationContext(servletContext);
            }
            if (this.context instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
                if(! cwac.isActive()) {// The context has not yet been refreshed -> provide services such as
                    // setting the parent context, setting the application context id, etc
                    if (cwac.getParent() == null) {
                        // The context instance was injected without an explicit parent ->
                        // determine parent for root web application context, if any.ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } configureAndRefreshWebApplicationContext(cwac, servletContext); }}// PS: place the root context in the servletContext
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

            ClassLoader ccl = Thread.currentThread().getContextClassLoader();
            if (ccl == ContextLoader.class.getClassLoader()) {
                currentContext = this.context;
            } else if(ccl ! =null) {
                currentContextPerThread.put(ccl, this.context);
            }

            if (logger.isDebugEnabled()) {
                logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
            }
            if (logger.isInfoEnabled()) {
                long elapsedTime = System.currentTimeMillis() - startTime;
                logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
            }

            return this.context;
        } catch (RuntimeException ex) {
            logger.error("Context initialization failed", ex);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
            throw ex;
        } catch (Error err) {
            logger.error("Context initialization failed", err);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
            throwerr; }}Copy the code
  1. Look again at how the WebApplicationContext object is created: contextLoader.java
    protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {
        // Decide which WebApplicationContext to use based on the configuration in web.xml. XmlWebApplicationContext is used by default
        // Related configuration in web.xml context-param name "contextClass"Class<? > contextClass = determineContextClass(sc);if(! ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        }

        // Instantiate the WebApplicationContext implementation class
        ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

        // Assign the best possible id value.
        if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
	    // Servlet <= 2.4: resort to name specified in web.xml, if any.
            String servletContextName = sc.getServletContextName();
            if(servletContextName ! =null) {
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName);
            } else{ wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX); }}else {
            // Servlet 2.5's getContextPath available!
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + sc.getContextPath());
        }

        wac.setParent(parent);

        wac.setServletContext(sc);
        // Set the spring configuration file
        wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
        customizeContext(sc, wac);
        // The spring container is initialized
        wac.refresh();
        return wac;
    }
Copy the code
  1. ContextLoaderListener builds the Root Context sequence diagram:

3 Spring MVC corresponding context loading and initialization

The core class in Spring MVC is DispatcherServlet, which loads and creates the Spring Context and distributes requests to Controller classes based on the content of the Spring Context. The DispatcherServlet inherits from HttpServlet, and configuration files about the Spring Context are loaded and created in the init() method, The main calling sequence is the init – > initServletBean – > initWebApplicationContext.

  1. First look at the implementation: initWebApplicationContext FrameworkServlet. Java
    protected WebApplicationContext initWebApplicationContext(a) {
        // Start by looking for WebApplicationContext in the Web container's ServletContext
        WebApplicationContext wac = findWebApplicationContext();
        if (wac == null) {
            // No fixed context defined for this servlet - create a local one.
            // Get the root context from ServletContext
            WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
            // Create the Spring MVC context with the root context as the parent context
            wac = createWebApplicationContext(parent);
        }

        if (!this.refreshEventReceived) {
            // Apparently not a ConfigurableApplicationContext with refresh support:
            // triggering initial onRefresh manually here.
            onRefresh(wac);
        }

        if (this.publishContext) {
            // Publish the context as a servlet context attribute.
            // Get the name of the context in ServletContext
            String attrName = getServletContextAttributeName();
            // Place the Spring MVC Context into the ServletContext
            getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); }}return wac;
    }
Copy the code

Through initWebApplicationContext method calls, create the DispatcherServlet corresponding context, and place it to the ServletContext, this completes the construction process of the Spring IoC container in the web container.

  1. DispatcherServlet create context sequence diagram:

  1. The general process of DispatcherServlet initialization:

  1. Class diagram and inheritance relationship of controller DispatcherServlet:

4 DispacherServlet, WebApplicationContext, ServletContext in Spring

To understand the relationship between these three contexts, you need to be familiar with how Spring gets started in a Web container. Spring’s startup process is essentially the startup of its IOC container, and for web applications, the IOC container startup process is the context establishment process.

The Spring startup process:

  1. First, for a Web application deployed in a Web container, the Web container provides its global context, the ServletContext, which provides the hosting environment for the subsequent Spring IoC container.

  2. Second, a contextLoaderListener is provided in web.xml. When the Web container is started, the container initialization event is triggered. The contextLoaderListener listens for this event and its contextInitialized method is called. In this method, Spring initializes a startup context called the root context. WebApplicationContext, which is an interface class, specifically, whose actual implementation class is XmlWebApplicationContext. This is spring’s IoC container, whose Bean definition configuration is specified by the context-param tag in web.xml. In the IoC container after the initialization, spring to WebApplicationContext. ROOTWEBAPPLICATIONCONTEXTATTRIBUTE as Key attributes, store it to the ServletContext, easy to obtain;

  3. Again, after the contextLoaderListener is initialized, it initializes the Servlet configured in web.xml. This Servlet can be configured in multiple ways. Take the DispatcherServlet as an example. The servlet is essentially a standard front controller that forwards, matches, and processes each servlet request. The DispatcherServlet context establishes its own IoC context at initialization to hold spring MVC related beans. When establishing the DispatcherServlet’s own IoC context, Will use WebApplicationContext ROOTWEBAPPLICATIONCONTEXTATTRIBUTE Get the previous root context (WebApplicationContext) from the ServletContext as the parent context of your own context. Once you have the parent context, initialize your own context. The work of this DispatcherServlet initializing its own context can be seen in its initStrategies method, which basically initializes processor mapping, view parsing, and so on. The default context implementation class that the servlet itself holds is also XmlWebApplicationContext. After initialization, Spring takes the property associated with the servlet name (not simply the servlet name Key here, but with some transformation, you can see the source code for yourself) as the property Key and also stores it in the ServletContext for later use. In this way, each servlet holds its own context, that is, its own independent bean space, and each servlet shares the same beans defined by the root context (the context initialized in Step 2).

When configuring Spring in a Web container (such as Tomcat), you may already be familiar with the following configuration code in the web.xml file:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
                                                                                                                                             
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
                                                                                                                                             
    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
                                                                                                                                         
    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping></span>
Copy the code

The above configuration first creates an applicationContext in ContextLoaderListener via applicationContext.xml in

. The ApplicationContext is then inserted into the ServletContext. The ServletContext setAttribute method does this. In the ContextLoaderListener source code, we can see the following code:

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
Copy the code

The ApplicationContext created by ContextLoaderListener is shared across the entire Web application, and as you probably already know, the DispatcherServlet maintains its own ApplicationContext, The/web-info /

-servlet.xml file is read by default, and can also be reconfigured:

    <servlet>  
        <servlet-name>  
           customConfiguredDispacherServlet  
        </servlet-name>  
        <servlet-class>  
            org.springframework.web.servlet.DispatcherServlet  
        </servlet-class>  
        <init-param>  
            <param-name>  
                contextConfigLocation  
            </param-name>  
            <param-value>  
                /WEB-INF/dispacherServletContext.xml  
            </param-value>  
        </init-param>  
        <load-on-startup>1</load-on-startup>  
    </servlet>
Copy the code

The question is: what is the relationship between these two applicationContexts, their scope of action, and their purpose?

The ApplicationContext created in ContextLoaderListener is used for components that need to be shared throughout the Web application, such as the DAO, the ConnectionFactory of the database, and so on. The ApplicationContext created by DispatcherServlet is used mainly for the components associated with the Servlet, such as Controller, ViewResovler, and so on.

For scope, the ApplicationContext created by ContextLoaderListener can be referenced in the DispatcherServlet and not vice versa.

In Spring’s implementation, both applicationContexts are placed into the ServletContext using the setAttribute method of the ServletContext. However, ContextLoaderListener creates the ApplicationContext before DispatcherServlet, The DispatcherServlet creates ApplicationContext by finding the ApplicationContext created by ContextLoaderListener. The ApplicationContext is passed as an argument to the setParent() method of the ApplicationContext of the DispatcherServlet. You can find the following code in Frameservlet.java:

wac.setParent(parent);
Copy the code

Where waC is the ApplicationContext created by DisptcherServlet and parent is the ApplicationContext created by ContextLoaderListener. After that, the framework calls the setAttribute() method of the ServletContext to add the WAC to the ServletContext.

When Spring executes the getBean of ApplicationContext, if it does not find the corresponding bean in its own context, it will look for it in the parent ApplicationContext. This also explains why we can get the bean in the ApplicationContext corresponding to the ContextLoaderListener in the DispatcherServlet.