在实际的工作和开发过程中,经常遇到有需要定时任务的场景,比如定时发送邮件,定时上传文件,定时下载文件等。当然定时任务的处理也有很多种方式.本文主要写的是对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类即可。运行结果如下: