Spring动态定时器

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

 

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值