JAVA WEB服务监听与周期执行某一任务

说明:本人为菜鸟,此文主要用于记录个人学习笔记,也希望给予同时菜鸟的朋友一点帮助,文中解决方法有些采用了他人文章,感谢原创者的付出。由于其他原因,本人只给出本人查阅采用的文章链接,对于参考文章非该作者原创的情况,希望原创者能够理解。
应用背景:在做一个java web工程服务器为tomcat,里面有个功能大致要求如下,周期性的检查计算机目录下的某个文件是否更新,若更新则对该文件进行分析。
关键词:ServletContextListener;Timer;TimerTask
解决方法:
    搜索资料后,发现采用ServletContextListener接口能够监听ServletContext对象的生命周期,实际上就是监听Web应用的生命周期。当Servlet容器启动或终止Web应用时,会触发ServletContextEvent事件,该事件由ServletContextListener 来处理。这样我们可以根据这个特性实现在web应用程序初始化时,自动运行一些初始化程序。在 ServletContextListener 接口中定义了处理ServletContextEvent事件的两个方法。该接口中有两个主要方法:contextDestroyed()与contextInitialized(),前者由Servlet容器调用在web应用的“初始化”阶段,后者的调用在web应用的“结束”阶段。在部署ServletContextListene的时候,需要在web.xml中对相关参数进行配置,相关更详细地信息,可以自行搜索。现就本文具体代码进行分析。
首先是web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	xmlns="http://java.sun.com/xml/ns/javaee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <display-name></display-name>	
   <listener>   
    <listener-class>Analyze</listener-class>
  </listener>  
  <context-param>
    <description>Analyze的监听周期以秒为单位</description>
  	<param-name>period</param-name>
  	<param-value>5</param-value>
  </context-param>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
 </web-app>

<listener-class>用于设定执行监听的类,后面的<param-name>和<param-value>用来设定后续schedule()方法中所需的周期。

Analyze.java
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class Analyze implements ServletContextListener {

	private static java.util.Timer timer = null;

	public void contextDestroyed(ServletContextEvent event) {
		timer.cancel();
		event.getServletContext().log("定时器已销毁,任务执行结束");
	}

	public void contextInitialized(ServletContextEvent event) {
		timer = new java.util.Timer(true);
		javax.servlet.ServletContext ctx = event.getServletContext();
		ctx.log("定时器已启动,任务开始执行");

		//读取web.xml配置文件中的参数值
		long period = Long.valueOf((String) ctx.getInitParameter("period"))
				.longValue() * 1000;
		timer.schedule(new NewTask(event.getServletContext()), 0, period);
		//schedule方法中三个参数各自意义:所需要执行的任务;延迟时间(0表示起动Web容器(或服务器)时就立即执行此任务);任务的执行间隔时间[单位:毫秒]
	}
}

Analyze类实现了ServletContextListener接口,实现了其中的两个方法,对于contextInitialized方法,主要说明的是,通过该方法,调用Timer类执行其schedule()方法,并读取web.xml中<context-param>
信息,作为参数放在Timer.schedule()方法中,并执行任务。对于Timer.schedule()方法需要说明一下:
schedule()方法根据参数的不同,带来的效果也不一样,主要说明如下:
schedule(TimerTask task, long delay)文档说明:Schedules the specified task for execution after the specified delay。大意是在延时delay毫秒后执行task,且只执行一次
schedule(TimerTask task, long delay, long period)的注释:Schedules the specified task for repeated fixed-delay execution, beginning after the specified delay。大意是在延时delay毫秒后重复的执行task,周期是period毫秒。本文采用的是后者。
监听启动后,调用Timer类,利用schedule调用NewTask类,该类继承TimerTask类。

NewTask.java
import java.util.List;
import java.util.TimerTask;
import javax.servlet.ServletContext;
import wstate.State;

public class NewTask extends TimerTask {
	public NewTask(ServletContext servletContext) {
		this.servletContext = servletContext;
	}

	private ServletContext servletContext = null;
	private static boolean isRunning = false; // 运行标志(表示是否正在运行计划的任务)

	@Override
	public void run() {
		if (!isRunning) { // 当未执行此任务时则开始执行
			List<State> l = NodesAnalysis.GetStates();
			if (l.size() != 0) {
				servletContext.setAttribute("states", l);
			}
			isRunning = false; // 将任务执行标志设置为执行完毕
		} else {
			System.out.println("任务正在运行中");
		}
	}
}

对于Timer和TimerTask的介绍,个人觉得http://hi.baidu.com/wmqxyh/item/a386395ba03db50ce6c4a5a9所写非常详细,贴出来,感谢该作者:
Timer与TimerTask都在java.util包中,它们是与任务调度相关的类。Timer是一个定时器,可以设置成在特定时间或按特定时间周期产生信号;TimerTask负责定义所要执行的任务。将Timer和TimerTask关联起来,在Timer发出信号的时候执行TimerTask定义的任务。 TimerTask是一个抽象类,只要继承TimerTask类并实现run()方法就行了。run()方法体就是定义所要执行的任务的地方,在Timer发出信号的时候,这个run()方法就会执行。要想知道本次run()方法再何时被启动,可以使用TimerTask类中的方法long scheduledExecutionTime(),使用这个方法不会造成时间精度的误差,它比System.currentTimeMillis()更节省资源。 可以通过Timer类的schedule(...)方法来设定发出信号的特定时间或特定时间周期,并与一个TimerTask类的实例相关联。使用void scheduleAtFixedRate(...)方法可以产生固定周期信号。可以使用Timer类的cancle()方法停止Timer,不再发出信号,此时,Timer与TimerTask也就脱离关系了。也可以通过TimerTask类中的cancle()方法达到同样的目的。 有一个问题,如果run()方法里面的任务在下一次信号出现之前还没有完成怎么办?有下面两种情况:(1)在用schedule(...)方法调度时:如果run()方法里面的任务在信号周期之内完成,那么下一次的信号就会在预定的时间产生;如果run()方法里面的任务在下一次信号出现之前还没有完成,那么下一次的信号就会延迟,并且上一个run()执行完成之后就立即产生下一个信号,下一个run()立即开始执行。(2)在用scheduleAtFixedRate(...)方法调度时:如果run()方法里面的任务在信号周期之内完成,那么下一次的信号就会在预定的时间产生;如果run()方法里面的任务在下一次信号出现之前还没有完成,那么等到run()执行完成之后,下一次的信号产生就尽量赶上本应该产生信号的时间,意思就是如果有累计的(从第一次信号算起)超时存在,那么下一次的信号就会在run()方法执行完之后立即产生;如果没有累计的超时存在,那么就按照周期产生信号。 另外,根据规范,如果调用cancle()时run()仍在执行,那么run()会继续执行直到完成。
感谢以下作者的辛勤劳动。
http://www.cnblogs.com/soarwell/archive/2009/03/18/1415206.html
http://www.cnblogs.com/secret1998/archive/2010/05/26/1744432.html
http://blog.csdn.net/blueling51/article/details/6931026
http://hi.baidu.com/wmqxyh/item/a386395ba03db50ce6c4a5a9

你可能感兴趣的:(java,tomcat,timer,timertask)