上一篇文章【修改定时时间,定时任务及时生效】 是定时任务与功能项目共用一个;
我目前所在公司的定时服务是专门有一个项目处理,然后定时查询库里面的定时信息配置。
话不多说,上程序
数据库设置
create table SCHEDULER_JOB
(id VARCHAR2(32) not null,job_name VARCHAR2(200),cron VARCHAR2(100),method_name varchar2(200),bean_name varchar2(200),remark varchar2(50),type varchar2(4) default '是否有参数 01否 02是'
)
实体类 SchedulerJob
package com.example.demo.entity;public class SchedulerJob {private String id;//任务名称private String jobName;//表达式private String cron;//方法名称private String methodName;//bean名称private String beanName;//备注(方法的参数)private String remark;//类型 是否有参数 01否 02是private String type;//todo get/set方法
}
SchedulerJobMapper
import com.example.demo.entity.SchedulerJob;
import org.apache.ibatis.annotations.Mapper;import java.util.List;@Mapper
public interface SchedulerJobMapper {List<SchedulerJob> queryList();
}
SchedulerJobMapper.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.SchedulerJobMapper"><resultMap type="com.example.demo.entity.SchedulerJob" id="resultMap"><result property="id" column="ID"/><result property="jobName" column="JOB_NAME"/><result property="cron" column="CRON"/><result property="methodName" column="METHOD_NAME"/><result property="beanName" column="BEAN_NAME"/><result property="remark" column="REMARK"/><result property="type" column="TYPE"/></resultMap><select id="queryList" resultMap="resultMap">select ID,JOB_NAME,CRON,METHOD_NAME,BEAN_NAME,REMARK,TYPE from scheduler_job</select></mapper>
JobRegisterService 用分布式锁定时查询定时任务
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;@Service
public class JobRegisterService {private static final Logger logger = LoggerFactory.getLogger(JobRegisterService.class);@Autowiredprivate RedisLockRegistry redisLockRegistry;@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Autowiredprivate JobService jobService;@Scheduled(cron = "0 0/2 * * * ?")public void startTaskJob() {Lock lock = redisLockRegistry.obtain("lock");try {boolean flag = lock.tryLock(10L, TimeUnit.SECONDS);ValueOperations<String, Object> map = redisTemplate.opsForValue();if (flag) {Object taskStatus = map.get("taskStatus");if (taskStatus != null && taskStatus.toString().equals("Y")) {jobService.clearAllJob();return;}map.set("taskStatus", "Y");redisTemplate.expire("taskStatus", 1L, TimeUnit.MINUTES);jobService.timeTask();} else {jobService.clearAllJob();}} catch (Exception e) {logger.error("获取锁异常:", e);} finally {lock.unlock();}}
}
JobService 查询数据库,并为每个job设置对应的表达式cron
import com.alibaba.fastjson.JSON;
import com.example.demo.entity.SchedulerJob;
import com.example.demo.mapper.SchedulerJobMapper;
import com.example.demo.util.DateUtils;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;@Service
@Transactional
public class JobService {private static final Logger logger = LoggerFactory.getLogger(JobService.class);@Autowiredprivate Scheduler scheduler;@Autowiredprivate SchedulerJobMapper schedulerJobMapper;public void timeTask() {//查询数据库存在需要定时的任务logger.info("任务重置开始,查询数据...");List<SchedulerJob> schedulerJobList = schedulerJobMapper.queryList();if (schedulerJobList != null) {try {scheduler.clear();logger.info("任务重置开始,旧的任务清理");schedulerJobList.forEach(this::addJob);} catch (Exception e) {logger.error("调用错误:", e);}}}public void addJob(SchedulerJob schedulerJob) {try {//作业名称JobKey jobKey = JobKey.jobKey(schedulerJob.getJobName());//设置触发时间CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(schedulerJob.getCron());//触发建立Trigger trigger = TriggerBuilder.newTrigger().withIdentity(schedulerJob.getJobName()).withSchedule(cronScheduleBuilder).forJob(jobKey).build();if (trigger != null && trigger.getStartTime() != null) {logger.info("作业【{}】启动时间为{}", schedulerJob.getJobName(), DateUtils.formatDate(trigger.getStartTime(), DateUtils.DATETIME_FORMAT));} else {logger.info("作业【{}】启动时间为空", schedulerJob.getJobName());}//建立作业JobDetail jobDetail = JobBuilder.newJob(QuartzFactory.class).withIdentity(schedulerJob.getJobName()).build();jobDetail.getJobDataMap().put("schedulerJob", schedulerJob);//调度作业scheduler.scheduleJob(jobDetail, trigger);if (!scheduler.isShutdown()) {scheduler.start();}} catch (Exception e) {logger.error("添加作业失败[schedulerJob={}]", JSON.toJSONString(schedulerJob), e);}}/*** 清除任务*/public void clearAllJob() throws SchedulerException {logger.info("清除任务...");scheduler.clear();}
}
QuartzFactory 定时任务工厂 根据method_name找到对应的映射信息,并执行该方法
import com.example.demo.constant.SchedulerJobTypeEnum;
import com.example.demo.entity.SchedulerJob;
import com.example.demo.util.SpringContextUtil;
import org.apache.commons.lang.StringUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Component
public class QuartzFactory implements Job {private static final Logger logger = LoggerFactory.getLogger(QuartzFactory.class);@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {//获取调度数据SchedulerJob schedulerJob = (SchedulerJob) jobExecutionContext.getMergedJobDataMap().get("schedulerJob");//获取对应的beanObject obj = SpringContextUtil.getObj(schedulerJob.getBeanName());try {if (obj == null) {throw new Exception("找不到该类");}if (StringUtils.isEmpty(schedulerJob.getType()) || SchedulerJobTypeEnum.NO.getCode().equals(schedulerJob.getType())) {Method method = obj.getClass().getMethod(schedulerJob.getMethodName());method.invoke(obj);} else {Method method = obj.getClass().getMethod(schedulerJob.getMethodName(), String.class);method.invoke(obj, schedulerJob.getRemark());}} catch (Exception e) {logger.error("定时任务获取映射异常:", e);}}
}
SpringContextUtil 上下文工具
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;@Component
public class SpringContextUtil implements ApplicationContextAware {//spring 上下文实例对象private static ApplicationContext context;// 根据class获取对象实例public static <T> T getObj(Class<T> tClass) {return context.getBean(tClass);}// 根据配置的 bean name 获取对象实例public static Object getObj(String beanName) {return context.getBean(beanName);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {context = applicationContext;}
}
RedisLockConfig Redis分布式锁配置bean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.integration.redis.util.RedisLockRegistry;@Configuration
public class RedisLockConfig {@Beanpublic RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {//第一个参数redisConnectionFactory//第二个参数registryKey,分布式锁前缀,设置为项目名称会好些//该构造方法对应的分布式锁,默认有效期是60秒.可以自定义return new RedisLockRegistry(redisConnectionFactory, "demo");}
}
SchedulerConfig 定时任务配置bean
import org.quartz.Scheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;import java.io.IOException;@Configuration
public class SchedulerConfig {@Beanpublic Scheduler scheduler() throws Exception {Scheduler scheduler = schedulerFactoryBean().getScheduler();return scheduler;}@Beanpublic SchedulerFactoryBean schedulerFactoryBean() throws IOException {SchedulerFactoryBean factory = new SchedulerFactoryBean();factory.setSchedulerName("Test_Scheduler");//覆盖已存在的任务factory.setOverwriteExistingJobs(true);// 延迟10s执行 防止发生系统未启动完成 定时任务却已经开始的情况factory.setStartupDelay(10);return factory;}
}
SchedulerJobTypeEnum 定时任务类型枚举
public enum SchedulerJobTypeEnum {NO("01", "否"),YES("02", "是");private String code;private String value;SchedulerJobTypeEnum(String code, String value) {this.code = code;this.value = value;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}
}
DateUtils 时间处理工具类
import org.apache.commons.lang.time.DateFormatUtils;import java.util.Date;public class DateUtils {public static final String DATE_FORMAT = "yyyy-MM-dd";public static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";public static final String TIME_FORMAT = "HH:mm:ss";/*** 常用的时间格式.*/private static String[] parsePatterns = {"yyyy-MM-dd","yyyy-MM-dd HH:mm:ss","yyyy-MM-dd HH:mm","HH:mm:ss"};/*** 得到当前日期字符串.*/public static String getDate() {return getDate(DateUtils.DATE_FORMAT);}public static String getDate(String pattern) {return DateFormatUtils.format(new Date(), pattern);}/*** 得到当前时间字符串.*/public static String getTime() {return formatDate(new Date(), DateUtils.TIME_FORMAT);}/*** 得到当前日期和时间字符串.*/public static String getDateTime() {return formatDate(new Date(), DateUtils.DATETIME_FORMAT);}/*** 获取日期时间字符串,默认格式为(yyyy-MM-dd).*/public static String formatDate(Date date, Object... pattern) {String formatDate = null;if (pattern != null && pattern.length > 0) {formatDate = DateFormatUtils.format(date, pattern[0].toString());} else {formatDate = DateFormatUtils.format(date, DateUtils.DATE_FORMAT);}return formatDate;}
}
TestSchedulerJobService 定义的定时任务方法
import com.example.demo.util.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;@Service
public class TestSchedulerJobService {private static final Logger logger = LoggerFactory.getLogger(TestSchedulerJobService.class);public void test() {String dateTime = DateUtils.getDateTime();logger.info("当前时间={}", dateTime);}public void test1(String param) {String dateTime = DateUtils.getDateTime();logger.info("当前时间1={},参数={}", dateTime, param);}
}
数据库补充数据
【注意:】数据库中bean_Name存储的是bean名称(默认类名首字母小写)
job_name和method_name数据值一样即可
测试结果
由此可看出,两个定时任务是按照数据库中配置的定时时间跑的。
具体定时任务时间等信息视情况而定!