Java SpringBoot 定时任务

定时任务实现的几种方式

  1. Timer:这是java自带的java.util.Timer类,这个类允许调度一个java.util.TimerTask任务。使用这种方式可以让程序按照某一个频度执行,但不能在指定时间运行。一般用的较少。
  2. ScheduledExecutorService:也jdk自带的一个类;是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。
  3. Spring Task:Spring3.0以后自带的task,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。
  4. Quartz:这是一个功能比较强大的的调度器,可以让程序在指定时间执行,也可以按照某一个频度执行,配置起来稍显复杂。

    1、使用Timer实现定时任务

    1. public class TestTimer {
    2. public static void main(String[] args) {
    3. TimerTask timerTask = new TimerTask() {
    4. @Override
    5. public void run() {
    6. System.out.println("task run:"+ new Date());
    7. }
    8. };
    9. Timer timer = new Timer();
    10. //安排指定的任务在指定的时间开始进行重复的固定延迟执行。这里是每3秒执行一次
    11. timer.schedule(timerTask,10,3000);
    12. }
    13. }

    2、使用ScheduledExecutorService实现定时任务

    1. public class TestScheduledExecutorService {
    2. public static void main(String[] args) {
    3. ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
    4. // 参数:1、任务体 2、首次执行的延时时间
    5. // 3、任务执行间隔 4、间隔时间单位
    6. service.scheduleAtFixedRate(()->System.out.println("task ScheduledExecutorService "+new Date()), 0, 3, TimeUnit.SECONDS);
    7. }
    8. }
    Scheduled只适合处理简单的计划任务,不能处理分布式计划任务。优势:是spring框架提供的计划任务,开发简单,执行效率比较高。且在计划任务数量太多的时候,可能出现阻塞,崩溃,延迟启动等问题。
    Scheduled定时任务是spring3.0版本之后自带的一个定时任务。其所属Spring的资源包为:spring-context-support。所以需要使用Scheduled定时任务机制时,需要在工程中依赖对应资源,具体如下:
    1. <dependency>
    2. <groupId>org.springframeworkgroupId>
    3. <artifactId>spring-context-supportartifactId>
    4. <dependency>
    如果在spring应用中需要启用Scheduled定时任务,则需要在启动类上增加注解@EnableScheduling,代表启用Scheduled定时任务机制。具体如下:
    1. @SpringBootApplication
    2. @EnableScheduling
    3. public class AppStarter {
    4. public static void main(String[] args) {
    5. SpringApplication.run(AppStarter.class, args);
    6. }
    7. }
    Scheduled定时任务的核心在于注解@Scheduled,这个注解的核心属性是cron,代表定时任务的触发计划表达式。这个表达式的格式为:
    1. @Scheduled(cron="seconds minutes hours day month week")
    1. @Scheduled(cron="seconds minutes hours day month week year")
    推荐使用第一种表达式形式,因为在很多其他技术中都有不同的定时任务机制,其中用于设置触发计划的表达式都是第一种cron表达式。第二种表达式不能说是Spring Scheduled特有的,也是只有少数技术支持的。
    cron表达式中,每个位置的约束如下:
位置 时间域名 允许值 允许的特殊字符
1 0-59 , - * /
2 分钟 0-59 , - * /
3 小时 0-23 , - * /
4 1-31 , - * / L W C
5 1-12 , - * /
6 星期 1-7 , - * / ? L C #
7 年(可选) 1980-2099 , - * /

星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如,在分钟字段时,表示“每分钟”;
问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于占位符;
减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;
逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在秒数字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用
/y,它等同于0/y;
L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五;
W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围;
LW组合:在日期字段可以组合使用LW,它的意思是当月的最后一个工作日;
井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;
C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。 :::info Cron表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。** ::: 计划任务Scheduled是通过一个线程池实现的。是一个多线程的调度。SpringBoot会初始化一个线程池,线程池默认大小为1,专门用于执行计划任务。每个计划任务启动的时候,都从线程池中获取一个线程执行,如果发生异常,只是执行当前任务的线程发生异常,而不是计划任务调度线程发生异常。如果当前定时任务还未执行完成,当相同的定时任务又进入到执行周期时,不会触发新的定时任务。如:

  1. @Scheduled(cron="* * * * * ?")
  2. public void test1(){
  3. Random r = new Random();
  4. /*int i = r.nextInt(100);
  5. if(i % 3 == 0){
  6. throw new RuntimeException("error");
  7. }*/
  8. System.out.println(Thread.currentThread().getName() + " cron=* * * * * ? --- " + new Date());
  9. try{
  10. Thread.sleep(2000);
  11. }catch(Exception e){
  12. e.printStackTrace();
  13. }
  14. }

如结果所示(每次的线程名称一致,由于前一个定时任务未执行完成,因此造成后一个任务的推迟,而不是1秒执行一次,而是3秒):

  1. pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:20 CST 2019
  2. pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:23 CST 2019
  3. pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:26 CST 2019
  4. pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:29 CST 2019

3、使用Spring Task实现定时任务

简单的定时任务

在SpringBoot项目中,可以很优雅的使用注解来实现定时任务,首先创建项目,导入依赖:

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.projectlombok</groupId>
  12. <artifactId>lombok</artifactId>
  13. <optional>true</optional>
  14. </dependency>
  15. <dependency>
  16. <groupId>org.springframework.boot</groupId>
  17. <artifactId>spring-boot-starter-test</artifactId>
  18. <scope>test</scope>
  19. </dependency>
  20. </dependencies>

创建任务类

  1. @Slf4j
  2. @Component
  3. public class ScheduledService {
  4. @Scheduled(cron = "0/5 * * * * *")
  5. public void scheduled(){
  6. log.info("=====>>>>>使用cron {}",System.currentTimeMillis());
  7. }
  8. @Scheduled(fixedRate = 5000)
  9. public void scheduled1() {
  10. log.info("=====>>>>>使用fixedRate{}", System.currentTimeMillis());
  11. }
  12. @Scheduled(fixedDelay = 5000)
  13. public void scheduled2() {
  14. log.info("=====>>>>>fixedDelay{}",System.currentTimeMillis());
  15. }
  16. }

在主类上使用@EnableScheduling注解开启对定时任务的支持,然后启动项目
可以看到三个定时任务都已经执行,并且使同一个线程中串行执行,如果只有一个定时任务,这样做肯定没问题,当定时任务增多,如果一个任务卡死,会导致其他任务也无法执行。

多线程执行

在传统的Spring项目中,可以在xml配置文件添加task的配置,而在SpringBoot项目中一般使用config配置类的方式添加配置,所以新建一个AsyncConfig类

  1. @Configuration
  2. @EnableAsync
  3. public class AsyncConfig {
  4. /*
  5. 此处成员变量应该使用@Value从配置中读取
  6. */
  7. private int corePoolSize = 10;
  8. private int maxPoolSize = 200;
  9. private int queueCapacity = 10;
  10. @Bean
  11. public Executor taskExecutor() {
  12. ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  13. executor.setCorePoolSize(corePoolSize);
  14. executor.setMaxPoolSize(maxPoolSize);
  15. executor.setQueueCapacity(queueCapacity);
  16. executor.initialize();
  17. return executor;
  18. }
  19. }

@Configuration:表明该类是一个配置类@EnableAsync:开启异步事件的支持 然后在定时任务的类或者方法上添加@Async 。最后重启项目,每一个任务都是在不同的线程中

执行时间的配置

在上面的定时任务中,在方法上使用@Scheduled注解来设置任务的执行时间,并且使用三种属性配置方式:
fixedRate:定义一个按一定频率执行的定时任务
fixedDelay:定义一个按一定频率执行的定时任务,与上面不同的是,改属性可以配合initialDelay, 定义该任务延迟执行时间。
cron:通过表达式来配置任务执行时间

4、整合Quartz实现定时任务

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。
Quartz是一个完全由java编写的开源作业调度框架。尽管Quartz框架整合了许多额外功能, 但就其简易形式看,可以发现它十分易用!
在开发Quartz相关应用时,只要定义了Job(任务),Trigger(触发器)和Scheduler(调度器),即可实现一个定时调度能力。其中Scheduler是Quartz中的核心,Scheduler负责管理Quartz应用运行时环境,Scheduler不是靠自己完成所有的工作,是根据Trigger的触发标准,调用Job中的任务执行逻辑,来完成完整的定时任务调度。
Job - 定时任务内容是什么。
Trigger - 在什么时间上执行job。
Scheduler - 维护定时任务环境,并让触发器生效。

添加依赖

如果SpringBoot版本是2.0.0以后的,则在spring-boot-starter中已经包含了quart的依赖,则可以直接使用spring-boot-starter-quartz依赖

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

如果是1.5.9则要使用以下添加依赖:

  1. <dependency>
  2. <groupId>org.quartz-scheduler</groupId>
  3. <artifactId>quartz</artifactId>
  4. <version>2.3.0</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework</groupId>
  8. <artifactId>spring-context-support</artifactId>
  9. </dependency>

这里使用SpringBoot版本是2.0.0.BUILD-SNAPSHOT ,该版本开始集成了Quartz,所以事实现起来很方便。创建任务类TestQuartz,该类主要是继承了QuartzJobBean

  1. public class TestQuartz extends QuartzJobBean {
  2. /**
  3. * 执行定时任务
  4. * @param jobExecutionContext
  5. * @throws JobExecutionException
  6. */
  7. @Override
  8. protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
  9. System.out.println("quartz task "+new Date());
  10. }
  11. }

创建配置类QuartzConfig

  1. @Configuration
  2. public class QuartzConfig {
  3. @Bean
  4. public JobDetail teatQuartzDetail(){
  5. return JobBuilder.newJob(TestQuartz.class).withIdentity("testQuartz").storeDurably().build();
  6. }
  7. @Bean
  8. public Trigger testQuartzTrigger(){
  9. SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
  10. .withIntervalInSeconds(10) //设置时间周期单位秒
  11. .repeatForever();
  12. return TriggerBuilder.newTrigger().forJob(teatQuartzDetail())
  13. .withIdentity("testQuartz")
  14. .withSchedule(scheduleBuilder)
  15. .build();
  16. }
  17. }

启动项目即可看到任务的执行日志

Job任务调用业务需要重写JobFactory的实现

启动器添加注解@EnableScheduling

  1. /**
  2. * @EnableScheduling 必要
  3. * 开启定时任务机制。
  4. */
  5. @SpringBootApplication
  6. @EnableScheduling
  7. public class AppStarter {
  8. public static void main(String[] args) {
  9. SpringApplication.run(AppStarter.class, args);
  10. }
  11. }

定义JOB任务以及JOB任务调用的模拟业务对象:

  1. public class SpringBootQuartzJobDemo implements Job {
  2. // 用于模拟任务中的业务对象。也可能是数据访问对象,或其他类型的对象。
  3. @Autowired
  4. private CommonsUtil4Quartz commonsUtil4Quartz;
  5. @Override
  6. public void execute(JobExecutionContext context) throws JobExecutionException {
  7. System.out.println("SpringBootQuartzJobDemo : " + new Date());
  8. this.commonsUtil4Quartz.testMethod();
  9. }
  10. }
  11. @Component
  12. public class CommonsUtil4Quartz {
  13. public void testMethod(){
  14. System.out.println("CommonsUtil4Quartz testMethod run...");
  15. }
  16. }

创建Trigger以及JobDetail对象,并用Schedule配置定时任务

  1. /**
  2. * 初始化类
  3. * Quartz环境初始化。
  4. *
  5. */
  6. @Configuration
  7. public class QuartzConfiguration {
  8. /**
  9. * 创建Job对象。在Spring环境中,创建一个类型的对象的时候,很多情况下,都是通过FactoryBean来间接创建的。
  10. * 如果有多个Job对象,定义多次方法。
  11. *
  12. * 在JobDetailFactoryBean类型中,用于创建JobDetail对象的方法,其底层使用的逻辑是:Class.newInstance()
  13. * 也就是说,JobDetail对象不是通过Spring容器管理的。
  14. * 因为Spring容器不管理JobDetail对象,那么Job中需要自动装配的属性,就无法实现自动状态。如上JOB的第10行会报空指针异常。
  15. *
  16. * 解决方案是:将JobDetail加入到Spring容器中,让Spring容器管理JobDetail对象。
  17. * 需要重写Factory相关代码。实现Spring容器管理JobDetail。
  18. * @return
  19. */
  20. @Bean
  21. public JobDetailFactoryBean initJobDetailFactoryBean(){
  22. JobDetailFactoryBean factoryBean =
  23. new JobDetailFactoryBean();
  24. // 提供job类型。
  25. factoryBean.setJobClass(SpringBootQuartzJobDemo.class);
  26. return factoryBean;
  27. }
  28. /**
  29. * 管理Trigger对象
  30. * CronTrigger - 就是Trigger的一个实现类型。其中用于定义周期时间的是CronSchedulerBuilder
  31. * 实际上,CronTrigger是用于管理一个Cron表达式的类型。
  32. * @param jobDetailFactoryBean - 上一个方法初始化的JobDetailFactoryBean
  33. * @return
  34. */
  35. @Bean(name="cronTriggerFactoryBean1")
  36. public CronTriggerFactoryBean initCronTriggerFactoryBean(
  37. ){
  38. CronTriggerFactoryBean factoryBean =
  39. new CronTriggerFactoryBean();
  40. JobDetailFactoryBean jobDetailFactoryBean = this.initJobDetailFactoryBean();
  41. factoryBean.setJobDetail(jobDetailFactoryBean.getObject());
  42. factoryBean.setCronExpression("0/3 * * * * ?");
  43. return factoryBean;
  44. }
  45. /**
  46. * 初始化Scheduler
  47. * @param cronTriggerFactoryBean - 上一个方法初始化的CronTriggerFactoryBean
  48. * @return
  49. */
  50. @Bean
  51. public SchedulerFactoryBean initSchedulerFactoryBean(
  52. CustomJobFactory customJobFactory,
  53. CronTriggerFactoryBean[] cronTriggerFactoryBean){
  54. SchedulerFactoryBean factoryBean =
  55. new SchedulerFactoryBean();
  56. CronTrigger[] triggers = new CronTrigger[cronTriggerFactoryBean.length];
  57. for(int i = 0; i < cronTriggerFactoryBean.length; i++){
  58. triggers[i] = cronTriggerFactoryBean[i].getObject();
  59. }
  60. // 注册触发器,一个Scheduler可以注册若干触发器。
  61. factoryBean.setTriggers(triggers);
  62. // 为Scheduler设置JobDetail的工厂。可以覆盖掉SpringBoot提供的默认工厂,保证JobDetail中的自动装配有效。
  63. factoryBean.setJobFactory(customJobFactory);
  64. return factoryBean;
  65. }
  66. }

重写JobFactory

  1. /**
  2. * 重写的工厂对象。
  3. */
  4. @Component
  5. public class CustomJobFactory extends AdaptableJobFactory {
  6. /**
  7. * AutowireCapableBeanFactory : 简单理解为Spring容器,是Spring容器Context的一个Bean对象管理工程。
  8. * 可以实现自动装配逻辑,和对象创建逻辑。
  9. * 是SpringIoC容器的一个重要组成部件。
  10. */
  11. @Autowired
  12. private AutowireCapableBeanFactory autowireCapableBeanFactory;
  13. @Override
  14. protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
  15. // 通过父类型中的方法,创建JobDetail对象。
  16. Object obj = super.createJobInstance(bundle);
  17. // 将JobDetail对象加入到Spring容器中,让Spring容器管理,并实现自动装配逻辑。
  18. this.autowireCapableBeanFactory.autowireBean(obj);
  19. return obj;
  20. }
  21. }

分布式quartz配置**

1、资源依赖配置

由于分布式的原因,Quartz中提供分布式处理的jar包以及数据库及连接相关的依赖。

  1. <dependency>
  2. <groupId>org.springframework<groupId>
  3. <artifactId>spring-context-support<artifactId>
  4. <dependency>
  5. <dependency>
  6. <groupId>org.quartz-scheduler<groupId>
  7. <artifactId>quartz<artifactId>
  8. <version>2.2.1<version>
  9. <exclusions>
  10. <exclusion>
  11. <artifactId>slf4j-api<artifactId>
  12. <groupId>org.slf4j<groupId>
  13. <exclusion>
  14. <exclusions>
  15. <dependency>
  16. <dependency>
  17. <groupId>org.quartz-scheduler<groupId>
  18. <artifactId>quartz-jobs<artifactId>
  19. <version>2.2.1<version>
  20. <dependency>
  21. <dependency>
  22. <groupId>org.springframework<groupId>
  23. <artifactId>spring-tx<artifactId>
  24. <dependency>
  25. <dependency>
  26. <groupId>org.springframework.boot<groupId>
  27. <artifactId>spring-boot-starter-web<artifactId>
  28. <dependency>
  29. <dependency>
  30. <groupId>org.springframework.boot<groupId>
  31. <artifactId>spring-boot-starter-data-jpa<artifactId>
  32. <dependency>
  33. <dependency>
  34. <groupId>mysql<groupId>
  35. <artifactId>mysql-connector-java<artifactId>
  36. <dependency>
  37. <dependency>
  38. <groupId>com.alibaba<groupId>
  39. <artifactId>druid<artifactId>
  40. <version>1.0.9<version>
  41. <dependency>

2、数据库相关配置

  1. spring.datasource.url=jdbc:mysql://localhost:3306/quartz?autoReconnect=true&useUnicode=true&characterEncoding=utf-8
  2. spring.datasource.username=root
  3. spring.datasource.password=root
  4. spring.datasource.driver-class-name=com.mysql.jdbc.Driver
  5. spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
  6. spring.datasource.initialSize=5
  7. spring.datasource.minIdle=5
  8. spring.datasource.maxActive=20
  9. spring.datasource.maxWait=600000
  10. spring.datasource.timeBetweenEvictionRunsMillis=600000
  11. spring.datasource.minEvictableIdleTimeMillis=300000
  12. spring.datasource.validationQuery=SELECT 1
  13. spring.datasource.testWhileIdle=true
  14. spring.datasource.testOnBorrow=false
  15. spring.datasource.testOnReturn=false
  16. server.port=8080

3、Quartz配置信息

为Quartz提供数据库配置信息,如数据库,表格的前缀之类的。

  1. # 是否使用properties作为数据存储
  2. org.quartz.jobStore.useProperties=false
  3. # 数据库中的表格命名前缀
  4. org.quartz.jobStore.tablePrefix = QRTZ_
  5. # 是否是一个集群,是不是分布式的任务
  6. org.quartz.jobStore.isClustered = true
  7. # 集群检查周期,单位毫秒。可以自定义缩短时间。当某一个节点宕机的时候,其他节点等待多久后开始执行任务。
  8. org.quartz.jobStore.clusterCheckinInterval = 5000
  9. # 单位毫秒, 集群中的节点退出后,再次检查进入的时间间隔。
  10. org.quartz.jobStore.misfireThreshold = 60000
  11. # 事务隔离级别
  12. org.quartz.jobStore.txIsolationLevelReadCommitted = true
  13. # 存储的事务管理类型
  14. org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
  15. # 使用的Delegate类型
  16. org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  17. # 集群的命名,一个集群要有相同的命名。
  18. org.quartz.scheduler.instanceName = ClusterQuartz
  19. # 节点的命名,可以自定义。AUTO代表自动生成。
  20. org.quartz.scheduler.instanceId= AUTO
  21. # rmi远程协议是否发布
  22. org.quartz.scheduler.rmi.export = false
  23. # rmi远程协议代理是否创建
  24. org.quartz.scheduler.rmi.proxy = false
  25. # 是否使用用户控制的事务环境触发执行job。
  26. org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

4、初始化数据库

建表语句可以在官方网站中查找(Quartz-lib中),使用tables-mysql.sql建表。

5、定义JOB类

启动器和普通quartz无差异,但是JOB自身定义有些许差异:

  1. /**
  2. * 使用Spring提供的Quartz相关Job类型实现Job的定义。
  3. * 父类型QuartzJobBean中,提供了分布式环境中任务的配置定义。
  4. * 保证分布式环境中的任务是有效的。
  5. */
  6. @PersistJobDataAfterExecution // 当job执行结束,持久化job信息到数据库
  7. @DisallowConcurrentExecution // 保证job的唯一性(单例)
  8. public class SpringBootQuartzJobDemo extends QuartzJobBean {
  9. @Override
  10. protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
  11. System.out.println("SpringBootQuartzJobDemo : " + new Date());
  12. }
  13. }

6、QuartzConfiguration类型定义

  1. @Configuration
  2. public class QuartzConfiguration {
  3. @Autowired
  4. private DataSource dataSource;
  5. /**
  6. * 创建调度器, 可以省略的。
  7. * @return
  8. * @throws Exception
  9. */
  10. @Bean
  11. public Scheduler scheduler() throws Exception {
  12. Scheduler scheduler = schedulerFactoryBean().getScheduler();
  13. scheduler.start();
  14. return scheduler;
  15. }
  16. /**
  17. * 创建调度器工厂bean对象。
  18. * @return
  19. * @throws IOException
  20. */
  21. @Bean
  22. public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
  23. SchedulerFactoryBean factory = new SchedulerFactoryBean();
  24. factory.setSchedulerName("Cluster_Scheduler");
  25. factory.setDataSource(dataSource);
  26. factory.setApplicationContextSchedulerContextKey("applicationContext");
  27. // 设置调度器中的线程池。
  28. factory.setTaskExecutor(schedulerThreadPool());
  29. // 设置触发器
  30. factory.setTriggers(trigger().getObject());
  31. // 设置quartz的配置信息
  32. factory.setQuartzProperties(quartzProperties());
  33. return factory;
  34. }
  35. /**
  36. * 读取quartz.properties配置文件的方法。
  37. * @return
  38. * @throws IOException
  39. */
  40. @Bean
  41. public Properties quartzProperties() throws IOException {
  42. PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
  43. propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
  44. // 在quartz.properties中的属性被读取并注入后再初始化对象
  45. propertiesFactoryBean.afterPropertiesSet();
  46. return propertiesFactoryBean.getObject();
  47. }
  48. /**
  49. * 创建Job对象的方法。
  50. * @return
  51. */
  52. @Bean
  53. public JobDetailFactoryBean job() {
  54. JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
  55. jobDetailFactoryBean.setJobClass(SpringBootQuartzJobDemo.class);
  56. // 是否持久化job内容
  57. jobDetailFactoryBean.setDurability(true);
  58. // 设置是否多次请求尝试任务。
  59. jobDetailFactoryBean.setRequestsRecovery(true);
  60. return jobDetailFactoryBean;
  61. }
  62. /**
  63. * 创建trigger factory bean对象。
  64. * @return
  65. */
  66. @Bean
  67. public CronTriggerFactoryBean trigger() {
  68. CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
  69. cronTriggerFactoryBean.setJobDetail(job().getObject());
  70. cronTriggerFactoryBean.setCronExpression("0/2 * * * * ?");
  71. return cronTriggerFactoryBean;
  72. }
  73. /**
  74. * 创建一个调度器的线程池。
  75. * @return
  76. */
  77. @Bean
  78. public Executor schedulerThreadPool() {
  79. ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  80. executor.setCorePoolSize(15);
  81. executor.setMaxPoolSize(25);
  82. executor.setQueueCapacity(100);
  83. return executor;
  84. }
  85. }

若JOB任务有定义调用业务等内容,也需要重写JobFactory。

cron表达式详解

一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。按顺序依次为:
秒(0~59)
分钟(0~59)
小时(0~23)
天(0~31)
月(0~11)
星期(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT)
年份(1970-2099)
其中每个元素可以是一个值(如6),一个连续区间(9-12),一个间隔时间(8-18/4)(/表示每隔4小时),一个列表(1,3,5),通配符。由于”月份中的日期”和”星期中的日期”这两个元素互斥的,必须要对其中一个设置。配置实例:
/5 * ?:每隔5秒执行一次
0 /1 ?:每隔1分钟执行一次
0 0 10,14,16 ?:每天上午10点,下午2点,4点
0 0/30 9-17 ?:朝九晚五工作时间内每半小时
0 0 12 ? * WED:表示每个星期三中午12点
0 0 12 ?:每天中午12点触发
0 15 10 ?:每天上午10:15触发
0 15 10 ?:每天上午10:15触发
0 15 10 ? *:每天上午10:15触发
0 15 10 ? 2005:2005年的每天上午10:15触发
0 14 * ?:在每天下午2点到下午2:59期间的每1分钟触发
0 0/5 14 ?:在每天下午2点到下午2:55期间的每5分钟触发
0 0/5 14,18 ?:在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
0 0-5 14 ?:在每天下午2点到下午2:05期间的每1分钟触发
0 10,44 14 ? 3 WED:每年三月的星期三的下午2:10和2:44触发
0 15 10 ? * MON-FRI:周一至周五的上午10:15触发
0 15 10 15 * ?:每月15日上午10:15触发
0 15 10 L * ?:每月最后一日的上午10:15触发
0 15 10 ? * 6L:每月的最后一个星期五上午10:15触发
0 15 10 ? * 6L 2002-2005:2002年至2005年的每月的最后一个星期五上午10:15触发
0 15 10 ? * 6#3:每月的第三个星期五上午10:15触发
有些子表达式能包含一些范围或列表,

例如:子表达式(天(星期))可以为 “MON-FRI”,“MON,WED,FRI”,“MON-WED,SAT”

“*”字符代表所有可能的值
“/“字符用来指定数值的增量

例如:在子表达式(分钟)里的“0/15”表示从第0分钟开始,每15分钟 在子表达式(分钟)里的“3/20”表示从第3分钟开始,每20分钟(它和“3,23,43”)的含义一样

“?”字符仅被用于天(月)和天(星期)两个子表达式,表示不指定值
当2个子表达式其中之一被指定了值以后,为了避免冲突,需要将另一个子表达式的值设为”?”
“L”字符仅被用于天(月)和天(星期)两个子表达式,它是单词”last”的缩写
如果在”L”前有具体的内容,它就具有其他的含义了。

例如:“6L”表示这个月的倒数第6天 注意:在使用“L”参数时,不要指定列表或范围,因为这会导致问题

W 字符代表着平日(Mon-Fri),并且仅能用于日域中。它用来指定离指定日的最近的一个平日。大部分的商业处理都是基于工作周的,所以 W 字符可能是非常重要的。

例如,日域中的 15W 意味着 “离该月15号的最近一个平日。” 假如15号是星期六,那么 trigger 会在14号(星期五)触发,因为星期四比星期一离15号更近。

C:代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。

例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。

字段 允许值 允许的特殊字符
0~59 , - * /
0~59 , - * /
小时 0~23 , - * /
日期 1-31 , - * ? / L W C
月份 1~12或者JAN~DEC , - * /
星期 1~7或者SUN~SAT , - * ? / L C #
年(可选) 留空,1970~2099 , - * /

在线cron表达式生成:http://qqe2.com/cron/index :::tips Cron表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。 :::