背景
最近做项目,需要定时的去执行某一项任务,针对间隔循环,每天,每周,每月等,于是翻阅了一些资料和一些项目,最后成功实现了动态的创建一个定时任务。这里选择的是spring集成Quartz
核心依赖
<dependency>
<groupId>org.springframerwork.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
核心 QuartzConfig 配置类
/**
*
*/
@Component("quartzJobFactory")
public class QuartzJobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
/**
* 注入scheduler到spring
* @param quartzJobFactory
* @return
* @throws Exception
*/
@Bean(name = "scheduler")
public Scheduler scheduler(QuartzJobFactory quartzJobFactory) throws Exception {
SchedulerFactoryBean factoryBean=new SchedulerFactoryBean();
factoryBean.setJobFactory(quartzJobFactory);
factoryBean.afterPropertiesSet();
Scheduler scheduler=factoryBean.getScheduler();
scheduler.start();
return scheduler;
}
在这里注入了核心的 quartzJobFactory 和 scheduler,接下来就是创建定时任务的工具类
ExecutionJob 日志记录
public class ExecutionJob extends QuartzJobBean {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private ExecutorService executorService = Executors.newSingleThreadExecutor();
@Override
protected void executeInternal(JobExecutionContext context) {
QuartzJob quartzJob = (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY);
// 获取spring bean
QuartzJobService quartzJobService = SpringContextHolder.getBean("quartzJobService");
QuartzLog log = new QuartzLog();
log.setJobName(quartzJob.getJobName());
log.setBeanName(quartzJob.getBeanName());
log.setMethodName(quartzJob.getMethodName());
log.setParams(quartzJob.getParams());
long startTime = System.currentTimeMillis();
log.setCronExpression(quartzJob.getCronExpression());
log.setType(quartzJob.getType());
try {
// 执行任务
logger.info("任务准备执行,任务名称:{}", quartzJob.getJobName());
QuartzRunnable task = new QuartzRunnable(quartzJob.getBeanName(), quartzJob.getMethodName(),
quartzJob.getParams());
Future<?> future = executorService.submit(task);
future.get();
long times = System.currentTimeMillis() - startTime;
log.setTime(times);
// 任务状态
log.setIsSuccess(true);
logger.info("任务执行完毕,任务名称:{} 总共耗时:{} 毫秒", quartzJob.getJobName(), times);
} catch (Exception e) {
logger.error("任务执行失败,任务名称:{}" + quartzJob.getJobName(), e);
long times = System.currentTimeMillis() - startTime;
log.setTime(times);
// 任务状态 0:成功 1:失败
log.setIsSuccess(false);
log.setExceptionDetail(ThrowableUtil.getStackTrace(e));
quartzJob.setIsPause(false);
//更新状态
quartzJobService.updateIsPause(quartzJob);
} finally {
//持久化日志到数据库
}
}
}
可以通过此类来记录定时任务执行的一些日志
QuartzManage类
private static final String JOB_NAME = "TASK_";
@Resource(name = "scheduler")
private Scheduler scheduler;
public void addJob(QuartzJob quartzJob){
try {
// 构建job信息
JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class).
withIdentity(JOB_NAME + quartzJob.getId()).build();
//通过触发器名和cron 表达式创建 Trigger
Trigger cronTrigger = newTrigger()
.withIdentity(JOB_NAME + quartzJob.getId())
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression()).withMisfireHandlingInstructionDoNothing())
.build();
cronTrigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob);
//重置启动时间
((CronTriggerImpl)cronTrigger).setStartTime(new Date());
//执行定时任务
scheduler.scheduleJob(jobDetail,cronTrigger);
// 暂停任务
if (quartzJob.getIsPause()) {
pauseJob(quartzJob);
}
} catch (Exception e){
log.error("创建定时任务失败", e);
throw new BadRequestException("创建定时任务失败");
}
}
/**
* 更新job cron表达式
* @param quartzJob
* @throws SchedulerException
*/
public void updateJobCron(QuartzJob quartzJob){
try {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 如果不存在则创建一个定时任务
if(trigger == null){
addJob(quartzJob);
trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
}
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression());
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
//重置启动时间
((CronTriggerImpl)trigger).setStartTime(new Date());
trigger.getJobDataMap().put(QuartzJob.JOB_KEY,quartzJob);
scheduler.rescheduleJob(triggerKey, trigger);
// 暂停任务
if (quartzJob.getIsPause()) {
pauseJob(quartzJob);
}
} catch (Exception e){
log.error("更新定时任务失败", e);
throw new BadRequestException("更新定时任务失败");
}
}
/**
* 删除一个job
* @param quartzJob
* @throws SchedulerException
*/
public void deleteJob(QuartzJob quartzJob){
try {
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.pauseJob(jobKey);
scheduler.deleteJob(jobKey);
} catch (Exception e){
log.error("删除定时任务失败", e);
throw new BadRequestException("删除定时任务失败");
}
}
/**
* 恢复一个job
* @param quartzJob
* @throws SchedulerException
*/
public void resumeJob(QuartzJob quartzJob){
try {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 如果不存在则创建一个定时任务
if(trigger == null)
addJob(quartzJob);
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.resumeJob(jobKey);
} catch (Exception e){
log.error("恢复定时任务失败", e);
throw new BadRequestException("恢复定时任务失败");
}
}
/**
* 立即执行job
* @param quartzJob
* @throws SchedulerException
*/
public void runAJobNow(QuartzJob quartzJob){
try {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 如果不存在则创建一个定时任务
if(trigger == null)
addJob(quartzJob);
JobDataMap dataMap = new JobDataMap();
dataMap.put(QuartzJob.JOB_KEY, quartzJob);
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.triggerJob(jobKey,dataMap);
} catch (Exception e){
log.error("定时任务执行失败", e);
throw new BadRequestException("定时任务执行失败");
}
}
/**
* 暂停一个job
* @param quartzJob
* @throws SchedulerException
*/
public void pauseJob(QuartzJob quartzJob){
try {
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.pauseJob(jobKey);
} catch (Exception e){
log.error("定时任务暂停失败", e);
throw new BadRequestException("定时任务暂停失败");
}
}
QuartzRunnable类
public class QuartzRunnable implements Callable {
private Object target;
private Method method;
private String params;
QuartzRunnable(String beanName, String methodName, String params)
throws NoSuchMethodException, SecurityException {
this.target = SpringContextHolder.getBean(beanName);
this.params = params;
if (StringUtils.isNotBlank(params)) {
this.method = target.getClass().getDeclaredMethod(methodName, String.class);
} else {
this.method = target.getClass().getDeclaredMethod(methodName);
}
}
@Override
public Object call() throws Exception {
ReflectionUtils.makeAccessible(method);
if (StringUtils.isNotBlank(params)) {
method.invoke(target, params);
} else {
method.invoke(target);
}
return null;
}
}
QuartzJob实体类
public static final String JOB_KEY = "JOB_KEY";
@NotNull(groups = {Update.class})
protected Long id;
/**
* 定时器名称
*/
private String jobName;
/**
* Bean名称
*/
@NotBlank
private String beanName;
/**
* 方法名称
*/
@NotBlank
private String methodName;
/**
* 参数
*/
private String params;
/**
* cron表达式
*/
@NotBlank
private String cronExpression;
/**
* 状态
*/
private Boolean isPause = false;
/**
* 备注
*/
private String remark;
/**
* 创建日期
*/
private Timestamp updateTime;
//类型
private Integer type;
通过实例化一个 quartzJob 实体来通过 QuartzManage 工具类创建开启一个定时任务,可以将这个实体持久化到数据库做系统初始化任务,也可以满足动态的创建定时任务
最后就是系统初始化的时候创建一些定时任务了:
@Component
@Slf4j
public class JobRunner implements ApplicationRunner {
@Autowired
private QuartzManage quartzManage;
@Override
public void run(ApplicationArguments args) throws Exception {
QuartzTask quartzTask =new QuartzTask();
quartzTask.run();
}
class QuartzTask implements Runnable {
@Override
public void run() {
try {
log.info("--------------------注入定时任务---------------------");
QuartzJob quartzJob = new QuartzJob();
quartzJob.setBeanName("testTask");
quartzJob.setCronExpression("0/5 * * * * ?");
quartzJob.setIsPause(false);
quartzJob.setJobName("aaa");
quartzJob.setMethodName("excute");
quartzJob.setParams("2");
quartzJob.setType(2);
quartzJob.setUpdateTime(new Timestamp(System.currentTimeMillis()));
quartzManage.addJob(quartzJob);
} catch (Exception e) {
log.error("QuartzTask error:{}", e);
}
}
}
}
@Component("testTask")
public class TestTask {
public void excute(String id){
System.out.println("任务执行,执行参数 = " + id);
}
}
可以通过beanName指定一个定时任务类,通过methodName来调用方法,最后要注意的cron表达式不要写错了!
至此,可以成功的满足动态的创建一个定时任务
总结:具体的数据库的增删改查这里就不罗列了,相信crud大家都会!