前言:对于Quartz(kwɔrts)以前在公司用过,比较尴尬的是真的只是用过,写个控制器在任务系统里配置一下cron表达式就完事 https://github.com/songwie/task。从那天起我就对Quartz失去了兴趣,后来在使用SpringBoot的时候了解到Scheduled(Spring 3.1以后支持),就用Scheduled搭建了一个简单的任务系统。当时我就在想怎么弄个到点就能执行的任务,由于用Scheduled注解有很大的局限性,查阅了好多文档(我好后悔我当初没有学好英语,形成如今一直很反感英文文档,每次都是搜索中文博客(开源中国,推酷,简书segmentfault,scdn,.....),若是我英语给力,技术也不会这么差)仍是没有发现比较好的解决方案,当时正好作众筹票务APP,好比用户下单以后30分钟没有支付须要将该订单的库存回收并改变订单状态为失效。若是轮询1秒一次的话,这样会频繁查询订单表,将全部失效时间小于当前时间的而且未支付的全部订单设置为失效,这样即不能作到及时,量比较多的话还会频繁锁表,订单表对于票务网站自己就很高频的,不论是下订单,支付过程的状态变动,仍是查询订单状态。我当时采用了很low的方式,就是查询订单的时候,若是失效时间小于或者等于当前时间就update该ID的状态。对于用户来讲没有什么变化,若是10条订单中只有一个就只会更新一个。问题来了,若是该用户没有查询订单是否是状态仍是未支付的状态呢?因此我写了一个1分钟一次的轮询来解决状态问题。今天我不是来BB这种方案,其实Quartz除了CronTrigger还有SimpleTrigger。java
(百度百科)Quartz是一个彻底由java编写的开源做业调度框架,是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它能够与J2EE与J2SE应用程序相结合也能够单独使用。Quartz能够用来建立简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。git
Scheduler – 与scheduler交互的主要API,这就是所谓的做业调度器github
Job – 你经过scheduler执行任务,你的任务类须要实现的接口;spring
JobDetail – 定义Job的实例;segmentfault
Trigger – 触发Job的执行;并发
3.1 首先在主程序开启对定时任务的支持框架
@EnableScheduling
3.2 编写须要定时跑的代码ide
@Scheduled(fixedRate=10000) public void test(){ System.out.println("程序跑来了"); }
经过@Scheduled注解 使用fixedRate时表示多少次执行一次,单位是毫秒网站
其实还有cron表达式属性,具体设置能够参考https://my.oschina.net/wangnian/blog/668209ui
我整合是设置时间到点执行,不是上面的cron表达式那种计划时间或者循环执行。
4.1添加依赖
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.3</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency>
4.2 配置job交给spring管理
package com.yudianbank.task.config; import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.scheduling.quartz.AdaptableJobFactory; import org.springframework.stereotype.Component; @Component public class JobFactory extends AdaptableJobFactory { @Autowired private AutowireCapableBeanFactory capableBeanFactory; @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { //调用父类的方法 Object jobInstance = super.createJobInstance(bundle); //进行注入 capableBeanFactory.autowireBean(jobInstance); return jobInstance; } }
主要目的就是解决job类 注入其余service或者使用Spring组件
4.3配置javaconfig bean
@Configuration public class BeanConfig { @Autowired JobFactory jobFactory; /** * 注册调度器 * * @return */ @Bean public SchedulerFactoryBean createSchedulerFactoryBean() { SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); schedulerFactoryBean.setJobFactory(jobFactory); return schedulerFactoryBean; } @Bean public JobDetailImpl createJobDetailImpl() { return new JobDetailImpl(); } }
4.4 任务方法
/** * 添加任务 * * @param job 任务类 * @param date 任务时间 * @param jobDetailName 任务消息名字 * @param triggerIdentity 触发器的惟一名 * @param description 触发器的描述 */ public synchronized void addJob(Job job, Date date, String jobDetailName, String triggerIdentity, String description, String url, String bodyParameter) { //job类的参数 JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put("url", url); jobDataMap.put("bodyParameter", bodyParameter); jobDataMap.put("jobDetailName", jobDetailName); //这是job类的任务 jobDetail.setName(jobDetailName); jobDetail.setJobClass(job.getClass()); jobDetail.setJobDataMap(jobDataMap); //做业触发器 Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerIdentity).withDescription(description) // .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(3))//循环间隔多久 .startAt(date)//执行时间 .build(); //做业调度器 try { Scheduler scheduler = schedulerFactoryBean.getScheduler(); //Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); } catch (SchedulerException e) { logger.error("任务调度器异常:", e);
4.5 编写任务类(这里就能够注入service)
@Service public class JobTask implements Job { static final Logger logger = LoggerFactory.getLogger(JobTask.class); @Autowired ExecuteTaskService executeTaskService; @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { JobDataMap jobDataMap = jobExecutionContext.getMergedJobDataMap(); String url = jobDataMap.get("url").toString(); String bodyParameter = jobDataMap.get("bodyParameter").toString(); String jobDetailName = jobDataMap.get("jobDetailName").toString(); try { executeTaskService.execute(url, bodyParameter, jobDetailName); } catch (Exception ex) { } }
关于job的状态数据(即JobDataMap)和并发性,还有一些地方须要注意。在job类上能够加入一些注解,这些注解会影响job的状态和并发性。
@DisallowConcurrentExecution:将该注解加到job类上,告诉Quartz不要并发地执行同一个job定义(这里指特定的job类)的多个实例。请注意这里的用词。拿前一小节的例子来讲,若是“SalesReportJob”类上有该注解,则同一时刻仅容许执行一个“SalesReportForJoe”实例,但能够并发地执行“SalesReportForMike”类的一个实例。因此该限制是针对JobDetail的,而不是job类的。可是咱们认为(在设计Quartz的时候)应该将该注解放在job类上,由于job类的改变常常会致使其行为发生变化。
@PersistJobDataAfterExecution:将该注解加在job类上,告诉Quartz在成功执行了job类的execute方法后(没有发生任何异常),更新JobDetail中JobDataMap的数据,使得该job(即JobDetail)在下一次执行的时候,JobDataMap中是更新后的数据,而不是更新前的旧数据。和 @DisallowConcurrentExecution注解同样,尽管注解是加在job类上的,但其限制做用是针对job实例的,而不是job类的。由job类来承载注解,是由于job类的内容常常会影响其行为状态(好比,job类的execute方法须要显式地“理解”其”状态“)。
若是你使用了@PersistJobDataAfterExecution注解,咱们强烈建议你同时使用@DisallowConcurrentExecution注解,由于当同一个job(JobDetail)的两个实例被并发执行时,因为竞争,JobDataMap中存储的数据极可能是不肯定的。
调用4.4的任务方法便可
博客地址:https://my.oschina.net/wangnian