Contents

spring项目中经常用到定时任务,

@Scheduled 定时任务

 1@Component
 2public class ScheduledTest {
 3    /**
 4     * 定时任务
 5     */
 6    @Scheduled(cron = "")
 7    public void scheduledTest() {
 8        System.out.println("定时开始 " + new Date());
 9    }
10}
11

cron 解释

cron表达式是一个字符串,分为6或7个域,每两个域之间用空格分隔,其语法格式为:

"秒域 分域 时域 日域 月域 周域 年域"

其中,年域可以省略,省略时表示每年。

定时任务启用

在启动类上添加@EnableScheduling 开启定时任务

常用 cron 表达式

每隔5秒执行一次:*/5 * * * * ?

每隔1分钟执行一次:0 */1 * * * ?

每天23点执行一次:0 0 23 * * ?

每天凌晨1点执行一次:0 0 1 * * ?

每月1号凌晨1点执行一次:0 0 1 1 * ?

每月最后一天23点执行一次:0 0 23 L * ?

每周星期天凌晨1点实行一次:0 0 1 ? * L

在26分、29分、33分执行一次:0 26,29,33 * * * ?

每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?

cron 表达式在线生成 https://www.bejson.com/othertools/cron/

Quartz

介绍

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,Quartz是一个完全由java编写的开源作业调度框架。

使用

Snipaste_2020-07-14_00-21-04.png

Quartz的基本组成部分:

调度器:Scheduler
任务:JobDetail
触发器:Trigger,包括SimpleTrigger和CronTrigger

springboot Demo

1<dependency>
2     <groupId>org.springframework.boot</groupId>
3     <artifactId>spring-boot-starter-quartz</artifactId>
4 </dependency>
5

创建job

 1
 2import org.quartz.Job;
 3import org.quartz.JobExecutionContext;
 4import org.quartz.JobExecutionException;
 5
 6public class HelloJob implements Job {
 7    @Override
 8    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
 9        System.out.println("hello job");
10    }
11}
12

创建Schedule

 1
 2import org.quartz.*;
 3import org.quartz.impl.StdSchedulerFactory;
 4
 5import java.util.concurrent.TimeUnit;
 6
 7public class MyQuartz {
 8    public static void main(String[] args) throws SchedulerException, InterruptedException {
 9        // 1、创建调度器Scheduler
10        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
11        Scheduler scheduler = schedulerFactory.getScheduler();
12        // 2、创建JobDetail实例
13        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build();
14
15        // 3、构建Trigger实例,每隔1s执行一次
16        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
17                .startNow()
18                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
19                        //每隔1s执行一次
20                        .withIntervalInSeconds(1)
21                        //一直执行
22                        .repeatForever()).build();
23        //4、执行
24        scheduler.scheduleJob(jobDetail, trigger);
25        System.out.println("--------scheduler start ! ------------");
26        scheduler.start();
27        //睡眠
28        TimeUnit.MINUTES.sleep(1);
29        scheduler.shutdown();
30        System.out.println("--------scheduler shutdown ! ------------");
31    }
32}
33

配置

 1server:
 2  port: 8080
 3spring:
 4  datasource:
 5    url: jdbc:mysql://127.0.0.1:3306/quartz?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
 6    username: root
 7    password: 123456
 8    driver-class-name: com.mysql.cj.jdbc.Driver
 9    type: com.zaxxer.hikari.HikariDataSource
10    hikari:
11      minimum-idle: 5
12      connection-test-query: SELECT 1 FROM DUAL
13      maximum-pool-size: 20
14      auto-commit: true
15      idle-timeout: 30000
16      pool-name: SpringBootDemoHikariCP
17      max-lifetime: 60000
18      connection-timeout: 30000
19  quartz:
20    # 参见 org.springframework.boot.autoconfigure.quartz.QuartzProperties
21    job-store-type: jdbc
22    wait-for-jobs-to-complete-on-shutdown: true
23    scheduler-name: SpringBootDemoScheduler
24    properties:
25      org.quartz.threadPool.threadCount: 5
26      org.quartz.threadPool.threadPriority: 5
27      org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
28      org.quartz.jobStore.misfireThreshold: 5000
29      org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
30      org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
31      # 在调度流程的第一步,也就是拉取待即将触发的triggers时,是上锁的状态,即不会同时存在多个线程拉取到相同的trigger的情况,也就避免的重复调度的危险。参考:https://segmentfault.com/a/1190000015492260
32      org.quartz.jobStore.acquireTriggersWithinLock: true
33logging:
34  level:
35    com.example.demo: debug
36    org.quartz: debug
37
38
  1@Service
  2@Slf4j
  3public class JobServiceImpl implements JobService {
  4    private final Scheduler scheduler;
  5    private final JobMapper jobMapper;
  6
  7    @Autowired
  8    public JobServiceImpl(Scheduler scheduler, JobMapper jobMapper) {
  9        this.scheduler = scheduler;
 10        this.jobMapper = jobMapper;
 11    }
 12
 13    /**
 14     * 添加并启动定时任务
 15     *
 16     * @param form 表单参数 {@link JobForm}
 17     * @return {@link JobDetail}
 18     * @throws Exception 异常
 19     */
 20    @Override
 21    public void addJob(JobForm form) throws Exception {
 22        // 启动调度器
 23        scheduler.start();
 24
 25        // 构建Job信息
 26        JobDetail jobDetail = JobBuilder.newJob(JobUtil.getClass(form.getJobClassName()).getClass()).withIdentity(form.getJobClassName(), form.getJobGroupName()).build();
 27
 28        // Cron表达式调度构建器(即任务执行的时间)
 29        CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(form.getCronExpression());
 30
 31        //根据Cron表达式构建一个Trigger
 32        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(form.getJobClassName(), form.getJobGroupName()).withSchedule(cron).build();
 33
 34        try {
 35            scheduler.scheduleJob(jobDetail, trigger);
 36        } catch (SchedulerException e) {
 37            log.error("【定时任务】创建失败!", e);
 38            throw new Exception("【定时任务】创建失败!");
 39        }
 40
 41    }
 42
 43    /**
 44     * 删除定时任务
 45     *
 46     * @param form 表单参数 {@link JobForm}
 47     * @throws SchedulerException 异常
 48     */
 49    @Override
 50    public void deleteJob(JobForm form) throws SchedulerException {
 51        scheduler.pauseTrigger(TriggerKey.triggerKey(form.getJobClassName(), form.getJobGroupName()));
 52        scheduler.unscheduleJob(TriggerKey.triggerKey(form.getJobClassName(), form.getJobGroupName()));
 53        scheduler.deleteJob(JobKey.jobKey(form.getJobClassName(), form.getJobGroupName()));
 54    }
 55
 56    /**
 57     * 暂停定时任务
 58     *
 59     * @param form 表单参数 {@link JobForm}
 60     * @throws SchedulerException 异常
 61     */
 62    @Override
 63    public void pauseJob(JobForm form) throws SchedulerException {
 64        scheduler.pauseJob(JobKey.jobKey(form.getJobClassName(), form.getJobGroupName()));
 65    }
 66
 67    /**
 68     * 恢复定时任务
 69     *
 70     * @param form 表单参数 {@link JobForm}
 71     * @throws SchedulerException 异常
 72     */
 73    @Override
 74    public void resumeJob(JobForm form) throws SchedulerException {
 75        scheduler.resumeJob(JobKey.jobKey(form.getJobClassName(), form.getJobGroupName()));
 76    }
 77
 78    /**
 79     * 重新配置定时任务
 80     *
 81     * @param form 表单参数 {@link JobForm}
 82     * @throws Exception 异常
 83     */
 84    @Override
 85    public void cronJob(JobForm form) throws Exception {
 86        try {
 87            TriggerKey triggerKey = TriggerKey.triggerKey(form.getJobClassName(), form.getJobGroupName());
 88            // 表达式调度构建器
 89            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(form.getCronExpression());
 90
 91            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
 92
 93            // 根据Cron表达式构建一个Trigger
 94            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
 95
 96            // 按新的trigger重新设置job执行
 97            scheduler.rescheduleJob(triggerKey, trigger);
 98        } catch (SchedulerException e) {
 99            log.error("【定时任务】更新失败!", e);
100            throw new Exception("【定时任务】创建失败!");
101        }
102    }
103
104    /**
105     * 查询定时任务列表
106     *
107     * @param currentPage 当前页
108     * @param pageSize    每页条数
109     * @return 定时任务列表
110     */
111    @Override
112    public PageInfo<JobAndTrigger> list(Integer currentPage, Integer pageSize) {
113        PageHelper.startPage(currentPage, pageSize);
114        List<JobAndTrigger> list = jobMapper.list();
115        return new PageInfo<>(list);
116    }
117}
118