SpringBoot 动态配置定时任务

  1. pom.xml依赖
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
</dependency>
  1. 主启动类上加上 @EnableScheduling

  2. 表结构

-- 定时器相关配置表
CREATE TABLE if not exists QRTZ_JOB_DETAILS(
    SCHED_NAME VARCHAR(120) NOT NULL,
    JOB_NAME VARCHAR(190) NOT NULL,
    JOB_GROUP VARCHAR(190) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    JOB_CLASS_NAME VARCHAR(250) NOT NULL,
    IS_DURABLE VARCHAR(1) NOT NULL,
    IS_NONCONCURRENT VARCHAR(1) NOT NULL,
    IS_UPDATE_DATA VARCHAR(1) NOT NULL,
    REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
    ENGINE=InnoDB;

CREATE TABLE if not exists QRTZ_TRIGGERS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    JOB_NAME VARCHAR(190) NOT NULL,
    JOB_GROUP VARCHAR(190) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    NEXT_FIRE_TIME BIGINT(13) NULL,
    PREV_FIRE_TIME BIGINT(13) NULL,
    PRIORITY INTEGER NULL,
    TRIGGER_STATE VARCHAR(16) NOT NULL,
    TRIGGER_TYPE VARCHAR(8) NOT NULL,
    START_TIME BIGINT(13) NOT NULL,
    END_TIME BIGINT(13) NULL,
    CALENDAR_NAME VARCHAR(190) NULL,
    MISFIRE_INSTR SMALLINT(2) NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
    REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
    ENGINE=InnoDB;

CREATE TABLE if not exists QRTZ_SIMPLE_TRIGGERS (
  	SCHED_NAME VARCHAR(120) NOT NULL,
  	TRIGGER_NAME VARCHAR(190) NOT NULL,
  	TRIGGER_GROUP VARCHAR(190) NOT NULL,
  	REPEAT_COUNT BIGINT(7) NOT NULL,
  	REPEAT_INTERVAL BIGINT(12) NOT NULL,
  	TIMES_TRIGGERED BIGINT(10) NOT NULL,
  	PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  	FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  	REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
    ENGINE=InnoDB;

CREATE TABLE if not exists QRTZ_CRON_TRIGGERS (
   SCHED_NAME VARCHAR(120) NOT NULL,
   TRIGGER_NAME VARCHAR(190) NOT NULL,
   TRIGGER_GROUP VARCHAR(190) NOT NULL,
   CRON_EXPRESSION VARCHAR(120) NOT NULL,
   TIME_ZONE_ID VARCHAR(80),
   PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
   FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
   REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
   ENGINE=InnoDB;

CREATE TABLE if not exists QRTZ_SIMPROP_TRIGGERS
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
    ENGINE=InnoDB;

CREATE TABLE if not exists QRTZ_BLOB_TRIGGERS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    BLOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
    ENGINE=InnoDB;

CREATE TABLE if not exists QRTZ_CALENDARS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    CALENDAR_NAME VARCHAR(190) NOT NULL,
    CALENDAR BLOB NOT NULL,
    PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
    ENGINE=InnoDB;

CREATE TABLE if not exists QRTZ_PAUSED_TRIGGER_GRPS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
    ENGINE=InnoDB;

CREATE TABLE if not exists QRTZ_FIRED_TRIGGERS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    ENTRY_ID VARCHAR(95) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    INSTANCE_NAME VARCHAR(190) NOT NULL,
    FIRED_TIME BIGINT(13) NOT NULL,
    SCHED_TIME BIGINT(13) NOT NULL,
    PRIORITY INTEGER NOT NULL,
    STATE VARCHAR(16) NOT NULL,
    JOB_NAME VARCHAR(190) NULL,
    JOB_GROUP VARCHAR(190) NULL,
    IS_NONCONCURRENT VARCHAR(1) NULL,
    REQUESTS_RECOVERY VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,ENTRY_ID))
    ENGINE=InnoDB;

CREATE TABLE if not exists QRTZ_SCHEDULER_STATE (
    SCHED_NAME VARCHAR(120) NOT NULL,
    INSTANCE_NAME VARCHAR(190) NOT NULL,
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,
    PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
    ENGINE=InnoDB;

CREATE TABLE if not exists QRTZ_LOCKS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    LOCK_NAME VARCHAR(40) NOT NULL,
    PRIMARY KEY (SCHED_NAME,LOCK_NAME))
    ENGINE=InnoDB;
  1. 定时任务配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.sql.DataSource;
import java.util.Properties;

/**
 * 定时任务配置
 */
@Configuration
public class ScheduleConfig {

	@Bean
	public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
		SchedulerFactoryBean factory = new SchedulerFactoryBean();
		factory.setDataSource(dataSource);

		//quartz参数
		Properties prop = new Properties();
		prop.put("org.quartz.scheduler.instanceName", "SchedulerName");
		prop.put("org.quartz.scheduler.instanceId", "AUTO");
		//线程池配置
		prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
		prop.put("org.quartz.threadPool.threadCount", "20");
		prop.put("org.quartz.threadPool.threadPriority", "5");
		//JobStore配置
		//定时任务抛出 org.quartz.SchedulerConfigException: DataSource name not set. 异常.
		//prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
		prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore");
		//集群配置
		prop.put("org.quartz.jobStore.isClustered", "true");
		prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
		prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");

		prop.put("org.quartz.jobStore.misfireThreshold", "12000");
		prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
		prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");

		//PostgreSQL数据库,需要打开此注释
		//prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");

		factory.setQuartzProperties(prop);

		factory.setSchedulerName("SchedulerName");
		//延时启动
		factory.setStartupDelay(30);
		factory.setApplicationContextSchedulerContextKey("applicationContextKey");
		//可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
		factory.setOverwriteExistingJobs(true);
		//设置自动启动,默认为true
		factory.setAutoStartup(true);

		return factory;
	}
}

  1. 定时任务工具类
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ScheduleJobUtils {

	private static final Logger logger = LoggerFactory.getLogger(ScheduleJobUtils.class);

	public static void deleteScheduleJob(Scheduler scheduler, String NAME, String JOB_NAME, String JOB_GROUP) {
		try {
			TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME,	JOB_GROUP);
			if( scheduler.checkExists(triggerKey) ){
				//如果存在测删除
				scheduler.pauseTrigger(triggerKey);
            	scheduler.unscheduleJob(triggerKey);
			}

			JobKey jobKey = new JobKey(JOB_NAME, JOB_GROUP);
			if(scheduler.checkExists(jobKey)){
				scheduler.pauseJob(jobKey);		//停止任务
				scheduler.deleteJob(jobKey);	//删除任务
			}
			logger.info("删除【"+NAME+"】定时任务成功!");
		}catch (Exception e) {
			logger.error("删除【"+NAME+"】定时任务失败!", e);
		}
	}

	/**
	 * 方法名:addOrUpdateScheduleJob
	 *
	 * @param NAME        任务中文名称
	 * @param JOB_NAME    任务名
	 * @param JOB_GROUP   任务组
	 * @param jobClass    任务执行类
	 */
	public static void addOrUpdateScheduleJob(Scheduler scheduler, String NAME, String JOB_NAME, String JOB_GROUP, String CronExpression, Class <? extends Job> jobClass){
		try{
			deleteScheduleJob(scheduler, NAME, JOB_NAME, JOB_GROUP);
			//CronExpression-表达式调度构建器
			/**
			Cron表达式范例:
				每隔5秒执行一次:0/5 * * * * ?
				每隔1分钟执行一次:0 0/1 * * * ?
				每天23点执行一次:0 0 23 * * ?
				每天凌晨1点执行一次:0 0 1 * * ?
				每月1号凌晨1点执行一次:0 0 1 1 * ?
				每月最后一天23点执行一次:0 0 23 L * ?
				每周星期天凌晨1点实行一次:0 0 1 ? * L
				在26分、29分、33分执行一次:0 26,29,33 * * * ?
				每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
			*/
			CronExpression cronExpressionObj = new CronExpression(CronExpression);
			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpressionObj).withMisfireHandlingInstructionDoNothing();

			//创建新触发条件
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
			String datestr = sdf.format(new Date()) + " 00:00:00";
			Date startDate = stringToDateTime(datestr);
			//Date startDate = new Date(System.currentTimeMillis()+5*1000);

			CronTrigger newcorntrigger = TriggerBuilder.newTrigger()
					.withIdentity(JOB_NAME, JOB_GROUP)
					.startAt(startDate)
					.withSchedule(scheduleBuilder)
					.build();

			//创建任务对象
			JobKey jobKey = new JobKey(JOB_NAME, JOB_GROUP);
			SimpleDateFormat sdfall = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			JobDetail executeJob= JobBuilder.newJob(jobClass)
									.withIdentity(jobKey)
									.withDescription(sdfall.format(new Date()))
									.storeDurably(true)//没有trigger关联时自动删除
									.requestRecovery()//失败后重做
									//.usingJobData("createtaskdaynum", createtaskdaynum)
									.build();

			//按新的trigger重新设置job执行
			scheduler.scheduleJob(executeJob, newcorntrigger);

			logger.info("启动或更新【"+NAME+"】定时任务成功!");
		}catch(Exception e){
			logger.error("启动或更新【"+NAME+"】定时任务失败!", e);
		}
	}

	/**
	 * 手动执行任务
	 * @param scheduler
	 * @param JOB_NAME
	 * @param JOB_GROUP
	 */
	public static void triggerScheduleJob(Scheduler scheduler, String JOB_NAME, String JOB_GROUP){
		try{
			JobKey jobKey = new JobKey(JOB_NAME, JOB_GROUP);
			scheduler.triggerJob(jobKey);
		}catch(Exception e){
			logger.error("手动执行【"+JOB_NAME+"】定时任务失败!", e);
		}
	}

	/**
	 * 方法名称:isMatch
	 * @param crontabExpress	 表达式(如:* * 7 * * ? *)
	 * @param date
	 * @return
	 * @throws java.text.ParseException
	 * 作用说明:此函数的作用是判断给定的时间 是否 满足 crontab 表达式【忽略毫秒】
	 */
	private static boolean isMatch(String crontabExpress, Date date) {
		try {
			if(crontabExpress==null){
				return false;
			}
			if(crontabExpress.equals("")){
				return false;
			}
			CronExpression crontabExpression = new CronExpression(crontabExpress);
			return crontabExpression.isSatisfiedBy(date);
		} catch (Exception e) {
			logger.error("错误的cron表达式"+crontabExpress, e);
			return false;
		}
	}


	/**
	 * 字符转换日期时间对象
	 */
	private static Timestamp stringToDateTime(String time) {
		try{
			SimpleDateFormat formatter = null;
			if ((time.indexOf("/") > -1) && (time.indexOf(" ") > -1)) {
				formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
			} else if ((time.indexOf("-") > -1) && (time.indexOf(" ") > -1)) {
				formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			}else if ((time.indexOf(".") > -1) && (time.indexOf(" ") > -1)) {
				formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
			} else if ((time.indexOf("/") > -1) && (time.indexOf(" ") == -1)) {
				formatter = new SimpleDateFormat("yyyy/MM/dd");
			} else if ((time.indexOf("-") > -1) && (time.indexOf(" ") == -1)) {
				formatter = new SimpleDateFormat("yyyy-MM-dd");
			}else if ((time.indexOf(".") > -1) && (time.indexOf(" ") == -1)) {
				formatter = new SimpleDateFormat("yyyy.MM.dd");
			}else{
				formatter = new SimpleDateFormat("yyyyMMddHHmmss");
			}
			Date ctime = formatter.parse(time);
			Timestamp reval =  new Timestamp(ctime.getTime());
			return reval;
		}catch(Exception e){
			return null;
		}
	}

}
  1. 日期工具类
import java.sql.Timestamp;

/**
 * 日期处理
 */
public class DateUtils {	

	/**
	 * timestamp 转 cron表达式
	 * @param timestamp
	 * @return
	 */
	public static String convertToCronExpression(Timestamp timestamp) {
		// 从Timestamp中提取小时、分钟和秒
		int hour = timestamp.toLocalDateTime().getHour();
		int minute = timestamp.toLocalDateTime().getMinute();
		int second = timestamp.toLocalDateTime().getSecond();

		// 构建每日的cron表达式
		String cronExpression = second + " " + minute + " " + hour + " * * ?";

		return cronExpression;
	}
}
  1. TestScheduled 类,新增定时任务
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.sql.Timestamp;

@Data
@Slf4j
@Component
public class TestScheduled {

	@Autowired
	private ITestService testService;

	@Resource
	private Scheduler scheduler;

	@PostConstruct
	public void init() {
		try{
			log.info("系统启动中, 注册定时任务");

			// 查数据库获取有效的 数据
			List<TestObj> list = new ArrayList<>();
			for (TestObj obj : list){
				// timestamp 转 cron表达式
				Timestamp cron = obj.getTime();
				String cronExpression = DateUtils.convertToCronExpression(cron);
				
				// 新增定时任务。obj.getId()参数必须唯一。
				ScheduleJobUtils.addOrUpdateScheduleJob(scheduler, "定时执行业务", "test-"+obj.getId(),"system", cronExpression, DetentionJob.class);
			}
		}catch (Exception e){
			log.error("系统启动中, 注册定时任务异常", e);
		}
	}



}
  1. Job 任务类, 定时任务到了触发时间执行具体业务逻辑
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.SpringUtil;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.sql.Timestamp;
import java.util.Date;
import java.util.List;

public class TestJob implements Job {

	protected Logger logger = LoggerFactory.getLogger(getClass());

	private ITestService testService;

	public TestJob(){
		testService = SpringUtil.getBean(ITestService.class);	
	}

	/**
	 * 该方法到了触发时间自动触发
	 */
	@Override
	public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
		try{
			// 查数据库获取有效的 数据
			List<TestEntity> list = new ArrayList<>();
			// 处理数据
			for (TestEntity obj : list){
				
				// 获取业务时间
				Timestamp cron = obj.getTime();
				// timestamp 转 cron表达式
				String cronExpression = ScheduleJobUtils.convertToCronExpression(cron);
				// jobExecutionContext.getFireTime() 为触发时间
				boolean match = isMatch(cronExpression, jobExecutionContext.getFireTime());
				// 满足业务条件
				if (match){
					// 执行业务操作
				}
					

			}
		}catch (Exception e){
			logger.error("错误: " , e);
		}

	}
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值