大多数定时任务还是写在.xml配置里面的,这样写的最大缺点就是如果因为公司需要把定时任务执行的时间、或者是执行类更换,就需要修改.xml代码并重新提交发布版本才行。为此出了一种关联数据库动态设置定时任务技术,并可通过业务逻辑修改定时任务。
1. 我们需要在父项目的pom.xml文件中加入jar依赖:
<dependency><!--定时器-->
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.11</version>
</dependency>
2. 定义任务实体类
TaskProgramJob任务逻辑实体类,实现具体任务逻辑
package dmp.platform.model;
import dmp.platform.dao.VRepBookDao;
import dmp.platform.service.VTaskConfigService;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import java.sql.Date;
import java.util.HashMap;
import java.util.Map;
public class TaskProgramJob implements Job {
private VRepBookDao vRepBookDao;
private VTaskConfigService vTaskConfigService;
@Override
public void execute(JobExecutionContext context) {
//使用归并的JobDataMap
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String taskProgram = dataMap.getString("taskProgram");
System.out.println("执行存储过程-----------> "+taskProgram+ "\t"+ System.currentTimeMillis());
vRepBookDao = (VRepBookDao)dataMap.get("vRepBookDao"); //通过JobDataMap获取实例化对象赋值给属性
vTaskConfigService = (VTaskConfigService)dataMap.get("vTaskConfigService");
//执行存储过程参数
Map map = new HashMap<>();
map.put("date",new Date(System.currentTimeMillis())); //系统时间
map.put("procName",taskProgram); //存储过程名
//获取组织Id
String orgId = vRepBookDao.queryOrg_idByUsername(dataMap.getString("tcChangeuser"));
map.put("orgId",orgId);
//执行存储过程
boolean flag = vTaskConfigService.executeStoredProcedure(map);
if(flag){
System.out.println("任务执行成功");
}else{
System.out.println("任务执行失败");
}
}
}
QuartzService初始化任务类,实现InitializingBean接口,在项目启动时自动运行一次重写的afterPropertiesSet方法,实现任务初始化
package dmp.platform.service;
import dmp.platform.dao.VRepBookDao;
import dmp.platform.model.TaskProgramJob;
import dmp.platform.model.VTaskConfig;
import dmp.platform.util.QuartzUtil;
import org.apache.log4j.Logger;
import org.quartz.CronTrigger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class QuartzService implements InitializingBean {
private Logger log = Logger.getLogger(QuartzService.class);
//默认的任务组名,触发器组名
@Value("${quartz.job_group_name}")
private String JOB_GROUP_NAME ;
@Value("${quartz.trigger_group_name}")
private String TRIGGER_GROUP_NAME ;
@Resource
private VTaskConfigService vTaskConfigService;
@Resource
private VRepBookDao vRepBookDao;
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("【系统启动】所有定时任务开启...");
// 1- 根据条件从数据库获取定时任务详情
List<VTaskConfig> jobList = vTaskConfigService.getTaskConfig();
if (jobList == null || jobList.isEmpty()) {
return;
}
// 2- 获取Scheduler
Scheduler scheduler = QuartzUtil.scheduler;
// 3- 循环添加数据库中的任务
for (VTaskConfig j : jobList) {
if (j.getIfStart()==1) {//启动定时器
//任务设置
JobDetail jobDetail = new JobDetail(j.getTaskId(), JOB_GROUP_NAME, TaskProgramJob.class);// 任务名,任务组,任务执行类
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("taskProgram", j.getTaskProgram());
jobDataMap.put("tcChangeuser", j.getTcChangeuser()); //任务创建者
jobDataMap.put("vRepBookDao", vRepBookDao); //在quartz中无法通过注解注入VRepBookDao属性,需要将spring注解创建的实例化bean通过JobDataMap传到job对象中
jobDataMap.put("vTaskConfigService", vTaskConfigService); //也不能new对象,因为new出来的实例中的属性无法通过spring注解自动注入
jobDetail.setJobDataMap(jobDataMap);
// 触发器
CronTrigger trigger = new CronTrigger(j.getTaskId(), TRIGGER_GROUP_NAME);// 触发器名,触发器组
String cronExpression = QuartzUtil.getCronExpression(j);
System.out.println("定时时间: " + cronExpression);
trigger.setCronExpression(cronExpression);// 触发器时间设定
scheduler.scheduleJob(jobDetail, trigger);
// 启动
if (!scheduler.isShutdown()) {
scheduler.start();
}
System.out.println("添加定时任务[jobName] - " + j.getTaskProgram() + "---------" + j.getTaskId() + "&" + JOB_GROUP_NAME + "&" + TRIGGER_GROUP_NAME + "||||scheduler: " + scheduler);
log.info("添加定时任务[jobName] - " + j.getTaskProgram());
}
}
}
}
spring配置文件 默认的任务组名,触发器组名 扫描任务类QuartzUtil和TaskProgramJob,装配到spring的Bean容器中 QuartzUtil任务工具类,实现任务的增,删,改
package dmp.platform.util;
import dmp.platform.dao.VRepBookDao;
import dmp.platform.model.TaskProgramJob;
import dmp.platform.model.VTaskConfig;
import dmp.platform.service.VTaskConfigService;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component //标识此类自动装配到spring的bean容器中
public class QuartzUtil {
//默认的任务组名,触发器组名
@Value("${quartz.job_group_name}")
private String JOB_GROUP_NAME ;
@Value("${quartz.trigger_group_name}")
private String TRIGGER_GROUP_NAME ;
public static Scheduler scheduler;
//静态代码块,在scheduler静态成员变量初始化之后执行一次,确保scheduler的唯一性
static {
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
try {
scheduler = schedulerFactory.getScheduler();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
@Resource
private VTaskConfigService vTaskConfigService;
@Resource
private VRepBookDao vRepBookDao;
/**
* SchedulerFactory使用获取Scheduler
*
* @return
* @throws Exception
*/
/*private static Scheduler getScheduler() throws Exception {
SchedulerFactory scheduler = new StdSchedulerFactory();
return scheduler.getScheduler();
}*/
/**
* @Author: xzh
* @Date: Created in 09:56 2019/2/18 0018
* @Description: 添加默认的任务组名,触发器组名的定时器
*/
//添加定时任务
public void addJob(VTaskConfig j) {
try {
JobDetail jobDetail = new JobDetail(j.getTaskId(), JOB_GROUP_NAME, TaskProgramJob.class);// 任务名,任务组,任务执行类
JobDataMap jobDataMap = new JobDataMap();
/*传递实例化对象
*在quartz中无法通过注解注入VRepBookDao属性,需要将spring注解创建的实例化bean通过JobDataMap传到job对象中
*也不能new对象,因为new出来的实例中的属性无法通过spring注解自动注入 */
jobDataMap.put("vRepBookDao", vRepBookDao);
jobDataMap.put("vTaskConfigService", vTaskConfigService);
//传递参数
jobDataMap.put("taskProgram", j.getTaskProgram());
jobDataMap.put("tcChangeuser", j.getTcChangeuser()); //任务创建者
jobDetail.setJobDataMap(jobDataMap);
// 触发器
CronTrigger trigger = new CronTrigger(j.getTaskId(), TRIGGER_GROUP_NAME);// 触发器名,触发器组
String cronExpression = getCronExpression(j);
trigger.setCronExpression(cronExpression);// 触发器时间设定
scheduler.scheduleJob(jobDetail, trigger);
// 启动
if (!scheduler.isShutdown()) {
scheduler.start();
}
System.out.println("添加定时任务[jobName] - " + j.getTaskId() + "定时: " + cronExpression);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//修改定时任务
public void modifyJob(VTaskConfig j) {
try {
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(j.getTaskId(),TRIGGER_GROUP_NAME);
if (trigger == null) {
return;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
System.out.println("修改定时任务[jobName] - " + j.getTaskId());
removeJob(j.getTaskId());
addJob(j);
}
/**
* @Description: 移除一个任务(使用默认的任务组名,触发器名,触发器组名)
*/
public void removeJob(String jobName) {
try {
scheduler.pauseTrigger(jobName, TRIGGER_GROUP_NAME);// 停止触发器
scheduler.unscheduleJob(jobName, TRIGGER_GROUP_NAME);// 移除触发器
scheduler.deleteJob(jobName, JOB_GROUP_NAME);// 删除任务
} catch (Exception e) {
throw new RuntimeException(e);
}
System.out.println("删除定时任务[jobName] - " + jobName);
}
/**
* @Description: 解析VTaskConfig,获取定时器设置时间
*/
public static String getCronExpression(VTaskConfig v){
StringBuilder cron = new StringBuilder("0 0 0 ");//0秒0分0时
String day = v.getTriggerDay(); //日
String week = v.getTriggerWeek(); //周
String month = v.getTriggerMonth(); //月
switch(v.getTriggerType()){
case 0://按日
cron.append(day); //日
cron.append(" * ?"); //每月
break;
case 1://按周
cron.append("? * ");
int i = 0;
if(week.charAt(0)=='1'){
System.out.println();
cron.append("2");
i++;
}
if(week.charAt(1)=='1'){
if(i==1){
cron.append(",");
i--;//归零
}
cron.append("3");
i++;
}
if(week.charAt(2)=='1'){
if(i==1){
cron.append(",");
i--;//归零
}
cron.append("4");
i++;
}
if(week.charAt(3)=='1'){
if(i==1){
cron.append(",");
i--;//归零
}
cron.append("5");
i++;
}
if(week.charAt(4)=='1'){
if(i==1){
cron.append(",");
i--;//归零
}
cron.append("6");
i++;
}
if(week.charAt(5)=='1'){
if(i==1){
cron.append(",");
i--;//归零
}
cron.append("7");
i++;
}
if(week.charAt(6)=='1'){
if(i==1){
cron.append(",");
i--;//归零
}
cron.append("1");
i++;
}
break;
case 2://按月
cron.append(day);//日
cron.append(" * ?");
break;
case 3://按季
Integer mon1 = Integer.valueOf(month);
cron.append(day);//日
cron.append(" ");
cron.append(mon1);
cron.append(",");
cron.append(mon1+3);
cron.append(",");
cron.append(mon1+6);
cron.append(" ?");
break;
case 4://按半年
Integer mon2 = Integer.valueOf(month);
cron.append(day);//日
cron.append(" ");
cron.append(mon2);
cron.append(",");
cron.append(mon2+6);
cron.append(" ?");
break;
case 5://按年
cron.append(day);//日
cron.append(" ");
cron.append(month);
cron.append(" ?");
break;
}
return cron.toString();
}
}
VTaskConfig任务属性实体类,任务的属性,时间等参数
package dmp.platform.model; import dmp.framework.model.BaseModel; import java.sql.Date; public class VTaskConfig extends BaseModel { /** * serialVersionUID. */ private static final long serialVersionUID = -1L; private Integer triggerType; //触发方式 0:按日 1:按周 2:按月 3:按季 4按半年 5:按年 private String triggerMonth; //触发周期_月份 private String triggerDay; //周期_日期 private String triggerWeek; //触发周期_周:从周一开始,周日结束格式0000000,0:不触发 1:触发 private Integer ifStart; //启动标志 0:停止 1:启动 private String taskId; //任务编号 private String taskProgram; //对应程序 private String taskIdRely; //依赖任务编号 private String taskType; //任务大类 0:业务任务 1:初始化任务 private Date tcCreatedate; //创建日期 private Date tcChangedate; //修改日期 private String tcCreateuser; //创建人 private String tcChangeuser; //修改人 /** * 取得"触发方式 0:按日 1:按周 2:按月 3:按季 4按半年 5:按年" * @return 返回"触发方式 0:按日 1:按周 2:按月 3:按季 4按半年 5:按年" */ public Integer getTriggerType(){ return this.triggerType; } /** * 设置"触发方式 0:按日 1:按周 2:按月 3:按季 4按半年 5:按年"的值 * @param triggerType 触发方式 0:按日 1:按周 2:按月 3:按季 4按半年 5:按年 */ public void setTriggerType(Integer triggerType){ this.triggerType = triggerType; } /** * 取得"触发周期_月份" * @return 返回"触发周期_月份" */ public String getTriggerMonth(){ return this.triggerMonth; } /** * 设置"触发周期_月份"的值 * @param triggerMonth 触发周期_月份 */ public void setTriggerMonth(String triggerMonth){ this.triggerMonth = triggerMonth; } /** * 取得"周期_日期" * @return 返回"周期_日期" */ public String getTriggerDay(){ return this.triggerDay; } /** * 设置"周期_日期"的值 * @param triggerDay 周期_日期 */ public void setTriggerDay(String triggerDay){ this.triggerDay = triggerDay; } /** * 取得"触发周期_周:从周一开始,周日结束格式0000000,0:不触发 1:触发" * @return 返回"触发周期_周:从周一开始,周日结束格式0000000,0:不触发 1:触发" */ public String getTriggerWeek(){ return this.triggerWeek; } /** * 设置"触发周期_周:从周一开始,周日结束格式0000000,0:不触发 1:触发"的值 * @param triggerWeek 触发周期_周:从周一开始,周日结束格式0000000,0:不触发 1:触发 */ public void setTriggerWeek(String triggerWeek){ this.triggerWeek = triggerWeek; } /** * 取得"启动标志 0:停止 1:启动" * @return 返回"启动标志 0:停止 1:启动" */ public Integer getIfStart(){ return this.ifStart; } /** * 设置"启动标志 0:停止 1:启动"的值 * @param ifStart 启动标志 0:停止 1:启动 */ public void setIfStart(Integer ifStart){ this.ifStart = ifStart; } /** * 取得"任务编号" * @return 返回"任务编号" */ public String getTaskId(){ return this.taskId; } /** * 设置"任务编号"的值 * @param taskId 任务编号 */ public void setTaskId(String taskId){ this.taskId = taskId; } /** * 取得"对应程序" * @return 返回"对应程序" */ public String getTaskProgram(){ return this.taskProgram; } /** * 设置"对应程序"的值 * @param taskProgram 对应程序 */ public void setTaskProgram(String taskProgram){ this.taskProgram = taskProgram; } /** * 取得"依赖任务编号" * @return 返回"依赖任务编号" */ public String getTaskIdRely(){ return this.taskIdRely; } /** * 设置"依赖任务编号"的值 * @param taskIdRely 依赖任务编号 */ public void setTaskIdRely(String taskIdRely){ this.taskIdRely = taskIdRely; } /** * 取得"任务大类 0:业务任务 1:初始化任务" * @return 返回"任务大类 0:业务任务 1:初始化任务" */ public String getTaskType(){ return this.taskType; } /** * 设置"任务大类 0:业务任务 1:初始化任务"的值 * @param taskType 任务大类 0:业务任务 1:初始化任务 */ public void setTaskType(String taskType){ this.taskType = taskType; } /** * 取得"创建日期" * @return 返回"创建日期" */ public Date getTcCreatedate(){ return this.tcCreatedate; } /** * 设置"创建日期"的值 * @param tcCreatedate 创建日期 */ public void setTcCreatedate(Date tcCreatedate){ this.tcCreatedate = tcCreatedate; } /** * 取得"修改日期" * @return 返回"修改日期" */ public Date getTcChangedate(){ return this.tcChangedate; } /** * 设置"修改日期"的值 * @param tcChangedate 修改日期 */ public void setTcChangedate(Date tcChangedate){ this.tcChangedate = tcChangedate; } /** * 取得"创建人" * @return 返回"创建人" */ public String getTcCreateuser(){ return this.tcCreateuser; } /** * 设置"创建人"的值 * @param tcCreateuser 创建人 */ public void setTcCreateuser(String tcCreateuser){ this.tcCreateuser = tcCreateuser; } /** * 取得"修改人" * @return 返回"修改人" */ public String getTcChangeuser(){ return this.tcChangeuser; } /** * 设置"修改人"的值 * @param tcChangeuser 修改人 */ public void setTcChangeuser(String tcChangeuser){ this.tcChangeuser = tcChangeuser; } }
3. 创建定时任务详细表(即用来存储的所有的定时任务信息,与VTaskConfig实体类关联),这里用的是Oracle
create table V_TASK_CONFIG
(
trigger_type NUMBER(1),
trigger_month VARCHAR2(7),
trigger_day VARCHAR2(4),
trigger_week VARCHAR2(7),
if_start NUMBER(1),
task_id VARCHAR2(10) not null,
task_program VARCHAR2(100),
task_id_rely VARCHAR2(10),
task_type VARCHAR2(4),
tc_createdate DATE,
tc_changedate DATE,
tc_createuser VARCHAR2(12),
tc_changeuser VARCHAR2(12)
)

输入数据如下图

4. service层VTaskConfigService中对任务进行增删改
package dmp.platform.service;
import dmp.framework.db.IEntityDao;
import dmp.framework.service.BaseService;
import dmp.platform.dao.VTaskConfigDao;
import dmp.platform.model.VTaskConfig;
import dmp.platform.util.QuartzUtil;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.sql.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class VTaskConfigService extends BaseService<VTaskConfig> {
@Resource
private VTaskConfigDao vTaskConfigDao;
@Resource
private QuartzUtil quartzUtil;
@Override
protected IEntityDao<VTaskConfig, Long> getEntityDao() {
// TODO Auto-generated method stub
return vTaskConfigDao;
}
/**
* @Author: xzh
* @Date: Created in 16:28 2019/1/28 0028
* @Description: 添加任务
*/
public String addTaskConfig(VTaskConfig taskConfig) {
//校验录入的"对应程序",是否已存在
Map map = new HashMap();
int count = vTaskConfigDao.queryTaskProgram(taskConfig);
if (count != 0) {
return "系统已经存在重复的'对应程序'信息,请重新选择";
}
taskConfig.setTcCreatedate(new Date(System.currentTimeMillis()));
taskConfig.setTcChangedate(new Date(System.currentTimeMillis()));
//获取任务唯一主键Id,亦是任务的jobName,通过此唯一任务名对任务进行增删改
String taskId = vTaskConfigDao.getTaskId();
taskConfig.setTaskId(taskId);
int n = vTaskConfigDao.insert(taskConfig);
if (n == 1) {
if(taskConfig.getIfStart()==1){
quartzUtil.addJob(taskConfig);//添加定时任务
}
return "任务设置成功";
} else {
return "任务设置失败,请联系管理员!";
}
}
/**
* @Author: xzh
* @Date: Created in 10:47 2019/1/30 0030
* @Description: 修改任务
*/
public String updateTaskConfig(VTaskConfig taskConfig) {
//校验录入的"对应程序",是否已存在
Map map = new HashMap();
int count = vTaskConfigDao.queryTaskProgram(taskConfig);
if (count != 0) {
return "系统已经存在重复的'对应程序'信息,请重新选择";
}
taskConfig.setTcChangedate(new Date(System.currentTimeMillis()));
int n = vTaskConfigDao.update(taskConfig);
if (n == 1) {
if(taskConfig.getIfStart()==1){
quartzUtil.modifyJob(taskConfig);//修改定时任务
}
return "任务修改成功";
} else {
return "任务修改失败,请联系管理员!";
}
}
}
本文介绍了一种基于Quartz框架的动态数据库定时任务解决方案,通过在数据库中存储任务配置,实现定时任务的动态调整,无需修改代码即可更改任务执行时间和执行类。
351

被折叠的 条评论
为什么被折叠?



