springboot mybatis Quartz 完成定时任务

什么是Quartz?

quartz是一个由java编写的任务调度库,由OpenSymphony组织开源出来。绝大多数公司都会用到任务调度这个功能, 比如公司需要定期执行任务调度生成报表, 或者比如博客什么的定时更新之类的,都可以靠Quartz来完成。

任务调度:现在有N个任务(程序),要求在指定时间执行,比如每周二3点执行任务A、每天相隔5s执行任务B等等,这种多任务拥有多种执行策略就是任务调度。而quartz的核心作用,是使任务调度变得丰富、高效、安全,开发者只需要调几个quartz接口并做简单配置,即可实现上述需求。

quartz号称能够同时对上万个任务进行调度,拥有丰富的功能特性,包括任务调度、任务持久化、可集群化、插件等

Quartz有哪些体系结构?(介绍和Cron表达式)

quartz体系结构:

         

1、Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中;

2、JobDetail:Quartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。

3、Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等等

4、Calendar:org.quartz.Calendar和java.util.Calendar不同,它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一个日历时间点)。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。假设,我们安排每周星期一早上10:00执行任务,但是如果碰到法定的节日,任务则不执行,这时就需要在Trigger触发机制的基础上使用Calendar进行定点排除。针对不同时间段类型,Quartz在org.quartz.impl.calendar包下提供了若干个Calendar的实现类,如AnnualCalendar、MonthlyCalendar、WeeklyCalendar分别针对每年、每月和每周进行定义;

5、Scheduler: 代表一个Quartz的独立运行容器, Trigger和JobDetail可以注册到Scheduler中, 两者在Scheduler中拥有各自的组及名称, 组及名称是Scheduler查找定位容器中某一对象的依据, Trigger的组及名称必须唯一, JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法, 允许外部通过组及名称访问和控制容器中Trigger和JobDetail。

Scheduler可以将Trigger绑定到某一JobDetail中, 这样当Trigger触发时, 对应的Job就被执行。一个Job可以对应多个Trigger, 但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()和getXxx()的方法。 可以通过Scheduler.getContext()获取对应的SchedulerContext实例;

6、ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率

Cron表达式特殊字符意义示意表:

特殊字符意义

*

匹配所有的值。如:*在分钟的字段域里表示 每分钟

?

只在日期域和星期域中使用。它被用来指定“非明确的值”

-

指定一个范围。如:“10-12”在小时域意味着“10点、11点、12点”

,

指定几个可选值。如:“MON,WED,FRI”在星期域里表示“星期一、星期三、星期五”

/

指定增量。如:“0/15”在秒域意思是没分钟的0,15,30和45秒。“5/15”在分钟域表示没小时的5,20,35和50。符号“*”在“/”前面(如:*/10)等价于0在“/”前面(如:0/10)

L

表示day-of-month和day-of-week域,但在两个字段中的意思不同,例如day-of-month域中表示一个月的最后一天。如果在day-of-week域表示‘7’或者‘SAT’,如果在day-of-week域中前面加上数字,它表示一个月的最后几天,例如‘6L’就表示一个月的最后一个星期五

W

只允许日期域出现。这个字符用于指定日期的最近工作日。例如:如果你在日期域中写 “15W”,表示:这个月15号最近的工作日。所以,如果15号是周六,则任务会在14号触发。如果15好是周日,则任务会在周一也就是16号触发。如果是在日期域填写“1W”即使1号是周六,那么任务也只会在下周一,也就是3号触发,“W”字符指定的最近工作日是不能够跨月份的。字符“W”只能配合一个单独的数值使用,不能够是一个数字段,如:1-15W是错误的

LW

L和W可以在日期域中联合使用,LW表示这个月最后一周的工作日

#

只允许在星期域中出现。这个字符用于指定本月的某某天。例如:“6#3”表示本月第三周的星期五(6表示星期五,3表示第三周)。“2#1”表示本月第一周的星期一。“4#5”表示第五周的星期三

C

允许在日期域和星期域出现。这个字符依靠一个指定的“日历”。也就是说这个表达式的值依赖于相关的“日历”的计算结果,如果没有“日历”关联,则等价于所有包含的“日历”。如:日期域是“5C”表示关联“日历”中第一天,或者这个月开始的第一天的后5天。星期域是“1C”表示关联“日历”中第一天,或者星期的第一天的后1天,也就是周日的后一天(周一)

Cron 表达式特殊字符意义对应表:

字段

允许值

允许的特殊字符

0-59

, - * /

0-59

, - * /

小时

0-23

, - * /

月内日期

1-31

, - * ? / L W C

1-12 或者 JAN-DEC

, - * /

周内日期

1-7 或者 SUN-SAT

, - * ? / L C #

年(可选)

留空, 1970-2099

, - * /

springboot 怎么使用Quartz?

首先你得准备一个springboot框架(用idea随便造一个,hahaha...)

引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
    <version>2.3.0.RELEASE</version>
</dependency>

在主程序类加上注解:@EnableScheduling

创建我们需要的任务记录表t_schedule_trigger

SET FOREIGN_KEY_CHECKS=0;

DROP TABLE IF EXISTS `t_schedule_trigger`;
CREATE TABLE `t_schedule_trigger` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `job_name` varchar(100) NOT NULL,
  `job_group` varchar(50) NOT NULL,
  `job_class` varchar(200) NOT NULL,
  `job_desc` varchar(200) NOT NULL,
  `cron` varchar(100) NOT NULL,
  `trigger_name` varchar(100) NOT NULL,
  `trigger_group` varchar(50) NOT NULL,
  `trigger_desc` varchar(200) NOT NULL,
  `status` char(1) NOT NULL,
  `account` int(11) NOT NULL,
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `job_name` (`job_name`),
  UNIQUE KEY `trigger_name` (`trigger_name`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

INSERT INTO `t_schedule_trigger` VALUES ('1', '愚人节的问候', '愚人组', 'com.manubao.quartz.job.HelloJob', '愚人节发个ILOVEYOU', '*/5 * * * * ?', '愚人节触发器', 'trigger1', '触发器描述', '1', '1', '2020-12-08 09:26:41');
INSERT INTO `t_schedule_trigger` VALUES ('2', '清明节的问候', '清明组', 'com.manubao.quartz.job.QmJob', '清明节发个IMISSYOU', '*/10 * * * * ?', '清明节触发器', 'trigger1', '清明节描述', '1', '1', '2020-12-08 09:26:41');
INSERT INTO `t_schedule_trigger` VALUES ('3', 'test', 'test', 'com.manubao.quartz.job.MyJob', '测试成功啦', '*/3 * * * * ?', 'test触发', 'trigger1', 'test', '1', '1', '2020-12-08 14:15:15');

创建对应的实体类(自己见一下哈-_-)

Dao层接口:

//添加任务
void addTrigger(Trigger trigger);
//获得所有任务
List<Trigger> getTriggers();

对应的XML:

<insert id="addTrigger"  useGeneratedKeys="true" keyProperty="id">
        insert into t_schedule_trigger( JOB_NAME, JOB_GROUP, JOB_CLASS, JOB_DESC, CRON, TRIGGER_NAME, TRIGGER_GROUP, TRIGGER_DESC, STATUS, ACCOUNT, CREATE_TIME)
        VALUES(#{jobName},#{jobGroup},#{jobClass},#{jobDesc},#{cron},#{triggerName},#{triggerGroup},#{triggerDesc},#{status},#{account},#{createTime})
</insert>

<select id="getTriggers" resultType="com.manubao.quartz.model.Trigger">
        select * from t_schedule_trigger where status=1
</select>

Service层:

//添加任务
void addTrigger(Trigger trigger);

//获得所有任务
List<Trigger> getTriggers();

//定时更新数据库表中的任务(重要)
public void refreshTrigger();

Service层实现类:

@Service
public class TriggerServiceImpl implements TriggerService {
    //自动装配QuartzConfigration配置类中的Scheduler
    @Autowired
    private Scheduler scheduler;

    //装配Dao层接口
    @Autowired
    private TriggerMapper triggerMapper;

    @Override
    public void addTrigger(Trigger trigger) {
        this.triggerMapper.addTrigger(trigger);
    }
    @Override
    public List<Trigger> getTriggers() {
        return this.triggerMapper.getTriggers();
    }
    //定时更新任务
    @Override
    @Scheduled(cron = "*/5 * * * * ?")
    public void refreshTrigger() {
        try {
            //查询出数据库中所有的定时任务
            List<Trigger> jobList = triggerMapper.getTriggers();
            if (jobList != null) {
                for (Trigger trigger : jobList) {
                    //获得每个任务触发器的状态
                    int status = trigger.getStatus();
                    //JobBuild中的JobKey是表明Job身份的一个对象,里面封装了Job的name和group
                    //TriggerBuild中的TriggerKey封装了Trigger的name和group
                    TriggerKey triggerKey = TriggerKey.triggerKey(trigger.getJobName(), trigger.getJobGroup());
                    CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
                    //说明本条任务还没有添加到quartz中
                    if (null == cronTrigger) {
                        if (status==0) { //如果是禁用,则不用创建触发器
                            continue;
                        }
                        JobDetail jobDetail = null;
                        try {
                            //创建JobDetail(数据库中job_name存的任务全路径,这里就可以动态的把任务注入到JobDetail中)
                            jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(trigger.getJobClass()))  //trigger.getJobName() => "com.manubao.quartz.MyJob1"
                                    .withIdentity(trigger.getJobName(), trigger.getJobGroup())
                                    .build();
                            //表达式调度构建器
                            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(trigger.getCron());
                            //按新的cronExpression表达式构建一个新的trigger
                            cronTrigger = TriggerBuilder.newTrigger()
                                    .withIdentity(trigger.getJobName(), trigger.getJobGroup())
                                    .withSchedule(scheduleBuilder)
                                    .build();
                            //把trigger和jobDetail注入到调度器
                            scheduler.scheduleJob(jobDetail, cronTrigger);
                            scheduler.start();
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    } else {
                        //说明查出来的这条任务,已经设置到quartz中了
                        // Trigger已存在,先判断是否需要删除,如果不需要,再判定是否时间有变化
                        if (status==0) { //如果是禁用,从quartz中删除这条任务
                            JobKey jobKey = JobKey.jobKey(trigger.getJobName(), trigger.getJobGroup());
                            scheduler.deleteJob(jobKey);
                            continue;
                        }
                        String searchCron = trigger.getCron(); //获取数据库的
                        String currentCron = cronTrigger.getCronExpression();
                        if (!searchCron.equals(currentCron)) {  //说明该任务有变化,需要更新quartz中的对应的记录
                            //表达式调度构建器
                            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(searchCron);

                            //按新的cronExpression表达式重新构建trigger
                            cronTrigger = cronTrigger.getTriggerBuilder()
                                    .withIdentity(triggerKey)
                                    .withSchedule(scheduleBuilder)
                                    .build();
                            //按新的trigger重新设置job执行
                            scheduler.rescheduleJob(triggerKey, cronTrigger);
                        }
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("定时任务每日刷新触发器任务异常,在ScheduleTriggerServiceImpl的方法refreshTrigger中,异常信息:"+e);
            throw new RuntimeException(e);
        }

    }
}

新建Job类:(里面主要就是你想要的操作)

@Component
@Slf4j
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("测试HelloJob....");
        System.out.println(new Date());
    }
}

@Component
@Slf4j
public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("测试MyJob....");
        System.out.println(new Date());
    }
}

@Component
@Slf4j
public class QmJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("测试QQmJob....");
        System.out.println(new Date());
    }
}

启动类:加注解

@SpringBootApplication
@MapperScan("com.manubao.quartz.dao")
@EnableTransactionManagement
@EnableScheduling

案例测试?

启动项目之后查看控制台:

 

看到这些,那么,恭喜你

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@insist123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值