SpringBoot坚持学习第九天:集成定时任务Quartz

常用表达式的例子
每隔5秒执行一次:*/5 * * * * ?
每隔5分钟执行一次:0 */5 * * * ?
每天5点执行一次:0 0 5 * * ?
每月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 * * ?
周一到周五每天上午10:15执行作业 0 15 10 ? * MON-FRI
表示2002-2006年的每个月的最后一个星期五上午10:15执行作
表示2002-2006年的每个月的最后一个星期五上午10:15执行
0 15 10 ? 6L 2002-2006
朝九晚五工作时间内每半小时 0 0/30 9-17 * * ?
表示每个星期三中午12点 0 0 12 ? * WED
在每天下午2点到下午2:59期间的每1分钟触发 0 * 14 * * ?
在每天下午2点到下午2:55期间的每5分钟触发 0 0/5 14 * * ?
每个月3号 凌晨3点 执行一次 0 0 3 3 * ?

一、Quartz是什么

        导入讲解Quartz的深度好文:Quartz使用总结 作者:路边飞

        Quartz是一个任务调度框架。比如你遇到这样的问题。

  • 想每月25号,信用卡自动还款
  • 想每年4月1日自己给当年暗恋女神发一封匿名贺卡
  • 想每隔1小时,备份一下自己的爱情动作片 学习笔记到云盘

        这些问题总结起来就是:在某一个有规律的时间点干某件事。并且时间的触发的条件可以非常复杂(比如每月最后一个工作日的17:50),复杂到需要一个专门的框架来干这个事。 Quartz就是来干这样的事,你给它一个触发条件的定义,它负责到了时间点,触发相应的Job起来干活。

二、SpringBoot内置定时器

         首先导入依赖。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>

SpringBoot内置的定时器需要使用@Scheduled注解的cron属性来指明运行周期。

cron属性共有七位,分别是 秒、分、小时、每日、每月、星期、年

0 15 10 ? * *   每天10:15运行

0 * 14 * * ?      每天14点到15点之间每分钟运行一次,开始于14:00,结束于14:59。

0 0/5 14 * * ?  每天14点到15点每5分钟运行一次,开始于14:00,结束于14:55。

0 15 10 15 * ? 每月15日10:15分运行。

@Component
public class SchedulerTask {
    @Scheduled(cron = "*/6 * * * * ?")
    private void process() {
        System.out.println("现在时间:" + dateToDateStr(new Date(), DEFAULT_FORMAT));
    }
}
@SpringBootApplication
@EnableScheduling
public class QuartzApplication {
    public static void main(String[] args) {
        SpringApplication.run(QuartzApplication.class, args);
    }
}

   

每隔六秒执行一次 。除了使用cron属性以外,还能换成fixedRate固定的周期。

  • @Scheduled(fixedRate = 6000)  :上一次开始执行时间点之后 6 秒再执行。
  • @Scheduled(fixedDelay = 6000):上一次执行完毕时间点之后 6 秒再执行。

三、Quartz

       quartz音括子。

       Quartz有四个核心概念:

Job(任务)   

        定义任务的执行逻辑,在实现接口的 execute 方法中编写所需要定时执行的 Job(任务)

JobDetail(任务信息)

        JobDetail 定义的是任务数据,而真正的执行逻辑是在Job中。

Trigger(触发器)

        触发 Job 执行的时间触发规则,主要有 SimpleTrigger 和 CronTrigger 这两个子类。当且仅当需调度一次或者以固定时间间隔周期执行调度,SimpleTrigger 是最适合的选择;而CronTrigger 则可以通过 Cron 表达式定义出各种复杂时间规则的调度方案

Scheduler(调度器)

        调度器就相当于一个容器,用于绑定Trigger与JobDetail。两者在 Scheduler 中拥有各自的不同的组及名称。   

         为什么设计成JobDetail + Job,不直接使用Job?这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题。 

Cron表达式简单来说就是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式:

Seconds Minutes Hours DayofMonth Month DayofWeek Year (七个域)
秒 分钟 小时 月份中的天 月份 星期中的天 年
Seconds Minutes Hours DayofMonth Month DayofWeek(六个域)
各字段的含义
字段    允许值    允许的特殊字符
秒(Seconds)    0~59的整数    , - * / 四个字符
分(Minutes)    0~59的整数    , - * / 四个字符
小时(Hours)    0~23的整数    , - * / 四个字符
日期(日)(DayofMonth)    1~31的整数(但是你需要考虑你月的天数)    ,- * ? / L W C 八个字符
月份(Month)    1~12的整数或者 JAN-DEC    , - * / 四个字符
星期(DayofWeek)    1~7的整数或者 SUN-SAT (1=SUN)    , - * ? / L C # 八个字符
年(可选,留空)(Year)    1970~2099    , - * / 四个字符
‘*’ 表示 匹配该区域任何值 假如Seconds使用 * 就会在每秒触发
? 可以用在日期(日)和星期字段 用来指出‘没有特定的值’ 日期和星期字段是互斥的 一个字段设置值之后另一个字段必须表示没有意义 所以就出现了’?’
也就是说 日期和星期 必须有且仅有 一个是’?’
‘-’ 表示范围 比如Seconds 2-12 表示在2秒到12秒时间内 每秒触发一次
‘/’ 表示起始时间开始触发每隔固定事件触发一次 Minutes 5/15 表示第5分钟开始触发第一次 过15分钟也就是 20分钟 35分钟弄50分钟 各触发一次
‘,’ 表示枚举 比如 Hours 1,5,8 表示在1点 5点 8点 各触发一次
‘L’表示最后 只能出现在 日期和星期中 如果在星期中是 5L表示在最后一个星期四 触发一次 (1-7 周日-周六)
‘W’表示有效工作日(周一到周五) 只能出现在日期中 系统将会在指定日期的最近有效工作日触发 例如 使用5W 如果5日是星期日那么将会在6日(星期五)触发 如果5日是星期六 那么将会在4日(星期五)触发 如果5日是星期一到星期五中的某一天 那就在五日触发 W的最近工作日寻找不会跨月
LW 可以连用 表示在某月的最后一天最近的工作日
‘#’只能在日期中出现 4#2 表示 每个月第四个星期二

常用表达式的例子
每隔5秒执行一次:*/5 * * * * ?
每隔5分钟执行一次:0 */5 * * * ?
每天5点执行一次:0 0 5 * * ?
每月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 * * ?
周一到周五每天上午10:15执行作业 0 15 10 ? * MON-FRI
表示2002-2006年的每个月的最后一个星期五上午10:15执行作
表示2002-2006年的每个月的最后一个星期五上午10:15执行
0 15 10 ? 6L 2002-2006
朝九晚五工作时间内每半小时 0 0/30 9-17 * * ?
表示每个星期三中午12点 0 0 12 ? * WED
在每天下午2点到下午2:59期间的每1分钟触发 0 * 14 * * ?
在每天下午2点到下午2:55期间的每5分钟触发 0 0/5 14 * * ?
每个月3号 凌晨3点 执行一次 0 0 3 3 * ?


感谢:https://www.cnblogs.com/javahr/p/8318728.html
http://www.cnblogs.com/xiandedanteng/p/3678650.html

四、SimpleSchedule 

        使用SimpleTrgger表明定时任务不是很复杂,仅仅运行一次或者按照指定的周期进行运行。

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

        先编写一个Job接口实现,编写它的任务方法,并表面其 运行时需要提供的数据信息。Job 运行时的信息保存在 JobDataMap 实例中。需要由JobDetail来指明。

@Getter
@Setter
public class SampleJob extends QuartzJobBean {

    private String name;

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println(String.format("Hello %s!", this.name));
    }
}

再编写一个JobDetail来描述 Job 的实现类及其他相关的运行时信息。

    @Bean
    public JobDetail generateJobDetail() {
        return JobBuilder.newJob(SampleJob.class).withIdentity("sampleJob").usingJobData("name", "World").
                storeDurably().build();
    }

最后再编写触发规则

    @Bean
    public Trigger sampleJobTrigger() {
        //触发规则:指定每两秒执行一次
        SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(2).repeatForever();
        //将触发规则与JobDetail绑定
        return TriggerBuilder.newTrigger().forJob(generateJobDetail())
                .withIdentity("sampleTrigger").withSchedule(scheduleBuilder).build();
    }

最后将这个触发规则Trigger与任务对象JobDetail注册到同一个容器Scheduler中。

@Configuration
public class SampleScheduler {

    @Bean
    public JobDetail generateJobDetail(){...}

    @Bean
    public Trigger sampleJobTrigger() {...}
}

  

五、CronSchedule 复杂调度任务

        按照上述编写习惯,首先先编写一个Job接口实现,然后再编写一个JobDetail来包装Job接口实现。再编写一个触发规则Trigger。最后将JobDetail与Trigger绑定到一起并加入Schedule中。

public class ScheduledJob implements Job {
    @Override  
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("schedule job1 is running ...");
    }  
} 
@Component
public class MySchedule {

    /**
     * 生成Schedule的工厂
     */
    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    public void startScheduleTask() throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        scheduleJob1(scheduler);
    }

    private void scheduleJob1(Scheduler scheduler) throws SchedulerException {
        //先通过Job生成JobDetail
        JobDetail jobDetail = JobBuilder.newJob(ScheduledJob.class).
withIdentity("job1", "groupName").build();
        //核心:编写cron周期规则
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/6 * * * * ?");
        //Trigger
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().
withIdentity("trigger1", "group1").withSchedule(scheduleBuilder).build();
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }
}

最后在项目启动的时候,开启定时任务

@Component
public class MyStartupRunner implements CommandLineRunner {

    @Autowired
    public MySchedule scheduleJobs;

    @Override
    public void run(String... args) throws Exception {
        scheduleJobs.startScheduleTask();
        System.out.println(">>>>>>>>>>>>>>>定时任务开始执行<<<<<<<<<<<<<");
    }
}

  

posted @ 2022-07-17 12:15  小大宇  阅读(68)  评论(0编辑  收藏  举报