#ID设置为自动获取 每一个必须不同 (所有调度器实例中是唯一的)
org.quartz.scheduler.instanceId=AUTO
#指定调度程序的主线程是否应该是守护线程
org.quartz.scheduler.makeSchedulerThreadDaemon=true
#ThreadPool实现的类名
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
#ThreadPool配置线程守护进程
org.quartz.threadPool.makeThreadsDaemons=true
#线程数量
org.quartz.threadPool.threadCount=20
#线程优先级
org.quartz.threadPool.threadPriority=5
#数据保存方式为持久化
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
#StdJDBCDelegate说明支持集群
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#quartz内部表的前缀
org.quartz.jobStore.tablePrefix=QRTZ_
#是否加入集群
org.quartz.jobStore.isClustered=true
#容许的最大作业延长时间
org.quartz.jobStore.misfireThreshold=55000
说明:在使用quartz做持久化的时候需要用到quartz的11张表,可以去quartz官网下载对应版本的quartz,解压打开docs/dbTables里面有对应数据库的建表语句。关于quartz.properties配置的详细解释可以查看quartz官网。另外新建一张表用于存放定时任务基本信息和描述等信息,定时任务的增、删、改、执行等功能与此表没有任何关系。
package com.demo.demo.config;
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;
/*
* @Author jt
* @Description //Quartz的核心配置类
* @Date 2020/8/11
**/
@Configuration
public class ConfigureQuartz {
//配置JobFactory
@Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
/**
* SchedulerFactoryBean这个类的真正作用提供了对org.quartz.Scheduler的创建与配置,并且会管理它的生命周期与Spring同步。
* org.quartz.Scheduler: 调度器。所有的调度都是由它控制。
*
* @param dataSource 为SchedulerFactory配置数据源
* @param jobFactory 为SchedulerFactory配置JobFactory
*/
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource, JobFactory jobFactory) throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
//可选,QuartzScheduler启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录
factory.setOverwriteExistingJobs(true);
factory.setAutoStartup(true); //设置自行启动
factory.setDataSource(dataSource);
factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties());
return factory;
}
//从quartz.properties文件中读取Quartz配置属性
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
//配置JobFactory,为quartz作业添加自动连接支持
public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
}
package com.demo.demo.ctrl;
import com.demo.demo.entity.JobEntity;
import com.demo.demo.service.DynamicJobService;
import org.quartz.*;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
/*
* @Author jt
* @Description //任务调度
* @Date 2020/8/11
**/
@RestController
@RequestMapping("job")
public class JobController {
@Resource
private SchedulerFactoryBean factory;
@Resource
private DynamicJobService jobService;
//被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次
//初始化启动所有的Job
@PostConstruct
public void initialize() {
jobService.reStartAllJobs();
}
//根据ID重启某个Job
@GetMapping("/refresh/{id}")
public String refresh(@PathVariable Integer id) {
String result;
JobEntity entity = jobService.getJobEntityById(id);
if (entity == null) return "error: id is not exist ";
TriggerKey triggerKey = new TriggerKey(entity.getName(), entity.getGroup());
JobKey jobKey = jobService.getJobKey(entity);
Scheduler scheduler = factory.getScheduler();
try {
scheduler.unscheduleJob(triggerKey);
scheduler.deleteJob(jobKey);
JobDataMap map = jobService.getJobDataMap(entity);
JobDetail jobDetail = jobService.geJobDetail(jobKey, entity.getDescription(), map);
if (entity.getStatus().equals("OPEN")) {
scheduler.scheduleJob(jobDetail, jobService.getTrigger(entity));
result = "Refresh Job : " + entity.getName() + "\t method: " + entity.getClassName() + "." + entity.getMethodName() + " success !";
jobService.updateJobEntityWorkStatusByKey(entity);
} else {
result = "Refresh Job : " + entity.getName() + "\t method: " + entity.getClassName() + "." + entity.getMethodName() + " failed ! , " + "Because the Job status is " + entity.getStatus();
}
} catch (SchedulerException e) {
e.printStackTrace();
result = "Error while Refresh " + e.getMessage();
}
return result;
}
//重启数据库中所有的Job
@GetMapping("/refresh/all")
public String refreshAll() {
String result;
try {
jobService.reStartAllJobs();
result = "SUCCESS";
} catch (Exception e) {
result = "EXCEPTION : " + e.getMessage();
}
return "refresh all jobs : " + result;
}
}
package com.demo.demo.service.Impl;
import com.demo.demo.entity.JobEntity;
import com.demo.demo.job.DynamicJob;
import com.demo.demo.mapper.JobEntityMapper;
import com.demo.demo.service.DynamicJobService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
@Service
@Slf4j
public class DynamicJobServiceImpl implements DynamicJobService {
@Resource
private SchedulerFactoryBean factory;
@Resource
private JobEntityMapper jobEntityMapper;
/**
* 重新启动所有的job
*/
public void reStartAllJobs() {
try {
Scheduler scheduler = factory.getScheduler();
Set set = scheduler.getJobKeys(GroupMatcher.anyGroup());
for (JobKey jobKey : set) {
scheduler.deleteJob(jobKey);
}
for (JobEntity job : this.loadJobs()) {
log.info("Job register name : {} , group : {} , cron : {}", job.getName(), job.getGroup(), job.getCron());
JobDataMap map = this.getJobDataMap(job);
JobKey jobKey = this.getJobKey(job);
JobDetail jobDetail = this.geJobDetail(jobKey, job.getDescription(), map);
if ("OPEN".equals(job.getStatus())) {
scheduler.scheduleJob(jobDetail, this.getTrigger(job));
} else {
log.info("Job jump name : {} , Because {} status is {}", job.getName(), job.getName(), job.getStatus());
}
updateJobEntityWorkStatusByKey(job);
}
log.info("INIT SUCCESS");
} catch (SchedulerException se) {
log.info("INIT EXCEPTION {}", se.getMessage());
}
}
//通过Id获取Job
public JobEntity getJobEntityById(Integer id) {
return jobEntityMapper.selectByPrimaryKey(id);
}
//更新Job的工作状态
public void updateJobEntityWorkStatusByKey(JobEntity entity) {
entity.setWorkStatus(1);
jobEntityMapper.updateJobEntityWorkStatusByKey(entity.getId(), entity.getWorkStatus());
}
//从数据库中加载获取到所有Job
private List loadJobs() {
return jobEntityMapper.selectAll();
}
//获取JobDataMap.(Job参数对象)
public JobDataMap getJobDataMap(JobEntity job) {
JobDataMap map = new JobDataMap();
map.put("name", job.getName());
map.put("group", job.getGroup());
map.put("cronExpression", job.getCron());
map.put("parameter", job.getParameter());
map.put("JobDescription", job.getDescription());
map.put("className", job.getClassName());
map.put("methodName", job.getMethodName());
map.put("params", job.getParams());
map.put("status", job.getStatus());
return map;
}
//获取JobDetail,JobDetail是任务的定义,而Job是任务的执行逻辑,JobDetail里会引用一个Job Class来定义
public JobDetail geJobDetail(JobKey jobKey, String description, JobDataMap map) {
return JobBuilder.newJob(DynamicJob.class)
.withIdentity(jobKey)
.withDescription(description)
.setJobData(map)
.storeDurably()
.build();
}
//获取Trigger (Job的触发器,执行规则)
public Trigger getTrigger(JobEntity job) {
return TriggerBuilder.newTrigger()
.withIdentity(job.getName(), job.getGroup())
.withSchedule(CronScheduleBuilder.cronSchedule(job.getCron()))
.build();
}
//获取JobKey,包含Name和Group
public JobKey getJobKey(JobEntity job) {
return JobKey.jobKey(job.getName(), job.getGroup());
}
}
package com.demo.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.demo.demo.entity.JobEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Component;
import java.util.List;
@Mapper
@Component
public interface JobEntityMapper extends BaseMapper {
@Select("select * from job_entity where id=#{id}")
JobEntity selectByPrimaryKey(@Param("id") Integer id);
@Update("update job_entity set work_status=#{workStatus} where id=#{id}")
void updateJobEntityWorkStatusByKey(@Param("id") Integer id, @Param("workStatus") Integer workStatus);
@Select("select * from job_entity")
List selectAll();
}