简单封装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类即可。运行结果如下:




目录
相关文章
|
网络协议 Linux Go
分享一个go开发的工具-SNMP Server
分享一个go开发的工具-SNMP Server
331 0
|
机器学习/深度学习 关系型数据库 MySQL
大模型中常用的注意力机制GQA详解以及Pytorch代码实现
GQA是一种结合MQA和MHA优点的注意力机制,旨在保持MQA的速度并提供MHA的精度。它将查询头分成组,每组共享键和值。通过Pytorch和einops库,可以简洁实现这一概念。GQA在保持高效性的同时接近MHA的性能,是高负载系统优化的有力工具。相关论文和非官方Pytorch实现可进一步探究。
1618 4
|
7月前
|
人工智能 JSON API
Nacos 发布 MCP Registry,实现存量应用接口“0改动”升级到 MCP 协议
MCP(Model Calling Protocol)生态快速发展,Nacos作为MCP Registry,通过与Higress网关结合,实现“0代码”将存量API转化为MCP协议接口。本文详细解析了Nacos如何快速构建MCP Server,包括工具列表暴露、协议转换原理及优势。同时,通过高德API实例演示“0改动”适配流程。Nacos 3.0正式发布,定位AI应用服务管理平台,支持动态服务发现与配置管理,助力MCP生态发展。欢迎参与社区共建!
1244 1
|
机器学习/深度学习 Python
Py之yacs:yacs的简介、安装、使用方法之详细攻略
Py之yacs:yacs的简介、安装、使用方法之详细攻略
Py之yacs:yacs的简介、安装、使用方法之详细攻略
|
12月前
|
存储 安全 关系型数据库
Blossom:开源私有部署的markdown笔记软件
Blossom 是一款功能强大的开源笔记软件,支持私有部署,可将笔记、图片、个人计划等数据保存在自己的服务器中,并实现实时同步。它还具备动态博客功能,方便记录和分享内容。Blossom 支持多种设备,提供完善的文件管理、快速迁移和丰富的附加功能,是个人知识管理和博客展示的理想选择。
578 7
Blossom:开源私有部署的markdown笔记软件
|
缓存 数据挖掘 API
淘宝商品类目API的获取与应用探索
淘宝商品类目API是淘宝开放平台提供的关键服务,允许开发者获取淘宝商品的类目信息,包括根类目、子类目及属性信息。本文介绍API的获取方法、应用场景及使用技巧,帮助电商从业者和开发者更好地利用类目数据,提升商品管理、搜索推荐及数据分析等能力。
692 1
|
SQL 物联网 大数据
TDengine的主要特性有哪些?
【5月更文挑战第13天】TDengine的主要特性有哪些?
341 10
|
供应链 搜索推荐 物联网
云上智能供应链:重塑物流与供应链管理的未来图景
云上智能供应链作为供应链管理领域的创新实践,正以其独特的优势和潜力引领着供应链管理的未来发展。通过数字化、智能化和集成化的手段,云上智能供应链不仅提升了供应链的整体效能和竞争力,还为企业带来了更多的商业价值和市场机遇。我们有理由相信,在未来的日子里,云上智能供应链将成为推动企业转型升级和实现可持续发展的重要力量。
1882 0
|
机器学习/深度学习 数据采集 自然语言处理
利用机器学习进行情感分析:技术与应用
本文将探讨机器学习在情感分析领域的应用。首先,我们将介绍情感分析的基本概念和重要性。接着,我们将详细讨论如何使用机器学习技术进行情感分析,包括数据预处理、特征提取、模型训练和评估等步骤。最后,我们将展示一些实际的情感分析应用案例。
|
存储 弹性计算 运维
阿里云服务器ECS经济型e实例特性、使用、价格和注意事项
阿里云服务器ECS经济型e实例特性、使用、价格和注意事项,阿里云服务器ECS推出经济型e系列,经济型e实例是阿里云面向个人开发者、学生、小微企业,在中小型网站建设、开发测试、轻量级应用等场景推出的全新入门级云服务器,CPU采用Intel Xeon Platinum架构处理器
下一篇
开通oss服务