简单封装quartz实现任务调度的配置和管理


在实际的工作和开发过程中,经常遇到有需要定时任务的场景,比如定时发送邮件,定时上传文件,定时下载文件等。当然定时任务的处理也有很多种方式.本文主要写的是对quartz的简单封装,实现方便的调用。

主要思路:

定时任务的类和定时表达式配置在自定义的配置文件中,系统启动时,读取配置文件,加载需要执行的类,并启动quartz服务。

项目结构如下:
简单封装quartz实现任务调度的配置和管理

主要包括如下类和配置文件:

1. 任务调度接口


package com.yanek.easytask.core;

/**
 * 任务调度接口
 * @author yanek
 *
 */
public interface Task {
	
	/**
	 * 所有都要实现该执行方法,任务被调度时会调用
	 */
	void execute();
	
	/**
	 * 打断执行方法
	 */
	void interrupt();

}



2. 任务配置实体类


package com.yanek.easytask.core;

/**
 * 任务配置实体类
 * @author yanek
 *
 */
public class TaskConfig {
	
	private String name; //任务名称
	private boolean activity = true; //是否被激活
	private String className; //任务执行的类全名
	private String scanPeriod; //任务执行的定时表达式配置
	
	public String getScanPeriod() {
		return scanPeriod;
	}
	public void setScanPeriod(String scanPeriod) {
		this.scanPeriod = scanPeriod;
	}

	
	public String getTaskClass() {
		return TaskClass;
	}
	public void setTaskClass(String taskClass) {
		TaskClass = taskClass;
	}
	private String TaskClass;
	

	public boolean isActivity()
	{
		return activity;
	}


	public void setActivity(boolean activity)
	{
		this.activity = activity;
	}
	

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}
	
	public String getClassName() {
		return className;
	}
	public void setClassName(String className) {
		this.className = className;
	}

}


3. 任务配置xml解析类


package com.yanek.easytask.core;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;


import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;



/**
 * 任务配置xml解析类
 * @author yanek
 *
 */
public class XmlReader {
	
	//配置文件路径	
	public final static String configFileName = "/conf/taskconfig.xml";


	public static void main(String[] args) {

		XmlReader.getTasks();
	}

	public static List getTasks() {

		List<TaskConfig> tasks = new ArrayList<TaskConfig>();
		
		System.out.println("load task config start...");
		
		File file = new File(Constants.APPLICATION_ROOT_DIR + configFileName);

		if (file.exists() && !file.isDirectory()) {

			try {
				SAXBuilder sx = new SAXBuilder();
				Document doc = sx.build(file);
				Element rootelement = doc.getRootElement();
				
				
					List<Element> childs = rootelement.getChildren();
					for (int i = 0; i < childs.size(); i++) {
						TaskConfig taskConfig = new TaskConfig();
						
						System.out.println("name:"+childs.get(i).getChildText("name"));
						System.out.println("activity:"+childs.get(i).getChildText("activity"));
						System.out.println("scanPeriod:"+childs.get(i).getChildText("scanPeriod"));
						System.out.println("className:"+childs.get(i).getChildText("className"));
						
						taskConfig.setName(childs.get(i).getChildText("name"));
						
						if (childs.get(i).getChildText("activity").equals("true"))
						{
							taskConfig.setActivity(true);
						}
						else
						{
							taskConfig.setActivity(false);
						}
						taskConfig.setScanPeriod(childs.get(i).getChildText("scanPeriod"));
						taskConfig.setTaskClass(childs.get(i).getChildText("className"));
						tasks.add(taskConfig);
				}
			} catch (NumberFormatException e) {
				e.printStackTrace();
			} catch (JDOMException e) {

				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}

		} else {
			System.out.println("task config file no exist!");

		}
		System.out.println("load task config end !");
		return tasks;

	}
	


}


4.任务工厂类


package com.yanek.easytask.core;

/**
 * 任务工厂类
 * @author yanek
 *
 */
public class TaskFactory {

	 /**
	  * 根据任务配置对象,创建任务类的对象实例,采用反射。
	  * @param config
	  * @return
	  */
	 public static Task createTask(TaskConfig config) {
		String classname = config.getTaskClass();
		Task task = null;
		try {
			task = (Task) Class.forName(classname).newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return task;
	}

}



5. 任务适配器:


package com.yanek.easytask.core;

import org.quartz.InterruptableJob;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.StatefulJob;
import org.quartz.UnableToInterruptJobException;

public class TaskAdapter implements StatefulJob, InterruptableJob {
	public TaskAdapter() {
	}

	private Task task = null;

	public void execute(JobExecutionContext context)
			throws JobExecutionException {
		Object taskObj = context.getJobDetail().getJobDataMap().get(
				Constants.JOB_NAME);
		System.out.println("job类型:" + taskObj);
		if (taskObj instanceof Task) {
			task = (Task) taskObj;
			task.execute();
		} else {
			System.out.println("未知的job类型:" + taskObj.getClass());
		}
	}


	public void interrupt() throws UnableToInterruptJobException {
		task.interrupt();
	}
}


6. 系统常量类


package com.yanek.easytask.core;

/**
 * 系统常量类
 */
public class Constants
{
	/**
	 * 应用程序根目录
	 */
	public static String APPLICATION_ROOT_DIR = null;
	
	//任务实例名称 
	public static final String JOB_NAME = "TASK_JOB_INSTANTS";
}




7. 系统启动类


package com.yanek.easytask.core;


import java.text.ParseException;
import java.util.List;

import org.quartz.CronTrigger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;

/**
 * 调度任务服务程序启动入口
 * 
 * 启动时需要设置-Dappdir参数,应当设置为-Dappdir=..
 * -Dconfig=配置文件文件绝对路径 可选的属性
 * 
 * 任务调度使用Quartz实现
 * 
 */
public class Startup
{

	
	/**
	 * @param args
	 */
	public static void startup() {

		

	}
	
	public static void main(String[] args)
		throws Exception
	{
		try
		{

			String appdir = System.getProperty("user.dir");
			
			System.out.println(appdir);

			
			//设置系统变量
			Constants.APPLICATION_ROOT_DIR = appdir;
			
			//System.setProperty("appdir","E:\\work\\task");

			//初始化调度服务器
			initSchedulerServer();
			
			List<TaskConfig> tasks=XmlReader.getTasks();
			
			if(tasks == null || tasks.size() <=0)
				throw new Exception("没有配置任务实例!");
			
			for(int i=0;i<tasks.size();i++)
			{
				TaskConfig tc=tasks.get(i);
				try
				{
					if(tc.isActivity())
					{
						scheduleJob(tc);
						System.out.println("任务实例["+tc.getName()+"]已加入调度.");
					}
					else
					{
						System.out.println(tc.getName()+" 任务实例Activity=false 不进行处理");
					}
				}
				catch(Exception cve)
				{
					//配置错误忽略这个任务
					System.out.println(cve);
				}
			}
			/*启动调度服务器*/
			startScheduleServer();
		}
		catch(Exception ex)
		{
	
			System.out.println("启动出现异常,3秒钟后自动关闭");
			Thread.sleep(1000*3);
			if(server!=null)
				server.shutdown();
			
		}
		
	}
	
	//----------------------任务调度处理----------------------------
	//任务调度服务
	private static Scheduler server = null;
	
	//任务id
	private static int jobID = 0;
	
	private static void initSchedulerServer()
		throws SchedulerException
	{
		if(server == null)
		{
			SchedulerFactory sf = new StdSchedulerFactory();
			server = sf.getScheduler();
		}
	}
	
	/**
	 * 调度一个任务
	 */
	private static void scheduleJob(TaskConfig conf) 
		throws SchedulerException, ParseException
	{
		jobID++;
		
		Task deliveryJob = TaskFactory.createTask(conf);
		
		//将具体的任务实例存储到jobdetail中,这样每次触发jobadapter时,都会调用我们声明的deliveryJob这个实例了。
		String jobName = conf.getName()+"_job"+jobID;
		JobDetail jobAdapter = new JobDetail(jobName,
				"Group", TaskAdapter.class);
		JobDataMap data = new JobDataMap();
		data.put(Constants.JOB_NAME, deliveryJob);
		jobAdapter.setJobDataMap(data);

		//注意下面需要5个参数
		CronTrigger trigger = new CronTrigger("trigger_"+jobID,"Group", 
				jobName, "Group",conf.getScanPeriod());
		server.addJob(jobAdapter, true);
		server.scheduleJob(trigger);	

	}
	
	/**
	 * 启动调度服务
	 * @throws SchedulerException
	 */
	private static void startScheduleServer() throws SchedulerException
	{
		if(server != null)
			server.start();
	}
}


配置文件:

taskconfig.xml



<?xml version="1.0" encoding="UTF-8"?>
<taskconfig>
    	<task>
	    	<name>test1</name>
			<activity>true</activity>
			<scanPeriod>0/5 * * * * ?</scanPeriod>
	    	<className>com.yanek.easytask.demo.TaskA</className>
    	</task>
    	
    	 <task>
	    	<name>test任务2</name>
			<activity>true</activity>
			<scanPeriod>0/5 * * * * ?</scanPeriod>
	    	<className>com.yanek.easytask.demo.TaskDemo</className>
    	</task>
    	

</taskconfig>


几点说明:

1. 上述代码可以封装打包为jar包。然后在其他项目中使用即可。

2. 使用该jar包事先任务开发时,只需要编写一个Task接口的实现类,配置在配置文件中即可。

实例调用如下:


package com.yanek.easytask.demo;

import com.yanek.easytask.core.Task;
import com.yanek.easytask.core.TaskConfig;


public class TaskDemo implements Task {

	
	//唯一构造函数
	public TaskDemo()
	{
		
		
	}

	@Override
	public void execute() {
		System.out.println(" TaskDemo execute");
	}

	@Override
	public void interrupt() {
		System.out.println("TaskDemo interrupt");
	}

}



在配置文件中增加如下配置:


    	 <task>
	    	<name>test任务2</name>
			<activity>true</activity>
			<scanPeriod>0/5 * * * * ?</scanPeriod>
	    	<className>com.yanek.easytask.demo.TaskDemo</className>
    	</task>


启动Startup类即可。运行结果如下:

简单封装quartz实现任务调度的配置和管理


上一篇:PHP实现MVC开发得最简单的方法——单点入口


下一篇:php中几种循环去记录集数据的方法