Spring源代码-ContextLoaderListener的学习

本文介绍了Spring在web应用程序中的启用,特别是在web.xml中配置ContextLoaderListener的角色。ContextLoaderListener的官方注释强调了配置顺序的重要性,该监听器在应用启动时加载Spring服务。此外,文章还探讨了ContextLoaderListener的结构,包括初始化和关闭web上下文的方法。最后,总结了web.xml中listener、servlet、filter的加载顺序,并指出Log4jConfigListener应先于ContextLoaderListener加载。

web.xml中启用Spring

在一般的web应用程序,我们倘若用到Spring的话,需要在web.xml中配置以下的信息来使一些容器,例如TomcatJetty等来加载Spring

    <!-- Spring配置文件信息 -->
    <context-param>
    	<!-- 这个contextConfigLocation参数是用来加载spring相关配置的标签 -->
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:com/conf/applicationContext.xml</param-value>
    </context-param>
    <!-- ContextLoaderListener监听器 -->
    <!-- ContextLoaderListener的作用就是启动Web容器时,自动装配
		ApplicationContext.xml的配置信息 
	-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

Spring主要通过ContextLoaderListener类在应用启动时加载其服务

 

ContextLoaderListener的官方注释

查看某个类的重要功能最好是观察其的注释,ContextLoaderLinstener官方注释如下:

//父级启动类,可用ContextLoader启动和ContextCleanupListener来关闭Spring的根web应用上下文
  Bootstrap listener to start up and shut down Spring's root {@link WebApplicationContext}.
  Simply delegates to {@link ContextLoader} as well as to {@link ContextCleanupListener}.
 
  //这里给出了提示,如果需要用到自定义log4j的配置的话,则ContextListener需要在Log4jConfigListener之后
  <p>This listener should be registered after {@link org.springframework.web.util.Log4jConfigListener}
  in {@code web.xml}, if the latter is used.
 //从Spring3.1之后,注入根web应用上下文可通过 WebApplicationInitializer,容器在启动会加载此接口,但是有个要求是容器的Servlet版本必须是3.0+,对Tomcat来说必须是7.0.15版本以上
  <p>As of Spring 3.1, {@code ContextLoaderListener} supports injecting the root web
  application context via the {@link #ContextLoaderListener(WebApplicationContext)}
  constructor, allowing for programmatic configuration in Servlet 3.0+ environments.
  See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
 
  @author Juergen Hoeller
  @author Chris Beams
  @since 17.02.2003
  @see #setContextInitializers
  @see org.springframework.web.WebApplicationInitializer
  @see org.springframework.web.util.Log4jConfigListener

重要的批注都在新增的注释上,并且我们可以发现,web.xml中的listener节点具有代码逻辑上的先写先加载的特点。

特别需要注意的是如果用户需要用到Log4jConfigListener的话,则必须写在ContextLoaderListener的前面

ContextLoaderListener结构

public class ContextLoaderListener extends ContextLoader implements ServletContextListener{}

下面是ContextLoaderListener的源代码:

/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.context;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * Bootstrap listener to start up and shut down Spring's root {@link WebApplicationContext}.
 * Simply delegates to {@link ContextLoader} as well as to {@link ContextCleanupListener}.
 *
 * <p>As of Spring 3.1, {@code ContextLoaderListener} supports injecting the root web
 * application context via the {@link #ContextLoaderListener(WebApplicationContext)}
 * constructor, allowing for programmatic configuration in Servlet 3.0+ environments.
 * See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
 *
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 17.02.2003
 * @see #setContextInitializers
 * @see org.springframework.web.WebApplicationInitializer
 */
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

	/**
	 * Create a new {@code ContextLoaderListener} that will create a web application
	 * context based on the "contextClass" and "contextConfigLocation" servlet
	 * context-params. See {@link ContextLoader} superclass documentation for details on
	 * default values for each.
	 * <p>This constructor is typically used when declaring {@code ContextLoaderListener}
	 * as a {@code <listener>} within {@code web.xml}, where a no-arg constructor is
	 * required.
	 * <p>The created application context will be registered into the ServletContext under
	 * the attribute name {@link WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE}
	 * and the Spring application context will be closed when the {@link #contextDestroyed}
	 * lifecycle method is invoked on this listener.
	 * @see ContextLoader
	 * @see #ContextLoaderListener(WebApplicationContext)
	 * @see #contextInitialized(ServletContextEvent)
	 * @see #contextDestroyed(ServletContextEvent)
	 */
	public ContextLoaderListener() {
	}

	/**
	 * Create a new {@code ContextLoaderListener} with the given application context. This
	 * constructor is useful in Servlet 3.0+ environments where instance-based
	 * registration of listeners is possible through the {@link javax.servlet.ServletContext#addListener}
	 * API.
	 * <p>The context may or may not yet be {@linkplain
	 * org.springframework.context.ConfigurableApplicationContext#refresh() refreshed}. If it
	 * (a) is an implementation of {@link ConfigurableWebApplicationContext} and
	 * (b) has <strong>not</strong> already been refreshed (the recommended approach),
	 * then the following will occur:
	 * <ul>
	 * <li>If the given context has not already been assigned an {@linkplain
	 * org.springframework.context.ConfigurableApplicationContext#setId id}, one will be assigned to it</li>
	 * <li>{@code ServletContext} and {@code ServletConfig} objects will be delegated to
	 * the application context</li>
	 * <li>{@link #customizeContext} will be called</li>
	 * <li>Any {@link org.springframework.context.ApplicationContextInitializer ApplicationContextInitializer org.springframework.context.ApplicationContextInitializer ApplicationContextInitializers}
	 * specified through the "contextInitializerClasses" init-param will be applied.</li>
	 * <li>{@link org.springframework.context.ConfigurableApplicationContext#refresh refresh()} will be called</li>
	 * </ul>
	 * If the context has already been refreshed or does not implement
	 * {@code ConfigurableWebApplicationContext}, none of the above will occur under the
	 * assumption that the user has performed these actions (or not) per his or her
	 * specific needs.
	 * <p>See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
	 * <p>In any case, the given application context will be registered into the
	 * ServletContext under the attribute name {@link
	 * WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} and the Spring
	 * application context will be closed when the {@link #contextDestroyed} lifecycle
	 * method is invoked on this listener.
	 * @param context the application context to manage
	 * @see #contextInitialized(ServletContextEvent)
	 * @see #contextDestroyed(ServletContextEvent)
	 */
	public ContextLoaderListener(WebApplicationContext context) {
		super(context);
	}


	/**
	 * Initialize the root web application context.
	 */
	@Override
	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}


	/**
	 * Close the root web application context.
	 */
	@Override
	public void contextDestroyed(ServletContextEvent event) {
		closeWebApplicationContext(event.getServletContext());
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}

}

最应该关注的是ServletContextListenr接口,我们应该知道实现此接口的类会在应用启动的时候,自动的调用其接口方法contextInitialized(ServletContextEvent event);
关闭应用时候则会调用其另外一个接口方法contextDestroyed(ServletContextEvent evet)用来关闭web上下文信息。

  • 初始化
    public void contextInitialized(ServletContextEvent event) {
    	//调用的是父类ContextLoader的方法,看出来这是启动的关键
    	initWebApplicationContext(event.getServletContext());
    }
    
  • 关闭
    	//与初始化相对
    	closeWebApplicationContext(event.getServletContext());
    	//使用ContextCleanupLister监听类来销毁ServletContext的springwork属性信息
    	ContextCleanupListener.cleanupAttributes(event.getServletContext());
    

这里我们简单的看下销毁属性的代码方法

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

 

	/**
	 * Find all Spring-internal ServletContext attributes which implement
	 * {@link DisposableBean} and invoke the destroy method on them.
	 * @param servletContext the ServletContext to check
	 * @see DisposableBean#destroy()
	 */
	static void cleanupAttributes(ServletContext servletContext) {
		Enumeration<String> attrNames = servletContext.getAttributeNames();
		while (attrNames.hasMoreElements()) {
			String attrName = attrNames.nextElement();
	        //筛选出专属spring的属性		
                if (attrName.startsWith("org.springframework.")) {
				Object attrValue = servletContext.getAttribute(attrName);
                                //基本WebApplication都实现了DisposableBean接口,表明所有的Bean都是可以释放的
				if (attrValue instanceof DisposableBean) {
					try {
						((DisposableBean) attrValue).destroy();
					}
					catch (Throwable ex) {
						if (logger.isWarnEnabled()) {
							logger.warn("Invocation of destroy method failed on ServletContext " +
									"attribute with name '" + attrName + "'", ex);
						}
					}
				}
			}
		}
	}

记录总结

  1. web.xml中的listener节点具有先写先加载的特点,类似于java代码中的顺序执行

  2. Log4jConfigListener类加载必须在ContextLoaderListenr类之前

  3. ContextLoaderListener启动Spring并生成Spring根web服务上下文则是通过其父类ContextLoader来实现的,其销毁也只会销毁具有org.springwork前缀的属性

  4. ServletContext代表应用服务上下文,其可以读取<context-param>级别的参数,而ServletConfig/FilterConfig则会读取其相应servlet节点/filter节点下的<init-param>参数

  5. web.xml中listener、servlet、filter执行顺序为listener>filter>servlet,同类型的执行顺序为listener满足先写先加载,servlet、filter则由mapping节点的先后顺序加载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值