Spring container and its initialization

Keywords: Java Spring xml Attribute jvm

1. Some concepts in spring

  • When reading spring source code or related literature, we often encounter these nouns: webapplicationcontext - ApplicationContext - ServletContext - ServletConfig. These nouns are very similar but have different scope of application, which is easy to cause confusion in the understanding of spring's internal implementation. Therefore, we should roughly explain these nouns first.

    • ServletContext: This is a concept from the Servlet specification. It is a combination of interfaces used by the Servlet to interact with containers. That is to say, this interface defines a series of methods by which the Servlet can easily interact with the container. From its definition, it can be seen that there is only one ServletContext in an application (a JVM). That is to say , all servlets in the container share a ServletContext.
    • ServletConfig: the difference between ServletConfig and ServletContext is that ServletConfig is for servlets. Each servlet has its unique ServletConfig information, which is not shared with each other.
    • ApplicationContext: this class is the core interface of Spring container function. It is the most important interface for Spring to implement IOC function. As can be seen from its name, it maintains the context information required by the entire program running period. Note that the application here is not necessarily a web program. In Spring, multiple applicationcontexts are allowed. These applicationcontexts form parent-child relationship with each other. , the relationship between inheritance and being inherited, which is also what we usually say: there are two contexts in Spring, one is Root Application Context, the other is Servlet Application Context, which will be explained in detail later.
    • Webapplicatoncontext: this interface is only a sub interface of ApplicationContext interface, but its application form is web. On the basis of applicatoncontext, it adds a reference to ServletContext.

2. Initialization of spring container

In the Spring MVC configuration file, we usually configure a front-end controller dispatcher servlet and listener ContextLoaderListener to initialize the Spring application context.

  • In the previous elaboration, it can be seen that ServletContext is the configuration shared by all servlets in the container, which is applied to the whole. According to the Servlet specification, according to the configuration of the above listener, context param specifies the unknown configuration file. When initializing ServletContext after the container is started, the listener will automatically load the configuration file to initialize Spring root container root application Co. Ntext.
  • Similarly, ServletConfig is configured for the progress of each servlet, so its configuration is in the servlet configuration. According to the above configuration of dispatcher servlet, init param also specifies the xml file to load the configuration information when the servlet initializes the call to init method, and initializes the Spring application container Servlet Application Context.

Next, we analyze Spring container initialization specifically:

  • For the configuration of ApplicationContext, first, configure the context param parameter in the ServletContext. The so-called Root Application Context will be generated through the listener, and the init param parameter specified in each dispatcher servlet will generate the Servlet Application Context. And its parent is the Root Application Context generated in the ServletContext. Therefore, what is defined in the ServletContext is All configurations will be inherited to the dispatcher servlet, which will be visualized in the code later.

1. Root Application Context

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    //...................
    public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext());
    }

}

--------------------------------------------------------------------------------------------------------------------------------------------------------

public class ContextLoader {
    //...................
 public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        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!");
        } else {
          //...................
            try {
                if(this.context == null) {
                    this.context = this.createWebApplicationContext(servletContext);
                }
                //...................
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
                ClassLoader ccl = Thread.currentThread().getContextClassLoader();
                return this.context;
            } 
        }
    }

 protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
        Class<?> contextClass = this.determineContextClass(sc);
        if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        } else {
            return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
        }
    }


 protected Class<?> determineContextClass(ServletContext servletContext) {
        String contextClassName = servletContext.getInitParameter("contextClass");
        if(contextClassName != null) {
            try {
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
            }
        } else {
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
            try {
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            } 
        }
    }


    static {
        try {
            ClassPathResource resource = new ClassPathResource("ContextLoader.properties", ContextLoader.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        } catch (IOException var1) {
            throw new IllegalStateException("Could not load 'ContextLoader.properties': " + var1.getMessage());
        }

        currentContextPerThread = new ConcurrentHashMap(1);
    }
}

  • For the convenience of reading, here are some non core code filtering

    1. According to the configuration file, first we listen to the ServletContext initialization through the ContextLoaderListener object. In the initialization method, the parent class contextloader ᦇ initwebapplicationcontext method will be called.
    2. In initWebApplication, first determine whether there is Root Application Context. If there is, throw an exception. Then create the container object through the createWebApplicationContext method, and put the container into ServletContext. So the difference between ApplicationContext and ServletContext is that ApplicationContext is actually an attribute value in ServletContext. Property contains all the context information of the program running. Since this ApplicationContext is a global application context, it is called "Root Application Context" in Spring.
    3. Next, let's see how the container is created: when we enter the createWebApplicationContext method, we can see that it creates the container by instantiating the class class of the container, while in the determineContextClass method, we first obtain the full path class name by initializing the parameter, and if it doesn't exist, we obtain the container name by configuring the class - defaultStrategies.
    4. We can find this configuration class from the static code of the current class. It gets the full path class name of the current container by reading the ContextLoader.properties configuration file.

2. Servlet Application Context

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
    public final void init() throws ServletException {
        //Traverse to get all parameters of servletConfig
        PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
        //Initialize servlet applicationContext
        this.initServletBean();
    }
}

--------------------------------------------------------------------------------------------------------------------------------------------------------

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    protected final void initServletBean() throws ServletException {
        //...................
        try {
            this.webApplicationContext = this.initWebApplicationContext();
        } 
    }

    protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        WebApplicationContext wac = null;

        if(wac == null) {
            wac = this.createWebApplicationContext(rootContext);
        }

        if(this.publishContext) {
            String attrName = this.getServletContextAttributeName();
            this.getServletContext().setAttribute(attrName, wac);
            if(this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]");
            }
        }

        return wac;
    }

    protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
        return this.createWebApplicationContext((ApplicationContext)parent);
    }

    protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
        Class<?> contextClass = this.getContextClass();
       
        if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
        } else {
            ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
            wac.setEnvironment(this.getEnvironment());
            wac.setParent(parent);
            wac.setConfigLocation(this.getContextConfigLocation());
            this.configureAndRefreshWebApplicationContext(wac);
            return wac;
        }
    }
}

  • Let's take a look at dispatcher servlet.

    1. As a Servlet, according to the specification, its configuration information should be completed in the ා - init method. First, we enter the dispatcher Servlet # - init method, which inherits from the parent HttpServletBean. In the ා - init method of the parent class, first obtain all parameters of the ServletConfig through traversal, and then initialize the Servlet Application Context.
    2. The container initialization method #initServletBean is located in the parent class FrameworkServlet, calling the #initWebApplicationContext method in the #initServletBean method.
    3. In the initWebApplicationContext method, first obtain the Root Application Context through the ServletContext, and then start to initialize the Servlet Application Context. In the process of creating the container, the Root Application Context will be passed in as its Parent, that is, the Parent-child relationship will be established between the two, forming the inheritance relationship mentioned before. Finally, the newly created container will also be put into the ContextServlet.

3. summary

The above is a general analysis of containers in Spring. We will inject different component instances into the Spring container when the project starts, so as to realize the Spring IOC mechanism.

  1. To learn how to customize DispatcherServlet through Spring annotation, please refer to: Analysis of web application initializer and its implementation in Spring
  2. If you want to know something about spring MVC custom configuration, please refer to:

Posted by buraks78 on Thu, 24 Oct 2019 07:13:34 -0700