在实践中遇到动态管理定时任务的需求,场景通常是动态添加、更新、删除任务,借助Quartz,可方便实现功能。
以下使用Quartz结合Spring Boot方式使用。
POM依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>
spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> <
relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <
dependency> <groupId>org.springframework.boot</groupId> <artifactId>
spring-boot-starter</artifactId> </dependency> <dependency> <groupId>
org.springframework</groupId> <artifactId>spring-context-support</artifactId> </
dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId
>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <
dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</
artifactId> <version>2.3.0</version> </dependency> </dependencies>
代码
QuartzManager类管理任务的增删改查。
/** * @author wzx * @time 2018/6/9 */ @Component @Scope("singleton") public
class QuartzManager implements ApplicationContextAware { private static
SchedulerFactory schedulerFactory =new StdSchedulerFactory(); private static
final String JOB_DEFAULT_GROUP_NAME ="JOB_DEFAULT_GROUP_NAME"; private static
final String TRIGGER_DEFAULT_GROUP_NAME ="TRIGGER_DEFAULT_GROUP_NAME"; private
Logger logger = LoggerFactory.getLogger(QuartzManager.class); private
ApplicationContext applicationContext; private Scheduler scheduler;@Autowired
private AutowiringSpringBeanJobFactory autowiringSpringBeanJobFactory; public
void start() { //启动所有任务 try { this.scheduler = schedulerFactory.getScheduler();
scheduler.setJobFactory(autowiringSpringBeanJobFactory);//
启动所有任务,这里获取AbstractTask的所有子类 Map<String, AbstractTask> tasks =
applicationContext.getBeansOfType(AbstractTask.class); tasks.forEach((k, v) ->
{ String cronExpression = v.getCronExpression();if (cronExpression != null) {
addJob(k, v.getClass().getName(), cronExpression); } }); logger.info("start
jobs finished."); } catch (SchedulerException e) { logger.error(e.getMessage(),
e); throw new RuntimeException("init Scheduler failed"); } } public boolean
addJob(String jobName, String jobClass, String cronExp) { boolean result = false
;if (!CronExpression.isValidExpression(cronExp)) { logger.error("Illegal cron
expression format({})", cronExp); return result; } try { JobDetail jobDetail =
JobBuilder.newJob().withIdentity(new JobKey(jobName, JOB_DEFAULT_GROUP_NAME)) .
ofType((Class<Job>) Class.forName(jobClass)) .build(); Trigger trigger =
TriggerBuilder.newTrigger() .forJob(jobDetail) .withSchedule
(CronScheduleBuilder.cronSchedule(cronExp)) .withIdentity(new
TriggerKey(jobName, TRIGGER_DEFAULT_GROUP_NAME)) .build(); scheduler.scheduleJob
(jobDetail, trigger); scheduler.start(); } catch (Exception e) { logger.error
(e.getMessage(), e); logger.error("QuartzManager add job failed"); } return
result; } public boolean updateJob(String jobName, String cronExp) { boolean
result = false; if (!CronExpression.isValidExpression(cronExp)) { logger.error(
"Illegal cron expression format({})", cronExp); return result; } JobKey jobKey =
new JobKey(jobName, JOB_DEFAULT_GROUP_NAME); TriggerKey triggerKey = new
TriggerKey(jobName, TRIGGER_DEFAULT_GROUP_NAME); try { if
(scheduler.checkExists(jobKey) && scheduler.checkExists(triggerKey)) { JobDetail
jobDetail = scheduler.getJobDetail(jobKey); Trigger newTrigger = TriggerBuilder.
newTrigger() .forJob(jobDetail) .withSchedule
(CronScheduleBuilder.cronSchedule(cronExp)) .withIdentity(new
TriggerKey(jobName, TRIGGER_DEFAULT_GROUP_NAME)) .build(); scheduler.
rescheduleJob(triggerKey, newTrigger); result = true; } else { logger.error(
"update job name:{},group name:{} or trigger name:{},group name:{} not exists.."
, jobKey.getName(), jobKey.getGroup(), triggerKey.getName(),
triggerKey.getGroup()); } } catch (SchedulerException e) { logger.error
(e.getMessage(), e); logger.error("update job name:{},group name:{} failed!",
jobKey.getName(), jobKey.getGroup()); } return result; } public boolean
deleteJob(String jobName) { boolean result = false; JobKey jobKey = new JobKey
(jobName, JOB_DEFAULT_GROUP_NAME); try { if (scheduler.checkExists(jobKey)) {
result = scheduler.deleteJob(jobKey); } else { logger.error("delete job
name:{},group name:{} not exists.", jobKey.getName(), jobKey.getGroup()); } }
catch (SchedulerException e) { logger.error(e.getMessage(), e); logger.error(
"delete job name:{},group name:{} failed!", jobKey.getName(), jobKey.getGroup())
; }return result; } @Override public void setApplicationContext
(ApplicationContext applicationContext) throws BeansException { this.
applicationContext = applicationContext; } }
定义抽象任务类AbstractTask,实现Job接口,子类Job实例需实现executeInternal方法。
/** * @author wzx * @time 2018/6/9 */ public abstract class AbstractTask
implements Job { private Logger logger =
LoggerFactory.getLogger(AbstractTask.class);protected abstract void
executeInternal(JobExecutionContext context); protected String cronExpression;
@Override public void execute(JobExecutionContext context) { try {
executeInternal(context); }catch (Exception e) { logger.error(e.getMessage(),
e); logger.error("job execute failed!"); } } public String getCronExpression() {
return cronExpression; } }
测试TestTask类,继承AbstractTask类,实现executeInternal方法。
/** * @author wzx * @time 2018/6/9 */ @Component("testTask") public class
TestTask extends AbstractTask { private Logger logger =
LoggerFactory.getLogger(TestTask.class);@PostConstruct public void init() { this
.cronExpression ="0/2 * * * * ? "; } @Override protected void executeInternal
(JobExecutionContext context) { logger.info("test task start execute."); try {
TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { logger.info("test
task execute interrupted."); } logger.info("test task execute end."); } }
测试用例,测试增加、更新、删除功能。
@RunWith(SpringRunner.class) @SpringBootTest public class
SpringbootdemoApplicationTests { private Logger logger = LoggerFactory.getLogger
(SpringbootdemoApplicationTests.class); @Autowired private QuartzManager
quartzManager; @Autowired private TestTask testTask; @Test public void
testCronTest() throws InterruptedException { quartzManager.start(); TimeUnit
.SECONDS.sleep(10); logger.info("start update job"); //修改任务 quartzManager
.updateJob("testTask", "0/3 * * * * ? "); logger.info("end update job");
TimeUnit.SECONDS.sleep(10); logger.info("start delete job"); //删除任务
quartzManager.deleteJob("testTask"); logger.info("end delete job"); TimeUnit
.SECONDS.sleep(10); //添加任务 logger.info("start add job"); quartzManager.addJob(
"testTask", testTask.getClass().getName(), "0/3 * * * * ?"); logger.info("end
add job"); TimeUnit.SECONDS.sleep(10); //修改任务 logger.info("start update job");
quartzManager.updateJob("testTask", "0/3 * * * * ?"); logger.info("end update
job"); TimeUnit.SECONDS.sleep(10); //删除任务 logger.info("start delete job");
quartzManager.deleteJob("testTask"); logger.info("end delete job"); logger.info(
"end."); } }
结果输出,符合预期。
2018-06-09 16:43:58.052 INFO 2884 --- [ main]
org.quartz.impl.StdSchedulerFactory : Using default implementationfor
ThreadExecutor2018-06-09 16:43:58.054 INFO 2884 --- [ main]
org.quartz.simpl.SimpleThreadPool : Job execution threads will use class loader
of thread: main2018-06-09 16:43:58.066 INFO 2884 --- [ main]
org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of
type: class org.quartz.core.SchedulerSignalerImpl2018-06-09 16:43:58.066 INFO
2884 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.0
created.2018-06-09 16:43:58.066 INFO 2884 --- [ main]
org.quartz.simpl.RAMJobStore : RAMJobStore initialized.2018-06-09 16:43:58.067
INFO2884 --- [ main] org.quartz.core.QuartzScheduler : Scheduler meta-data:
Quartz Scheduler (v2.3.0)'DefaultQuartzScheduler' with instanceId
'NON_CLUSTERED' Scheduler class: 'org.quartz.core.QuartzScheduler' - running
locally. NOT STARTED. Currentlyin standby mode. Number of jobs executed: 0
Using thread pool'org.quartz.simpl.SimpleThreadPool' - with 10 threads. Using
job-store'org.quartz.simpl.RAMJobStore' - which does not support persistence.
and is not clustered.2018-06-09 16:43:58.067 INFO 2884 --- [ main]
org.quartz.impl.StdSchedulerFactory : Quartz scheduler'DefaultQuartzScheduler'
initialized from default resource filein Quartz package: 'quartz.properties'
2018-06-09 16:43:58.067 INFO 2884 --- [ main]
org.quartz.impl.StdSchedulerFactory : Quartz scheduler version:2.3.0 2018-06-09
16:43:58.077 INFO 2884 --- [ main] org.quartz.core.QuartzScheduler : Scheduler
DefaultQuartzScheduler_$_NON_CLUSTERED started.2018-06-09 16:43:58.077 INFO 2884
--- [ main] c.e.s.service.task.QuartzManager : start jobs finished.2018-06-09
16:43:58.081 INFO 2884 --- [eduler_Worker-1] c.e.s.service.task.TestTask : test
task start execute.2018-06-09 16:44:01.082 INFO 2884 --- [eduler_Worker-1]
c.e.s.service.task.TestTask : test task execute end.... 2018-06-09 16:44:08.077
INFO2884 --- [ main] c.e.s.SpringbootdemoApplicationTests : start update job
2018-06-09 16:44:08.078 INFO 2884 --- [ main]
c.e.s.SpringbootdemoApplicationTests : end update job2018-06-09 16:44:09.001
INFO2884 --- [eduler_Worker-7] c.e.s.service.task.TestTask : test task start
execute.2018-06-09 16:44:12.002 INFO 2884 --- [eduler_Worker-7]
c.e.s.service.task.TestTask : test task execute end.... 2018-06-09 16:44:18.078
INFO2884 --- [ main] c.e.s.SpringbootdemoApplicationTests : start delete job
2018-06-09 16:44:18.078 INFO 2884 --- [ main]
c.e.s.SpringbootdemoApplicationTests : end delete job2018-06-09 16:44:28.079
INFO2884 --- [ main] c.e.s.SpringbootdemoApplicationTests : start add job 2018-
06-09 16:44:28.079 INFO 2884 --- [ main] org.quartz.core.QuartzScheduler :
Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.2018-06-09 16:44:28.079
INFO2884 --- [ main] c.e.s.SpringbootdemoApplicationTests : end add job 2018-06
-09 16:44:30.001 INFO 2884 --- [eduler_Worker-1] c.e.s.service.task.TestTask :
test task start execute.2018-06-09 16:44:33.001 INFO 2884 --- [eduler_Worker-1]
c.e.s.service.task.TestTask : test task execute end.... 2018-06-09 16:44:38.080
INFO2884 --- [ main] c.e.s.SpringbootdemoApplicationTests : start update job
2018-06-09 16:44:38.080 INFO 2884 --- [ main]
c.e.s.SpringbootdemoApplicationTests : end update job2018-06-09 16:44:39.001
INFO2884 --- [eduler_Worker-4] c.e.s.service.task.TestTask : test task start
execute.2018-06-09 16:44:42.003 INFO 2884 --- [eduler_Worker-4]
c.e.s.service.task.TestTask : test task execute end.... 2018-06-09 16:44:48.080
INFO2884 --- [ main] c.e.s.SpringbootdemoApplicationTests : start delete job
2018-06-09 16:44:48.080 INFO 2884 --- [ main]
c.e.s.SpringbootdemoApplicationTests : end delete job2018-06-09 16:44:48.080
INFO2884 --- [ main] c.e.s.SpringbootdemoApplicationTests : end. 2018-06-09 16:
44:48.083 INFO 2884 --- [ Thread-2] s.c.a.AnnotationConfigApplicationContext :
Closing
org.springframework.context.annotation.AnnotationConfigApplicationContext@1da2cb77:
startup date [Sat Jun09 16:43:57 CST 2018]; root of context hierarchy
Job默认由Quartz管理,如果需要使用Spring容器管理bean,也就是依赖注入,需要指定JobFactory,也就是指定将Job由Spring容器管理。
/** * @author wzx * @time 2018/6/9 */ @Component public class
AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
ApplicationContextAware { private transient AutowireCapableBeanFactory
beanFactory;@Override protected Object createJobInstance(TriggerFiredBundle
bundle)throws Exception { final Object jobInstance = super
.createJobInstance(bundle); beanFactory.autowireBean(jobInstance);return
jobInstance; }@Override public void setApplicationContext(ApplicationContext
applicationContext)throws BeansException { this.beanFactory =
applicationContext.getAutowireCapableBeanFactory(); } }
热门工具 换一换