项目中需要定时执行某个任务,功能包括
启动,重新启动,暂停,恢复,更新,添加,删除,查询
这些功能是通用的哈,可以拿来用在任何项目中
1. pom.xml
<!-- quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
2. 实体类
这里用到了lombok,方便管理实体类(下几篇文章中会讲解lombok,swagger2,通用Mapperde等使用方式。都是一些方便开发的小技术)
@EqualsAndHashCode(callSuper = false)
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "table_schedule_job")
public class ScheduleJobVo {
@Id
@Column(name = "Id")
private String id = UUID.randomUUID().toString();
/** 任务名称 */
private String jobName;
/** 任务类 */
private String jobClass;
/** 任务方法 */
private String jobMethod;
/** 任务状态 0禁用 1启用 2删除 */
private String status;
/** 任务运行时间表达式 */
private String cronExpression;
/** 任务描述 */
private String description;
/** 计划开始时间 */
private Timestamp planStartDate;
/** 计划结束时间 */
private Timestamp planEndDate;
@Transient
private Integer page = 1;
@Transient
private Integer rows = 10;
public ScheduleJobVo(String id, String status) {
super();
this.id = id;
this.status = status;
}
}
3. 工具类 SpringUtil.java
用于获得Spring管理的Bean
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
System.out.println("---------------------------------------------------------------------");
System.out.println("========ApplicationContext配置成功,在普通类可以通过调用SpringUtils.getAppContext()获取applicationContext对象,applicationContext="+SpringUtil.applicationContext+"========");
System.out.println("---------------------------------------------------------------------");
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}
4. Service接口类 SchedulerService.java
public interface SchedulerService {
/**
* @Description: 创建任务调度
* @param parms
* @param name
* @param group
* @param cronExpression
* @param c
* @throws Exception
*/
public void createScheduler(ScheduleJobVo job) throws SchedulerException;
/**
* @Description: 删除任务
* @param name
* 工作名称
* @param group
* 工作组
* @throws SchedulerException
* @throws ParseException
*/
public boolean deleteJob(String name) throws SchedulerException;
/**
* @Description: 创建任务调度
* @param parms
* 工作参数
* @param name
* 工作名称
* @param c
* 类名称
* @param minutes
* 间隔执行时长(分钟),传参时,需把时间转换成分钟
* @throws Exception
*/
public void createSchedulerJob(Map<String, Object> parms, String name,
Class<? extends Job> c, String cronExpression) throws Exception;
/**
* @Description: 暂停任务
* @param name
* 任务名称
* @throws SchedulerException
*/
public void pauseJob(String name) throws SchedulerException;
/**
* @Description:恢复任务
* @param name
* 任务名称
* @throws SchedulerException
*/
public void resumeJob(String name) throws SchedulerException;
public void restartJob(ScheduleJobVo job) throws SchedulerException ;
}
5. Service实现类
一些调度器操作
@Service
@Slf4j
public class SchedulerServiceImpl implements SchedulerService {
private static Scheduler scheduler;
static {
SchedulerFactory sf = new StdSchedulerFactory();
try {
scheduler = sf.getScheduler();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* @Description: 创建任务调度
*/
public void createScheduler(ScheduleJobDto job) throws SchedulerException {
TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (trigger == null) {
JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class)
.withIdentity(job.getJobName()).build();
jobDetail.getJobDataMap().put("scheduleJob", job);
// 表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
.cronSchedule(job.getCronExpression());
scheduleBuilder.withMisfireHandlingInstructionDoNothing();
// 按新的cronExpression表达式构建一个新的trigger
try {
TriggerBuilder<CronTrigger> triggerBuilder = TriggerBuilder
.newTrigger().withIdentity(triggerKey)
.withSchedule(scheduleBuilder);
if (job.getPlanStartDate() != null) {
triggerBuilder.startAt(DateUtil.timestampToDate(job.getPlanStartDate()));
} else {
triggerBuilder.startNow();
}
if (job.getPlanEndDate() != null) {
triggerBuilder.endAt(DateUtil.timestampToDate(job.getPlanEndDate()));
}
trigger = triggerBuilder.build();
} catch (Exception e1) {
e1.printStackTrace();
}
try {
scheduler.scheduleJob(jobDetail, trigger);
if (!scheduler.isShutdown()) {
scheduler.start();
}
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
/**
* @Description: 删除任务
* @param name
* JOB名称
*/
public boolean deleteJob(String name) throws SchedulerException {
TriggerKey triggerKey = new TriggerKey(name);
Trigger trigger = scheduler.getTrigger(triggerKey);
if (trigger != null) {
JobKey jobKey = JobKey.jobKey(name);
scheduler.pauseTrigger(triggerKey);// 停止触发器
scheduler.unscheduleJob(triggerKey);// 移除触发器
return scheduler.deleteJob(jobKey);// 移除当前进程的Job
}
return false;
}
/**
* @Description: 创建任务调度
* @param parms
* 工作参数
* @param name
* 工作名称
* @param c
* 类名称
* @param minutes
* 间隔执行时长(分钟),传参时,需把时间转换成分钟
* @throws Exception
*/
public void createSchedulerJob(Map<String, Object> parms, String name,
Class<? extends Job> c, String cronExpression) throws Exception {
JobDetail job = JobBuilder.newJob(c).withIdentity(name).build();
if (parms != null && !parms.isEmpty()) {
JobDataMap dataMap = job.getJobDataMap();
dataMap.putAll(parms);
}
CronScheduleBuilder builder = CronScheduleBuilder
.cronSchedule(cronExpression);
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(name)
.withSchedule(builder).build();
scheduler.scheduleJob(job, trigger);
scheduler.start();
scheduler.isStarted();
}
/**
* @Description: 暂停任务
* @param name
* 任务名称
* @throws SchedulerException
*/
public void pauseJob(String name) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(name);
scheduler.pauseJob(jobKey);
//需要补偿的话就注释掉触发器(后两句)
//如果注释掉,当暂停一段时间后,再启动就会把暂停期间的任务全部执行一次
TriggerKey triggerKey = TriggerKey.triggerKey(name);
scheduler.pauseTrigger(triggerKey);
}
/**
* @Description:恢复任务
* @param name
* 任务名称
* @throws SchedulerException
*/
public void resumeJob(String name) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(name);
scheduler.resumeJob(jobKey);
//需要补偿的话就注释掉触发器(后两句)
//如果注释掉,当暂停一段时间后,再启动就会把暂停期间的任务全部执行一次
TriggerKey triggerKey = TriggerKey.triggerKey(name);
scheduler.resumeTrigger(triggerKey);
}
@Override
public void restartJob(ScheduleJobVo job) throws SchedulerException {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName());
//获取trigger,即在spring配置文件中定义的 bean id="myTrigger"
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
//修改按照新的执行策略执行,不允许修改立即执行一次
scheduleBuilder.withMisfireHandlingInstructionDoNothing();
//按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
.withSchedule(scheduleBuilder).build();
//按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
6. Job工厂
用来得到某个JOB
public class QuartzJobFactory implements Job {
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
try {
ScheduleJobVo scheduleJob = (ScheduleJobVo) context
.getMergedJobDataMap().get("scheduleJob");
Object task = SpringUtil.getBean(scheduleJob.getJobClass());
Class<?> demo = task.getClass();//得到类
Method method = demo.getMethod(scheduleJob.getJobMethod());//得到方法
method.invoke(task);//执行方法
} catch (Exception e) {
e.printStackTrace();
}
}
}
7. Controler 控制类
以上有调度的Service方法,业务的Service自己写吧,就不贴出来了(对于ScheduleJobVo增删改查而已)
@Slf4j
@RestController
@RequestMapping("/quartz")
@Api(value = "/", description = "调度相关接口")
public class QuartzController {
@Autowired
private SchedulerService schedulerService;
@Autowired
private IScheduleJobService scheduleJobService;//ScheduleJobVo的Service实现类(你们自己去实现)
//存储Job的id用
private List<String> ids = new ArrayList<String>();
@ResponseBody
@PostConstruct
// 被@PostConstruct修饰的方法会在服务器加载Servle的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。
public void initJob() {
List<ScheduleJobVo> list = scheduleJobService.findEnableScheduleJob();//得到所有任务
for (ScheduleJobVo scheduleJob : list) {
if ((scheduleJob.getStatus().trim()).equals("1")) {//1:表示任务处于启动状态
try {
log.info("调度启动开始=============================");
schedulerService.createScheduler(scheduleJob);
log.info("调度启动结束=============================");
} catch (Exception e) {
e.printStackTrace();
}
} else {
ids.add(scheduleJob.getId());//删除的时候用
}
}
}
@RequestMapping(value = "/startJob", method = RequestMethod.GET)
@ResponseBody
@ApiOperation(notes = "/startJob", httpMethod = "GET", value = "启动trigger")
public Response startJob(@RequestParam String id) {
ScheduleJobVo job = scheduleJobService.findScheduleJob(id);
if(job == null){
return Response.failure("任务不存在");
}
try {
schedulerService.createScheduler(job);
} catch (SchedulerException e) {
e.printStackTrace();
}
return new Response(Response.STATUS_OK,"启动成功");
//Response是我自己实现的类,你们改成自己的返回值就好了。
}
@RequestMapping(value = "/restartJob", method = RequestMethod.GET)
@ResponseBody
//@ApiOperation 是swagger中的注解,不需要的可以删掉(后续会有专门讲解)
@ApiOperation(notes = "/restartJob", httpMethod = "GET", value = "重新启动trigger")
public Response restartJob(@RequestParam String id) {
ScheduleJobVo job = scheduleJobService.findScheduleJob(id);
if(job == null){
return Response.failure("任务不存在");
}
try {
schedulerService.restartJob(job);
} catch (SchedulerException e) {
e.printStackTrace();
}
return new Response(Response.STATUS_OK,"重新启动成功");
}
@RequestMapping(value = "/pauseJob/{id}", method = RequestMethod.PATCH)
@ResponseBody
@ApiOperation(notes = "/pauseJob/{id}", httpMethod = "PATCH", value = "暂停JOB")
public Response pauseJob(@PathVariable String id) {
ScheduleJobVo job = scheduleJobService.findScheduleJob(id);
if(job == null){
return Response.failure("任务不存在");
}
try {
schedulerService.pauseJob(job.getJobName());
} catch (SchedulerException e) {
e.printStackTrace();
}
//更新数据库中任务状态
Response response = scheduleJobService
.updateScheduleJob(new ScheduleJobVo(id, "0"));
return response;
}
@RequestMapping(value = "/resumeJob/{id}", method = RequestMethod.PATCH)
@ResponseBody
@ApiOperation(notes = "/resumeJob/{id}", httpMethod = "PATCH", value = "恢复JOB")
public Response resumeJob(@PathVariable String id) {
ScheduleJobVo job = scheduleJobService.findScheduleJob(id);
if(job == null){
return Response.failure("任务不存在");
}
try {
schedulerService.resumeJob(job.getJobName());
} catch (SchedulerException e) {
e.printStackTrace();
}
//更新数据库中任务状态
Response response = scheduleJobService
.updateScheduleJob(new ScheduleJobVo(id, "1"));
return response;
}
@RequestMapping(value = "/updateJob", method = RequestMethod.PUT)
@ResponseBody
@ApiOperation(notes = "/updateJob", httpMethod = "PUT", value = "更新JOB")
public Response updateJob(@RequestBody ScheduleJobVo job) {
Response response = scheduleJobService.updateScheduleJob(job);
return response;
}
@RequestMapping(value = "/insertJob", method = RequestMethod.POST)
@ResponseBody
@ApiOperation(notes = "/insertJob", httpMethod = "POST", value = "添加JOB")
public Response insertJob(@RequestBody ScheduleJobVo job) {
Response response = scheduleJobService.insertScheduleJob(job);
return response;
}
@RequestMapping(value = "/deleteJob/{id}", method = RequestMethod.DELETE)
@ResponseBody
@ApiOperation(notes = "/deleteJob/{id}", httpMethod = "DELETE", value = "删除JOB")
public Response deleteJob(@PathVariable String id) {
ScheduleJobVo job = scheduleJobService.findScheduleJob(id);
if(job == null){
return Response.failure("任务不存在");
}
if (!ids.contains(id)) {//如果任务在启动列表中
try {
schedulerService.deleteJob(job.getJobName());
} catch (Exception e) {
e.printStackTrace();
}
}
//更新数据库任务状态
Response response = scheduleJobService
.updateScheduleJob(new ScheduleJobVo(id, "2"));
return response;
}
@RequestMapping(value = "/findJobs", method = RequestMethod.POST)
@ResponseBody
@ApiOperation(notes = "/findJobs", httpMethod = "POST", value = "查询JOB")
public Response findJobs(@RequestBody ScheduleJobVo job) {
List<ScheduleJobVo> list = scheduleJobService.findJobs(job);
return Response.success(new PageInfo<ScheduleJobVo>(list));
}
}
8. 自己的JOB
就是@Component注解的简单类,例如:
@Component
public class DispatchJob {
public void dispatch() {
system.out.println("=====拯救世界的程序员=====");
}
}
9. 数据表
好了,到这里调度器已经完成,自己在控制台跑着玩玩吧^^
更新日志:2017-07-11 增加计划开始时间,计划结束时间
更新日志:2017-07-25 增加多实例解决方案QUARTZ多实例解决方案