前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot之定时任务quartz

SpringBoot之定时任务quartz

作者头像
王念博客
发布2019-07-25 17:56:39
1K0
发布2019-07-25 17:56:39
举报
文章被收录于专栏:王念博客王念博客

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

1.Quartz的简单介绍

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

2.Quartz的主要接口介绍

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

Job – 你通过scheduler执行任务,你的任务类需要实现的接口;

JobDetail – 定义Job的实例;

Trigger – 触发Job的执行;

3.简单的Scheduled任务

3.1 首先在主程序开启对定时任务的支持

代码语言:javascript
复制
@EnableScheduling

3.2 编写需要定时跑的代码

代码语言:javascript
复制
     @Scheduled(fixedRate=10000)
	public void test(){
		System.out.println("程序跑来了");
	}

通过@Scheduled注解 使用fixedRate时表示多少次执行一次,单位是毫秒

其实还有cron表达式属性,具体设置可以参考https://cloud.tencent.com/developer/article/1472746

4.整合Quartz

代码语言:txt
复制
 我整合是设置时间到点执行,不是上面的cron表达式那种计划时间或者循环执行。

4.1添加依赖

代码语言:javascript
复制
        <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管理

代码语言:javascript
复制
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

代码语言:javascript
复制
@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 任务方法

代码语言:javascript
复制
    /**
     * 添加任务
     *
     * @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)

代码语言:javascript
复制
@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的任务方法即可

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.Quartz的简单介绍
  • 2.Quartz的主要接口介绍
  • 3.简单的Scheduled任务
  • 4.整合Quartz
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档