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容器的初始化过程,参考下一篇文章。

 

posted @ 2018-07-24 00:09  有爱jj  阅读(7980)  评论(2编辑  收藏  举报