Servlet监听器(附带spring中监听器使用分析)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yyyCHyzzzz/article/details/64919562

一.简介

Servlet监听器 是Servlet规范中定义的一种特殊类 ,或者说一种高级特性.常用于监听request,session,context的创建,销毁或属性改变

二.监听器分类及作用(Servlet3.0规范)

eclipse里创建listener所提供的监听器接口 image


1.ServletContextListener , SerlvetContextAttributeListener 用于监听应用程序环境对象(ServletContext)

在web.xml中配置

<context-param>
  <param-name>begincode</param-name>
  <param-value>code</param-value>
</context-param>
//在实现ServletContextListener接口中
public void contextInitialized(ServletContextEvent arg0)  { 
     String value = arg0.getServletContext().getInitParameter("begincode");
     System.out.println(value);  //code
}

当 Servlet 容器启动后,会部署和加载所有 web 应用。当web 应用被加载,Servlet 容器会创建一次 ServletContext,然后将其保存在服务器的内存中。web 应用的 web.xml 被解析,找到其中所有 servlet、filter 和 Listener 或 @WebServlet、@WebFilter 和 @WebListener 注解的内容,创建一次并保存到服务器的内存中。ServletContext 与 web 应用存活时间一样长。它被所有 session 中的所有请求共享。

主要用途:作为定时器、加载全局属性对象、创建全局数据库连接、加载缓存信息等

2.ServeltRequestListener接口 监听HttpServletRequest的创建 销毁

public void requestInitialized(ServletRequestEvent sre)//request创建时调用
public void requestDestroyed(ServletRequestEvent sre)//request销毁时调用

ServletRequestAttributeListener接口 request域对象值改变

public void attributeAdded(ServletRequestAttributeEvent arg0) {
		
}
@Override
public void attributeRemoved(ServletRequestAttributeEvent arg0) {
	
}
@Override
public void attributeReplaced(ServletRequestAttributeEvent arg0) {
	
}
主要用途:读取request参数,记录访问历史

3.HttpSessionListener,HttpSessionAttributeListener 用于监听HttpSession的创建 销毁 或者属性改变

public void sessionCreated(HttpSessionEvent se)//session创建时调用
public void sessionDestroyed(HttpSessionEvent se)//session销毁时调用
public void attributeAdded(HttpSessionBindingEvent arg0) {
}
public void attributeRemoved(HttpSessionBindingEvent arg0) {
}
public void attributeReplaced(HttpSessionBindingEvent arg0) {
}
主要用途:统计在线人数、记录访问日志等

最后再来讲讲 HttpSessionAcivationListener,HttpSessionBindingListener :

监听绑定到HttpSeesion域中的某个对象的状态的事件监听器(创建普通JavaBean)

HttpSessionBingingListener:当实现此接口的对象被加入HttpSession或从中移除时,就会调用对应的valueBound()与valueUnbound()方法,并传入HttpSessionBindingEvent对象,可以通过该对象的getSession()取得HttpSession对象。

HttpSessionAcivationListener: 可以实现对象的钝化和活化 >具体的可以自己百度,因为用的比较少,这里不再详细介绍.

钝化:    将session对象持久化到存储设备上

活化:    将session对象从存储设备上进行恢复

ps:使用注解 @WebListener无法去定义监听器的执行顺序

三. Spring 监听器源码简单分析

在使用spirng,springmvc的web项目中 我们常用的监听器有两个

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
            classpath*:spring/applicationContext-*.xml
            classpath*:spring/dataSource.xml
       </param-value>
</context-param>
<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
	<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

ContextLoaderListener 能够监听ServletContext对象的生命周期,实际上就是监听Web应用的生命周期。当Servlet容器启动或终止Web应用时,会触发ServletContextEvent事件,该事件由ServletContextListener来处理。在ServletContextListener接口中定义了处理ServletContextEvent 事件的两个方法contextInitialized()和contextDestroyed()。

ContextLoaderListener监听器的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置了这个监听器,启动容器时,就会默认执行它实现的方法。由于在ContextLoaderListener中关联了ContextLoader这个类,所以整个加载配置过程由ContextLoader来完成。

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

	public ContextLoaderListener() {
	}
	
	public ContextLoaderListener(WebApplicationContext context) {
		super(context);
	}
	
	@Override
	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}

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

接下来看父类

//看名字就知道是拿到当前的上下文对象
private static volatile WebApplicationContext currentContext;

//初始化保存的context对象
private WebApplicationContext context;

//静态代码块 加载配置文件里的信息
static {
	try {
		ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
		defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
	}
	catch (IOException ex) {
		throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
	}
}

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

	Log logger = LogFactory.getLog(ContextLoader.class);
	servletContext.log("Initializing Spring root WebApplicationContext");
	if (logger.isInfoEnabled()) {
		logger.info("Root WebApplicationContext: initialization started");
	}
	long startTime = System.currentTimeMillis();

	try {
		if (this.context == null) {
		        //通过反射创建上下文实例
			this.context = createWebApplicationContext(servletContext);
		}
		if (this.context instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
			if (!cwac.isActive()) {
				if (cwac.getParent() == null) {
					ApplicationContext parent = loadParentContext(servletContext);
					cwac.setParent(parent);
				}
				//配置 并刷新WebApplicationContext
				configureAndRefreshWebApplicationContext(cwac, servletContext);
			}
		}
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

		ClassLoader ccl = Thread.currentThread().getContextClassLoader();
		if (ccl == ContextLoader.class.getClassLoader()) {
			currentContext = this.context;
		}
		else if (ccl != null) {
			currentContextPerThread.put(ccl, this.context);
		}

		if (logger.isDebugEnabled()) {
			logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
					WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
		}
		if (logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - startTime;
			logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
		}

		return this.context;
	}
	catch (RuntimeException ex) {
		logger.error("Context initialization failed", ex);
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
		throw ex;
	}
	catch (Error err) {
		logger.error("Context initialization failed", err);
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
		throw err;
	}
}

这段代码最主要的是在这里

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

key为 :String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

value为: this.context

在程序启动的时候 我们debug看看里面保存了什么 imageimage发现了一个很熟悉的名字 beanFactory ,这不就是spring的bean工厂类嘛!!

我们可以实现一个接口 用来手动拿取spring容器里面的bean

@Component
public class SpringApplicationContextHolder implements ApplicationContextAware {
    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        SpringApplicationContextHolder.context = context;
    }


    public static Object getSpringBean(String beanName) {
        Validate.notEmpty(beanName, "bean name is required");
        return context==null?null:context.getBean(beanName);
    }

    public static String[] getBeanDefinitionNames() {
        return context.getBeanDefinitionNames();
    }
}

具体可以应用在多线程中,spring为了安全 不允许在多线程中用注解来注入类,我们可以用上面的代码来实现注入.

Reference:

http://www.cnblogs.com/sherryueda/p/4273169.html

http://blog.csdn.net/lihuapiao/article/details/51919283

猜你喜欢

转载自blog.csdn.net/yyyCHyzzzz/article/details/64919562