大多数定时任务还是写在.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 "任务修改失败,请联系管理员!"; } } }