如何写一个自动运行定时任务程序,我知道有很多人喜欢quartz,我也喜欢,但是我喜欢简单。
当有一个需求时,第一件事就是找方法,比如,我现在需要做一个定时程序,定时触发,定时执行。
因为我的需求很简单,所以我决定使用Timer和TimerTask就可以解决我的需求,于是,我先创建了一个Web项目,
然后,当程序启动时,运行程序自动启动,第一想到的是servlet,
开始撸代码了,
第一个创建HttpServlet的SysLoad类
加入两个仓库,用来存放Timer和TimerTask
public static List<TaskJob> tasks = new ArrayList<TaskJob>();
private static List<Timer> timers = new ArrayList<Timer>();
当然你也可以吧初始化放到:
public SysLoad() {
tasks = new ArrayList<TaskJob>();
timers = new ArrayList<Timer>();
logger.info("建立系统启动自动服务");
}
第二部,在初始化HttpServlet时,自动初始化所有定时任务,
这时候,你需要考虑,是在这里写死方法,还是程序动态生成。
像我这样懒得人,一般都会使用程序动态生成,
找了一个ClassUtil类,通过查找指定包下面所有类的方法来初始化。然后就有了下面的代码。
public void init() throws ServletException {
logger.info("系统启动自动服务");
String startTask = getInitParameter("startTask");
// 启动定时器
if (startTask.equals("true")) {
logger.info("系统开启自动运行服务");
List<Class<?>> jobclasses = ClassUtil.getClasses("cn.wangkai.task.job");
logger.info("获取"+jobclasses.size()+"任务!");
for (Class<?> jobclass : jobclasses) {
try {
TaskJob task = (TaskJob) jobclass.newInstance();
task.getRunTime();
Timer timer = new Timer();
timer.schedule( (TimerTask) task,1000*task.getRunTime(), 1000*task.getRunTime());
tasks.add(task);
timers.add(timer);
logger.info(task.getTaskname()+"开始工作,间隔"+task.getRunTime()+"秒!");
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
初始化完毕后,还需要讲初始化的数据在关闭时后reload时销毁,增加如下代码
public void destroy() {
logger.info("正在关闭自动服务");
try{
for (TaskJob task : tasks) {
if(task!=null)tasks.remove(task);
}
}catch(Exception e){
}
try{
for (Timer timer : timers) {
if (timer != null) {
timer.cancel();
}
timers.remove(timer);
}
}catch(Exception e){
}
logger.info("================tasks="+tasks.size()+"=====================");
logger.info("================timers="+timers.size()+"=====================");
super.destroy();
}
第三步加入web.xml
<servlet>
<servlet-name>SysLoad</servlet-name>
<servlet-class>cn.wangkai.task.taskjob.SysLoad</servlet-class>
<init-param>
<param-name>startTask</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>intervalTime</param-name>
<param-value>1</param-value>
</init-param>
<load-on-startup>20</load-on-startup>
</servlet>
剩下就是如何实现TaskJob
第一步创建一个接口,
public interface TaskJob {
/**
* 获取服务运行描述信息
* @return
*/
public String getMessage();
/**
* 设置服务运行描述信息
* @param message
*/
public void setMessage(String message);
/**
* 得到任务名称
* @return
*/
public String getTaskname() ;
/**
* 设置当前任务名称
* @param taskname
*/
public void setTaskname(String taskname);
/**
* 获取当前任务运行时间
* @return
*/
public int getRunTime() ;
/**
* 设置当前任务运行时间
* @param runTime
*/
public void setRunTime(int runTime);
/**
* 任务是否暂停
* @return
*/
public boolean isRunning() ;
/**
* 设置任务暂停状态
* @param isRunning
*/
public void setRunning(boolean isRunning) ;
}
第二部创建一个实现方法
import java.util.TimerTask;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import org.apache.commons.lang.math.NumberUtils;
import cn.wangkai.util.ProUtil;
import cn.wangkai.util.StackTraceUtils;
public class TaskJobImp extends TimerTask implements TaskJob {
private static Logger logger = Logger.getAnonymousLogger();
public TaskJobImp(ServletContext context) {
}
//运行描述
private String Message=null;
//程序名称
private String taskname=getClass().getName();
//多长时间,时间秒
private int RunTime=10;
//正常运行
private boolean isRunning = true;
public TaskJobImp() {
}
private ServletContext context;
public void setContext(ServletContext context) {
logger.info(getTaskname()+"任务开始启动!");
this.context = context;
}
public void run() {
if (isRunning()) {
Message = getTaskname()+"上次启动时间\n\t";
try {
Message = Message+"程序运行中....\n\t";
//TODO
Message = Message+"任务启动完成 。\n\t";
} catch (Exception e) {
Message = Message+"出现错误:"+StackTraceUtils.getStackTrace(e);
}finally{
}
logger.info(Message);
}
}
public ServletContext getContext() {
return context;
}
public String getMessage() {
return Message;
}
public void setMessage(String message) {
Message = message;
}
public String getTaskname() {
return taskname;
}
public void setTaskname(String taskname) {
this.taskname = taskname;
}
public int getRunTime() {
RunTime = NumberUtils.toInt(ProUtil.read(getClass().getName()+"_runtime",RunTime+"",true),RunTime);
return RunTime;
}
public void setRunTime(int runTime) {
RunTime = runTime;
}
public boolean isRunning() {
return isRunning;
}
public void setRunning(boolean isRunning) {
this.isRunning = isRunning;
}
public boolean cancel() {
logger.info(getTaskname()+"正在退出!");
return super.cancel();
}
第四步,我们在cn.wangkai.task.job包中创建一个测试类吧
package cn.wangkai.task.job;
import java.util.logging.Logger;
import cn.wangkai.task.taskjob.TaskJobImp;
import cn.wangkai.util.StackTraceUtils;
public class SampleTask extends TaskJobImp{
private String Message=null;
private static Logger logger = Logger.getAnonymousLogger();
public void run() {
if (isRunning()) {
Message = "\r\n"+getTaskname()+"上次启动时间\r\n";
try {
Message = Message+"程序运行中....\r\n";
//TODO 这里就是你需要实现的代码。
Message = Message+"任务启动完成 。";
} catch (Exception e) {
Message = Message+"出现错误:"+StackTraceUtils.getStackTrace(e);
}finally{
}
logger.info(Message);
}
}
}
好了,代码撸完了,可以睡觉了,
其他工具方法
ClassUtil.java(网上找的,我也懒得写)
public static void main(String[] args) throws Exception {
List<Class<?>> classes = ClassUtil.getClasses("cn.wangkai.task.job");
for (Class<?> clas : classes) {
System.out.println(clas.getName());
}
}
/**
* 取得某个接口下所有实现这个接口的类
*/
public static List<Class<?>> getAllClassByInterface(Class<?> c) {
List<Class<?>> returnClassList = null;
if (c.isInterface()) {
// 获取当前的包名
String packageName = c.getPackage().getName();
// 获取当前包下以及子包下所以的类
List<Class<?>> allClass = getClasses(packageName);
if (allClass != null) {
returnClassList = new ArrayList<Class<?>>();
for (Class<?> classes : allClass) {
// 判断是否是同一个接口
if (c.isAssignableFrom(classes)) {
// 本身不加入进去
if (!c.equals(classes)) {
returnClassList.add(classes);
}
}
}
}
}
return returnClassList;
}
/*
* 取得某一类所在包的所有类名 不含迭代
*/
public static String[] getPackageAllClassName(String classLocation, String packageName) {
// 将packageName分解
String[] packagePathSplit = packageName.split("[.]");
String realClassLocation = classLocation;
int packageLength = packagePathSplit.length;
for (int i = 0; i < packageLength; i++) {
realClassLocation = realClassLocation + File.separator + packagePathSplit[i];
}
File packeageDir = new File(realClassLocation);
if (packeageDir.isDirectory()) {
String[] allClassName = packeageDir.list();
return allClassName;
}
return null;
}
/**
* 从包package中获取所有的Class
*
* @param pack
* @return
*/
public static List<Class<?>> getClasses(String packageName) {
// 第一个class类的集合
List<Class<?>> classes = new ArrayList<Class<?>>();
// 是否循环迭代
boolean recursive = true;
// 获取包的名字 并进行替换
String packageDirName = packageName.replace('.', '/');
// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循环迭代下去
while (dirs.hasMoreElements()) {
// 获取下一个元素
URL url = dirs.nextElement();
// 得到协议的名称
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服务器上
if ("file".equals(protocol)) {
// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式扫描整个包下的文件 并添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定义一个JarFile
JarFile jar;
try {
// 获取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
// 从此jar包 得到一个枚举类
Enumeration<JarEntry> entries = jar.entries();
// 同样的进行循环迭代
while (entries.hasMoreElements()) {
// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/开头的
if (name.charAt(0) == '/') {
// 获取后面的字符串
name = name.substring(1);
}
// 如果前半部分和定义的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"结尾 是一个包
if (idx != -1) {
// 获取包名 把"/"替换成"."
packageName = name.substring(0, idx).replace('/', '.');
}
// 如果可以迭代下去 并且是一个包
if ((idx != -1) || recursive) {
// 如果是一个.class文件 而且不是目录
if (name.endsWith(".class") && !entry.isDirectory()) {
// 去掉后面的".class" 获取真正的类名
String className = name.substring(packageName.length() + 1, name.length() - 6);
try {
// 添加到classes
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
/**
* 以文件的形式来获取包下的所有Class
*
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
List<Class<?>> classes) {
// 获取此包的目录 建立一个File
File dir = new File(packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
public boolean accept(File file) {
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
// 循环所有文件
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
// 如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0, file.getName().length() - 6);
try {
// 添加到集合中去
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
ProUtil.java 本人原创
详见:http://www.oschina.net/code/snippet_54124_55380
StackTraceUtils.java 很久以前写的,一直在用
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
public class StackTraceUtils {
/**
* 将异常堆栈转换为字符串
* @param aThrowable 异常
* @return String
*/
public static String getStackTrace(Throwable aThrowable) {
final Writer result = new StringWriter();
final PrintWriter printWriter = new PrintWriter(result);
aThrowable.printStackTrace(printWriter);
return result.toString();
}
}