springmvc web.xml配置之 -- ContextLoaderListener


首先回歸一下web.xml的常用配置,看一個示例:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:spring/applicationcontext-*.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:spring/springmvc-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
View Code

了解web.xml的作用之前必須要了解web容器(也稱為是servlet容器)這個概念,常用的web容器有Tomcat、Jetty、JBoss等。

使用web容器,就不得不清楚什么是ServletContext。ServletContext中的信息都是由容器提供的,在web容器啟動時,它作為為公共環境容器存放公共信息,為各個Servlet提供信息,也可以作為各個Servlet之間通信的橋梁。

將應用部署到web容器中,啟動web容器后,web容器會讀取web.xml中的配置信息,對ServletContext進行信息補充。(注:配置加載順序為:context-param -> listener -> filter -> servlet)

好了,回到本文主題。

ContextLoaderListener

web.xml中的配置文件中ContextLoaderListener作為監聽器,會監聽web容器相關事件,在web容器啟動或者關閉時觸發執行響應程序。具體地,ContextLoaderListener繼承ContextLoader類並實現了ServletContextListener接口。

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }

    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }

    public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext());
    }

    public void contextDestroyed(ServletContextEvent event) {
        this.closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}

  

1.關於監聽

ServletContextListener接口中只有初始化和銷毀的兩個方法::

public interface ServletContextListener extends EventListener {
    /**
     ** Notification that the web application initialization
     ** process is starting.
     ** All ServletContextListeners are notified of context
     ** initialization before any filter or servlet in the web
     ** application is initialized.
     */
    public void contextInitialized ( ServletContextEvent sce );

    /**
     ** Notification that the servlet context is about to be shut down.
     ** All servlets and filters have been destroy()ed before any
     ** ServletContextListeners are notified of context
     ** destruction.
     */
    public void contextDestroyed ( ServletContextEvent sce );
}

也就是監聽ServletContext的狀態,ServletContext屬於容器對象,在用於在容器開啟或關閉時觸發事件,執行contextInitialized/contextDestroyed 。如果想了解程序執行原由可以先了解事件監聽設計模式。查閱Tomcat源碼,tomcat觸發ServletContext初始化監聽事件的源碼如下:

...(部分略)
   
Object instances[] = getApplicationLifecycleListeners();
for (int i = 0; i < instances.length; i++) {
    if (!(instances[i] instanceof ServletContextListener))
        continue;
    ServletContextListener listener = (ServletContextListener) instances[i];
    try {
        if (noPluggabilityListeners.contains(listener)) {
            listener.contextInitialized(tldEvent);
        } else {
            listener.contextInitialized(event);
        }
    } catch (Throwable t) {
    }
}

 ...(部分略)

也就是,遍歷所有ServletContextListener,調用監聽方法。

 

2.關於功能

關於功能主要看initWebApplicationContext()方法。 ContextLoader中的initWebApplicationContext()方法中會創建WebApplicationContext上下文對象,並將這個對象設置到servletContext中:

ContextLoaderListener加載過程:

2.1 創建IOC容器

initWebApplicationContext調用createWebApplicationContext方法;

-->createWebApplicationContext 調用determineContextClass方法

--> defaultStrategies中加載配置文件:

defaultStrategies.getProperty(WebApplicationContext.class.getName())

ContextLoader 類中有段靜態代碼:

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

        currentContextPerThread = new ConcurrentHashMap(1);
    }

ContextLoader.properties 文件內容如下:

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

可知,默認加載的WebApplicationContext是XmlWebApplicationContext

題外話,如果你不想使用默認的,在web.xml中配置contextClass即可

protected Class<?> determineContextClass(ServletContext servletContext) {
        String contextClassName = servletContext.getInitParameter("contextClass");
        if(contextClassName != null) {
            try {
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
                ...(略)

也就是這種形式:

<context-param> <param-name>contextClass</param-name> <param-value>webApplicationContext類全路徑名稱</param-value> <context-param>

org.springframework.web.context.support包下查詢webApplicationContext。

2.1 IOC容器配置加載,初始化

這一步主要是調用configureAndRefreshWebApplicationContext,可知在上一步中創建了WebApplicationContext對象(ConfigurableApplicationContext類型),也就是創建了IOC容器對象,接着就要給這個對象進行配置參數的設置了。

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
        String configLocationParam;
        if(ObjectUtils.identityToString(wac).equals(wac.getId())) {
            configLocationParam = sc.getInitParameter("contextId");
            if(configLocationParam != null) {
                wac.setId(configLocationParam);
            } else {
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
            }
        }

        wac.setServletContext(sc);
        configLocationParam = sc.getInitParameter("contextConfigLocation");
        if(configLocationParam != null) {
            wac.setConfigLocation(configLocationParam);
        }

        ConfigurableEnvironment env = wac.getEnvironment();
        if(env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);
        }

        this.customizeContext(sc, wac);
        wac.refresh();
    }

可知,首先會給WebApplicationContext對象設置ServletContext全局信息,然后讀取><param-name>contextConfigLocation</param-name>對應的.xml中的配置信息,例如:

<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/applicationcontext-*.xml</param-value> </context-param>

這些applicationcontext-*.xml配置文件中的信息之后再執行ConfigurableApplicationContext.refresh()方法就會被加載到IOC容器中。

IOC容器的初始化過程,參考下一篇文章。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM