SpringBoot之定时任务quartz

前言:对于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

1.Quartz的简单介绍

(百度百科)Quartz是一个彻底由java编写的开源做业调度框架,是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它能够与J2EE与J2SE应用程序相结合也能够单独使用。Quartz能够用来建立简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。git

2.Quartz的主要接口介绍

Scheduler – 与scheduler交互的主要API,这就是所谓的做业调度器github

Job – 你经过scheduler执行任务,你的任务类须要实现的接口;spring

JobDetail – 定义Job的实例;segmentfault

Trigger – 触发Job的执行;并发

3.简单的Scheduled任务

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

4.整合Quartz

     我整合是设置时间到点执行,不是上面的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

相关文章
相关标签/搜索