[Spring MVC learning] Three ways to initialize WebApplicationContext

ApplicationContext is the core of Spring. We usually interpret Context as context. I want to use "container" to describe it to be easier to understand. ApplicationContext is "application container"; in web applications, we will use WebApplicationContext, WebApplicationContext Inherited from ApplicationContext; the initialization method of WebApplicationContext is different from BeanFactory.ApplicationContext, because WebApplicationContext requires a ServletContext instance, that is to say, it must have a Web container to complete the startup work. Readers who have experience in Web development know that you can Configure the self-starting Servlet in web.xml or define the Web container listener (ServletContextListener), with either of the two, we can start the work of the Spring Web application context.

Spring provides servlet and web container listeners for starting the WebApplicationContext, respectively:

org.springframework.web.context.ContextLoaderServlet;

org.springframework.web.context.ContextLoaderListener.
  • 1
  • 2
  • 3

These two methods are to initialize the WebApplicationContext when the web application starts. I personally think that the Listener is better than the Servlet, because the Listener monitors the start and end of the application, and the Servlet has to start a little delay. If you want to When doing some business operations, the sequence of startup has an impact.

The configuration example is as follows:

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> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

So what exactly is done in ContextLoaderListener and ContextLoaderServlet? 
Taking ContextLoaderListener as an example, we can see

public class ContextLoaderListener implements ServletContextListener {  

    private ContextLoader contextLoader;  

    /** 
     * Initialize the root web application context. 
     */  
    public void contextInitialized(ServletContextEvent event) {  
        this.contextLoader = createContextLoader();  
        this.contextLoader.initWebApplicationContext(event.getServletContext());  
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Obviously, ContextLoaderListener implements ServeletContextListenet. When ServletContext is initialized, Spring will be initialized. Everyone must think that Spring's initialization should have a certain relationship with ServletContext, right? does it matter? Next let's look at the 
ContextLoader.initWebApplicationContext method.

ContextLoader is a tool class used to initialize WebApplicationContext. Its main method is initWebApplicationContext. We continue to study the method of initWebApplicationContext:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext)  throws IllegalStateException, BeansException {  

            //从ServletContext中查找,是否存在以WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE为Key的值

    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!");  
    }  


    try {  
        // Determine parent for root web application context, if any.  
        ApplicationContext parent = loadParentContext(servletContext);  

        // it is available on ServletContext shutdown.  
        this.context = createWebApplicationContext(servletContext, parent);  
        //将ApplicationContext放入ServletContext中,其key为<WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 


    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); 
                //将ApplicationContext放入ContextLoader的全局静态常量Map中,其中key为:Thread.currentThread().getContextClassLoader()即当前线程类加载器 

        currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);  

        return this.context;  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

From the above code, you should understand that after Spring is initialized, the ApplicationContext is stored in two places (servletContext and currentContextPerThread), so does it mean that we can obtain ApplicationContext in two ways?

The first way to get:

 注:WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
即为 "org.springframework.web.context.WebApplicationContext.ROOT"
  • 1
  • 2

So can we get the ApplicationContext like this:

request.getSession().getServletContext().getAttribute("org.springframework.web.context.WebApplicationContext.ROOT")  
  • 1

It does, and when we thought of this approach, Spring already provided us with an interface:

public abstract class WebApplicationContextUtils {  

public static WebApplicationContext getRequiredWebApplicationContext(ServletContext sc)  throws IllegalStateException {  

        WebApplicationContext wac = getWebApplicationContext(sc);  
        if (wac == null) {  
            throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");  
        }  
        return wac;  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

The getWebApplicationContext method is as follows:

public static WebApplicationContext getWebApplicationContext(ServletContext sc) {  
        return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);  
    }  
  • 1
  • 2
  • 3
  • 4

The second method:

As mentioned earlier, when Spring is initialized, the ApplicationContext is also stored in the Map of ContextLoader, so can we pass Map.get(key) ??? Unfortunately, this Map is private.

private static final Map currentContextPerThread = CollectionFactory.createConcurrentMapIfPossible(1);  
  • 1
  • 2

Spring provides us with methods:

public static WebApplicationContext getCurrentWebApplicationContext() {  
        return (WebApplicationContext) currentContextPerThread.get(Thread.currentThread().getContextClassLoader());  
    }  
  • 1
  • 2
  • 3
  • 4

How does the second method compare to the first method? That is, it does not require parameters , as long as in the Web container, when Spring is initialized, you can get the ApplicationContext without passing in any parameters. However, this method does not exist in the Spring 2.52 version, but is provided in the 2.5.5 version.

In fact, the second acquisition method looks simple, but its principle is still difficult. It is related to the thread context of the class loader. This thread context is useful in our commonly used Mysql driver.

The third way:

Borrowing ApplicationContextAware, the helper class of ApplicationContext can automatically load ApplicationContext. As long as you implement this interface for a class and configure this implementation class in the Spring configuration file, Spring will automatically help you inject ApplicationContext.ApplicationContextAware The code structure is as follows:

public interface ApplicationContextAware {  

        void setApplicationContext(ApplicationContext applicationContext) throws BeansException;  

}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Just this interface. You can simply implement an ApplicationContextHelper class like this:

public class ApplicationHelper implements ApplicationContextAware {  


    private ApplicationContext applicationContext; 

    public void setApplicationContext(ApplicationContext applicationContext)  
            throws BeansException {  
            this.applicationContext = applicationContext;  
    }  


    public  ApplicationContext getApplicationContext(){
        return this.applicationContext;  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Through ApplicationHelper we can get the AppilcationContext class we want.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325751228&siteId=291194637