SpringBoot系列学习笔记(二十四)之定时任务

SpringBoot中实现定时任务的两种方式


以前的笔记用OneNote写的,传到了博客园很不方便换成了Markdown

在Spring+SpringMVC环境中要实现定时任务一共有两种解决方案:

  • 使用Spring自带的定时任务处理@Scheduled注解
  • 使用第三方框架Quartz

@Schedule

  1. 创建SpringBoot项目并导入Web依赖
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
  1. 在SpringBoot的启动类里加上@EnableScheduling注解表示开启一个定时任务
@SpringBootApplication
@EnableScheduling
public class ScheduleApplication {

    public static void main(String[] args) {
        SpringApplication.run(ScheduleApplication.class, args);
    }

}
  1. 配置你需要的定时任务
@Service
public class HelloService {
    @Scheduled(fixedDelay = 2000)
    public void fixedDelay(){
        System.out.println("fixedDelay>>"+new Date());
    }
    @Scheduled(fixedRate = 2000)
    public void fixedRate() {
        System.out.println("fixedRate>>" + new Date());
    }
    //表示每五秒执行一次
    @Scheduled(cron = "0/5 * * * * ?")
    public void cron(){
        System.out.println("cron>>" + new Date());
    }
}

关于上述代码的简单叙述:

  • fixedDelay表示前一次任务结束后一次任务结束间隔2秒

  • fixedRate表示两次任务开始时间的间隔,即每两秒执行一次任务,有可能前一次任务还没运行完,下一次任务就已经要开始执行了

  • initialDelay 表示首次任务启动的延迟时间

  • 单位都是毫秒

  • cron表达式格式:

    * * * * * * *

    [秒][分][时][日][月][周][年]

序号 说明 是否必填 允许填写的值 允许的通配符
1 0~59 - * /
2 0~59 - * /
3 0~23 - * /
4 1~31 - * ? / L W
5 1-12 or JAN-DEC - * /
6 1-7 or SUN-SAT - * ? / L #
7 1970-2099 - * /

注:值得注意的是,**月份中的日期和星期可能会起冲突,因此在配置时这两个得有一个是?`**

  • ?表示不指定值,即不关心某个字段的取值使用。需要注意的是,月份中的日期和星期可能会起冲突,因此在配置时这两个得有一个是?`
  • *表示所有值,例如:在秒的字段上设置*,表示每一秒都会触发
  • ,用来区分多个值,例如在周字段上设置MON,WED,FRI表示周一,周三和周五触发
  • -表示区间,例如在秒字段上设置10-12,则表示10,11,12秒都会触发
  • /表示递增触发,如在秒字段上设置5/15表示从5秒开始,每15秒触发(5,20,35)
  • #表示每个月的第几个周几,例如在周字段上设置6#3表示在每月的第三个周六,(用在父亲节和母亲节再合适不过了)
  • 周字段的设置,若是使用英文是不区分大小写的,即MON与mon效果相同
  • L表示最后的意思。在日字段上设置,表示当月的最后一天(依据当前月份,如果是二月还会自动判断是否是闰年),在周字段上表示星期六,相等于7SAT(注意周日算是第一天)。如果在L前加上数字,则表示该数据的最后一个。例如:在周字段上设置6L这样的格式,则表示本月最后一个星期五`
  • W表示离指定日期的最近工作日(周一至周五),例如在日字段上设置15W,表示离每月15号最近的工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发,如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 “1W”,它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,”W”前只能设置具体的数字,不允许区间”-“)
  • LW 可以一组合使用。如果在日字段上设置”LW”,则表示在本月的最后一个工作日触发(一般指发工资 )

Quartz

一般在项目中,除非定时任务涉及到的业务实在是太简单,使用 @Scheduled 注解来解决定时任务,否则大部分情况可能都是使用 Quartz 来做定时任务。在 Spring Boot 中使用 Quartz ,只需要在创建项目时,添加 Quartz 依赖即可:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
  1. 在启动类加上@EnableScheduling注解表示开启定时任务
@SpringBootApplication
@EnableScheduling
public class QuartzApplication {

    public static void main(String[] args) {
        SpringApplication.run(QuartzApplication.class, args);
    }

}
  1. Quartz 在使用过程中,有两个关键概念,一个是JobDetail(要做的事情)另一个是触发器(什么时候做);要定义 JobDetail,需要先定义 Job。

    Job 的定义有两种方式:

    2.1. 第一种直接定义一个bean

    @Component
    public class MyFirstJob {
        public void firstJob(){
            System.out.println("my first job say hello:" + new Date());
        }
    }
    

    关于上述代码的两点叙述:

    ​ 2.1.1. @Component表示把这个类注册到Spring容器中去

    ​ 2.1.2. 第一种方法无法接受参数

    2.2. 第二种定义方式,则是继承QuartzJobBean并实现默认方法

    @Component
    public class MySecondJob extends QuartzJobBean {
        private String name;
        @Override
        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
            System.out.println("my second job say hello" + name + ":" + new Date());
        }
    }
    

    注:第二种方式相较于第一种方式,优点在于定时任务可以传参。其参数可以是任务类型

  2. 有了Job类(具体要做的事情)之后就需要JobDetail(要做的事情)Trigger触发器(什么时候做)

    3.1. 无参定时任务的JobDetail和Trigger触发器

    /**
         * 无参的JobDetail
         * @return
         */
    @Bean
    MethodInvokingJobDetailFactoryBean methodInvokingJobDetailFactoryBean(){
            MethodInvokingJobDetailFactoryBean bean = new 	MethodInvokingJobDetailFactoryBean();
             //定时任务的bean的名字,这是之所以可以写成“myFirstJob”是因为我们已经将它注册到Spring容器中去了,可以写成首字母小写的形式
            bean.setTargetBeanName("myFirstJob");
            //定时任务的方法
            bean.setTargetMethod("firstJob");
            return bean;
        }
    
     /**
         * 无参的Trigger触发器
         * @return
         */
        @Bean
        SimpleTriggerFactoryBean simpleTriggerFactoryBean(){
            SimpleTriggerFactoryBean bean = new SimpleTriggerFactoryBean();
            bean.setJobDetail(methodInvokingJobDetailFactoryBean().getObject());
            //开始的时间,立马开始
            bean.setStartTime(new Date());
            //2秒重复一次
            bean.setRepeatInterval(2000);
            //重复3次,如果不给参数代表一直重复下去
            bean.setRepeatCount(3);
            return bean;
        }
    
    	@Bean
        SchedulerFactoryBean schedulerFactoryBean(){
            SchedulerFactoryBean bean = new SchedulerFactoryBean();
            //将Trigger触发器传入用于执行,可以传入一个或多个参数
            bean.setTriggers(simpleTriggerFactoryBean().getObject());
            return bean;
        }
    

    3.2. 带参定时任务的JobDetail和Trigger触发器

     /**
         * 带参的JobDetail
         * @return
         */
        @Bean
        JobDetailFactoryBean jobDetailFactoryBean(){
            JobDetailFactoryBean bean = new JobDetailFactoryBean();
            HashMap<String, String> map = new HashMap<>();
            map.put("name", "fern");
            //定时任务所需的参数
            bean.setJobDataAsMap(map);
            //bean
            bean.setJobClass(MySecondJob.class);
            return bean;
        }
     /**
         * 带参的Trigger触发器
         * @return
         */
        @Bean
        CronTriggerFactoryBean cronTriggerFactoryBean(){
            CronTriggerFactoryBean bean = new CronTriggerFactoryBean();
            bean.setJobDetail(jobDetailFactoryBean().getObject());
            //每十秒触发一次
            bean.setCronExpression("0/10 * * * * ?");
            return bean;
        }
    
    	@Bean
        SchedulerFactoryBean schedulerFactoryBean(){
            SchedulerFactoryBean bean = new SchedulerFactoryBean();
            //将Trigger触发器传入用于执行,可以传入一个或多个参数
            bean.setTriggers(cronTriggerFactoryBean().getObject());
            return bean;
        }
    

关于这个配置总结如下几点:

  1. JobDetail 的配置有两种方式MethodInvokingJobDetailFactoryBeanJobDetailFactoryBean
  2. 使用MethodInvokingJobDetailFactoryBean 可以配置目标 Bean 的名字和目标方法的名字,这种方式不支持传参
  3. 使用JobDetailFactoryBean 可以配置 JobDetail,任务类继承自 QuartzJobBean ,这种方式支持传参,将参数封装在 JobDataMap 中进行传递。
  4. Trigger 是指触发器,Quartz 中定义了多个触发器,这里向大家展示其中两种的用法,SimpleTriggerCronTrigger
  5. SimpleTrigger 有点类似于前面说的 @Scheduled 的基本用法。
  6. CronTrigger 则有点类似于 @Scheduled 中 cron 表达式的用法。

效果图:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_35953966/article/details/104659729