Quartz(6-SpringBoot整合) | 总字数: 3.5k | 阅读时长: 15分钟 | 浏览量: | 
Quartz 自动装配机制 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @ConfigurationProperties("spring.quartz") public  class  QuartzProperties  {    private  JobStoreType  jobStoreType  =  JobStoreType.MEMORY;     private  String schedulerName;     private  boolean  autoStartup  =  true ;     private  Duration  startupDelay  =  Duration.ofSeconds(0 );     private  boolean  waitForJobsToCompleteOnShutdown  =  false ;     private  boolean  overwriteExistingJobs  =  false ;     private  final  Map<String, String> properties = new  HashMap <>();     private  final  Jdbc  jdbc  =  new  Jdbc ();     public  static  class  Jdbc  {         private  static  final  String  DEFAULT_SCHEMA_LOCATION  =  "classpath:org/quartz/impl/"                  + "jdbcjobstore/tables_@@platform@@.sql" ;         private  String  schema  =  DEFAULT_SCHEMA_LOCATION;         private  DataSourceInitializationMode  initializeSchema  =  DataSourceInitializationMode.EMBEDDED;         private  List<String> commentPrefix = new  ArrayList <>(Arrays.asList("#" , "--" ));     } } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Scheduler.class, SchedulerFactoryBean.class, PlatformTransactionManager.class }) @EnableConfigurationProperties(QuartzProperties.class) @AutoConfigureAfter({ DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, 		LiquibaseAutoConfiguration.class, FlywayAutoConfiguration.class }) public  class  QuartzAutoConfiguration  {    @Bean      @ConditionalOnMissingBean      public  SchedulerFactoryBean quartzScheduler (QuartzProperties properties,              ObjectProvider<SchedulerFactoryBeanCustomizer> customizers, ObjectProvider<JobDetail> jobDetails,             Map<String, Calendar> calendars, ObjectProvider<Trigger> triggers, ApplicationContext applicationContext)  {        SchedulerFactoryBean  schedulerFactoryBean  =  new  SchedulerFactoryBean ();         SpringBeanJobFactory  jobFactory  =  new  SpringBeanJobFactory ();         jobFactory.setApplicationContext(applicationContext);         schedulerFactoryBean.setJobFactory(jobFactory);         if  (properties.getSchedulerName() != null ) {             schedulerFactoryBean.setSchedulerName(properties.getSchedulerName());         }         schedulerFactoryBean.setAutoStartup(properties.isAutoStartup());         schedulerFactoryBean.setStartupDelay((int ) properties.getStartupDelay().getSeconds());         schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(properties.isWaitForJobsToCompleteOnShutdown());         schedulerFactoryBean.setOverwriteExistingJobs(properties.isOverwriteExistingJobs());         if  (!properties.getProperties().isEmpty()) {             schedulerFactoryBean.setQuartzProperties(asProperties(properties.getProperties()));         }         schedulerFactoryBean.setJobDetails(jobDetails.orderedStream().toArray(JobDetail[]::new ));         schedulerFactoryBean.setCalendars(calendars);         schedulerFactoryBean.setTriggers(triggers.orderedStream().toArray(Trigger[]::new ));         customizers.orderedStream().forEach((customizer) -> customizer.customize(schedulerFactoryBean));         return  schedulerFactoryBean;     }     private  Properties asProperties (Map<String, String> source)  {         Properties  properties  =  new  Properties ();         properties.putAll(source);         return  properties;     }     @Configuration(proxyBeanMethods = false)      @ConditionalOnSingleCandidate(DataSource.class)      @ConditionalOnProperty(prefix = "spring.quartz", name = "job-store-type", havingValue = "jdbc")      protected  static  class  JdbcStoreTypeConfiguration  {         @Bean          @Order(0)          public  SchedulerFactoryBeanCustomizer dataSourceCustomizer (QuartzProperties properties, DataSource dataSource,                  @QuartzDataSource  ObjectProvider<DataSource> quartzDataSource,                 ObjectProvider<PlatformTransactionManager> transactionManager)  {            return  (schedulerFactoryBean) -> {                 DataSource  dataSourceToUse  =  getDataSource(dataSource, quartzDataSource);                 schedulerFactoryBean.setDataSource(dataSourceToUse);                 PlatformTransactionManager  txManager  =  transactionManager.getIfUnique();                 if  (txManager != null ) {                     schedulerFactoryBean.setTransactionManager(txManager);                 }             };         }         private  DataSource getDataSource (DataSource dataSource, ObjectProvider<DataSource> quartzDataSource)  {             DataSource  dataSourceIfAvailable  =  quartzDataSource.getIfAvailable();             return  (dataSourceIfAvailable != null ) ? dataSourceIfAvailable : dataSource;         }         @Bean          @ConditionalOnMissingBean          public  QuartzDataSourceInitializer quartzDataSourceInitializer (DataSource dataSource,                  @QuartzDataSource  ObjectProvider<DataSource> quartzDataSource, ResourceLoader resourceLoader,                 QuartzProperties properties)  {            DataSource  dataSourceToUse  =  getDataSource(dataSource, quartzDataSource);             return  new  QuartzDataSourceInitializer (dataSourceToUse, resourceLoader, properties);         }                  @Configuration(proxyBeanMethods = false)          static  class  QuartzSchedulerDependencyConfiguration  {             @Bean              static  SchedulerDependsOnBeanFactoryPostProcessor quartzSchedulerDataSourceInitializerDependsOnBeanFactoryPostProcessor ()  {                 return  new  SchedulerDependsOnBeanFactoryPostProcessor (QuartzDataSourceInitializer.class);             }             @Bean              @ConditionalOnBean(FlywayMigrationInitializer.class)              static  SchedulerDependsOnBeanFactoryPostProcessor quartzSchedulerFlywayDependsOnBeanFactoryPostProcessor ()  {                 return  new  SchedulerDependsOnBeanFactoryPostProcessor (FlywayMigrationInitializer.class);             }             @Configuration(proxyBeanMethods = false)              @ConditionalOnClass(SpringLiquibase.class)              static  class  LiquibaseQuartzSchedulerDependencyConfiguration  {                 @Bean                  @ConditionalOnBean(SpringLiquibase.class)                  static  SchedulerDependsOnBeanFactoryPostProcessor quartzSchedulerLiquibaseDependsOnBeanFactoryPostProcessor ()  {                     return  new  SchedulerDependsOnBeanFactoryPostProcessor (SpringLiquibase.class);                 }             }         }     }          private  static  class  SchedulerDependsOnBeanFactoryPostProcessor  extends  AbstractDependsOnBeanFactoryPostProcessor  {         SchedulerDependsOnBeanFactoryPostProcessor(Class<?>... dependencyTypes) {             super (Scheduler.class, SchedulerFactoryBean.class, dependencyTypes);         }     } } 
 
SpringBoot 整合 Quartz 后,通过 spring.quartz 前缀可以设置其对应的属性值,实例化 SchedulerFactoryBean 后,调用其 afterPropertiesSet()方法去初始化 Quartz 配置信息(详细查看 SchedulerFactoryBean 的 initialize()方法及 Quartz(3-Scheduler)文章 )
单机版配置 
依赖 
1 2 3 4 5 6 7 8 9 10 <dependency >     <groupId > org.springframework.boot</groupId >      <artifactId > spring-boot-starter-web</artifactId >      <version > 2.3.12.RELEASE</version >  </dependency > <dependency >     <groupId > org.springframework.boot</groupId >      <artifactId > spring-boot-starter-quartz</artifactId >      <version > 2.3.12.RELEASE</version >  </dependency > 
 
任务创建 
1 2 3 4 5 6 7 8 9 10 11 12 public  class  TestJob  extends  QuartzJobBean  {    private  static  final  Log  logger  =  LogFactory.getLog(TestJob.class);     @Override      protected  void  executeInternal (JobExecutionContext context)  throws  JobExecutionException {         logger.info("测试======" );     } } 
 
可以实现 Job 接口,也可以继承 QuartzJobBean 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Configuration public  class  QuartzConfig  {    @Bean      public  JobDetail jobDetail ()  {                  return  JobBuilder.newJob(TestJob.class)                                  .withIdentity("testJob" )                                  .withDescription("testJob" )                                  .storeDurably()                 .build();     }     @Bean      public  Trigger trigger ()  {                  return  TriggerBuilder.newTrigger()                                  .forJob(jobDetail())                                  .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5 ))                 .build();     } } 
 
项目初始化手动创建任务 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Component public  class  JobInit  implements  ApplicationRunner  {    private  static  final  String  ID  =  "SUMMERDAY" ;     @Autowired      private  Scheduler scheduler;     @Override      public  void  run (ApplicationArguments args)  throws  Exception {         JobDetail  jobDetail  =  JobBuilder.newJob(TestJob.class)                 .withIdentity(ID + " 01" )                 .storeDurably()                 .build();         CronScheduleBuilder  scheduleBuilder  =                  CronScheduleBuilder.cronSchedule("0/5 * * * * ? *" );                  Trigger  trigger  =  TriggerBuilder.newTrigger()                 .forJob(jobDetail)                 .withIdentity(ID + " 01Trigger" )                 .withSchedule(scheduleBuilder)                 .startNow()                  .build();                  scheduler.scheduleJob(jobDetail, trigger);     } } 
 
yaml 配置 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 spring:      quartz:      job-store-type:  memory       auto-startup:  true       startup-delay:  0       wait-for-jobs-to-complete-on-shutdown:  true       overwrite-existing-jobs:  false       properties:         org:          quartz:            threadPool:              threadCount:  25               threadPriority:  5               class:  org.quartz.simpl.SimpleThreadPool   
 
持久化配置 
集群部署 
一个 Quartz 集群中的每个节点是一个独立的 Quartz 应用,它又管理着其他的节点。这就意味着你必须对每个节点分别启动或停止。Quartz 集群中,独立的 Quartz 节点并不与另一其的节点或是管理节点通信,而是通过同一个数据库表来感知到另一 Quartz 应用的。因为 Quartz 集群依赖于数据库,所以必须首先创建 Quartz 数据库表,Quartz 发布包中包括了所有被支持的数据库平台的 SQL 脚本。
创建数据库表 
针对不同的数据库,org.quartz.impl.jdbcjobstore 包下提供了不同建表脚本,数据库脚本放在 org\quartz-scheduler\quartz\2.3.2\quartz-2.3.2.jar! 包中的\org\quartz\impl\jdbcjobstore 路径下
表名 
描述 
 
 
qrtz_fired_triggers 
存储已触发的 trigger 相关信息 
 
qrtz_paused_trigger_grps 
存储已暂停的 trigger 组信息 
 
qrtz_scheduler_state 
存储 Scheduler 状态信息 
 
qrtz_locks 
存储悲观锁的信息 
 
qrtz_simple_triggers 
存储 Simple trigger 信息 
 
qrtz_simprop_triggers 
存储其他几种 trigger 信息 
 
qrtz_cron_triggers 
存储 cron trigger 信息 
 
qrtz_blob_triggers 
blog 类型存储 triggers 
 
qrtz_triggers 
存储已配置的 trigger 信息 
 
qrtz_job_details 
存储每一个已配置的 job details 
 
qrtz_calendars 
以 blog 类型存储 Calendar 信息 
 
 
所有的表中都含有一个 SCHED_NAME 字段,对应我们配置的 scheduler-name,相同 Scheduler-name 的节点,形成一个 Quartz 集群
存储类型 
Quartz 提供两种基本作业存储类型:RAMJobStore 和 JDBC 作业存储。在默认情况下 Quartz 将任务调度的运行信息保存在内存中,这种方法提供了最佳的性能,因为内存中数据访问最快。不足之处是缺乏数据的持久性,当程序路途停止或系统崩溃时,所有运行的信息都会丢失。
类型 
优点 
缺点 
 
 
RAMJobStore 
不要外部数据库,配置容易,运行速度快 
因为调度程序信息是存储在被分配给 JVM 的内存里面,所以,当应用程序停止运行时,所有调度信息将被丢失。另外因为存储到 JVM 内存里面,所以可以存储多少个 Job 和 Trigger 将会受到限制 
 
JDBC 作业存储 
支持集群,因为所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应用服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启而导致执行失败的任务 
运行速度的快慢取决与连接数据库的快慢 
 
 
事务管理类型 
类型 
描述 
 
 
JobStoreTX 
如果不需要将调度命令(例如添加和删除 triggers)绑定到其他事务,那么可以通过使用 JobStoreTX 管理事务(这是最常见的选择) 
 
JobStoreCMT 
如果需要 Quartz 与其他事务(即 J2EE 应用程序服务器)一起工作,那么应该使用 JobStoreCMT,这种情况下,Quartz 将让应用程序服务器容器管理事务 
 
 
1 2 3 org.quartz.jobStore.class =org.quartz.impl.jdbcjobstore.JobStoreCMT 
 
数据库驱动代理 
针对不同的数据库制作了不同的数据库的代理,其中使用最多的是 StdJDBCDelegate ,它是一个使用 JDBC 代码(和 SQL 语句)来执行其工作的委托。其他驱动代理可以在 “org.quartz.impl.jdbcjobstore” 包或其子包中找到。如 DB2v6Delegate(用于 DB2 版本 6 及更早版本),HSQLDBDelegate(HSQLDB),MSSQLDelegate(SQLServer),PostgreSQLDelegate(PostgreSQL),WeblogicDelegate(用于使用 Weblogic 创建的 JDBC 驱动程序)
1 org.quartz.jobStore.driverDelegateClass  = org.quartz.impl.jdbcjobstore.StdJDBCDelegate 
 
配置信息 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <dependency >     <groupId > org.springframework.boot</groupId >      <artifactId > spring-boot-starter-quartz</artifactId >      <version > 2.3.12.RELEASE</version >  </dependency > <dependency >     <groupId > org.springframework.boot</groupId >      <artifactId > spring-boot-starter-web</artifactId >      <version > 2.3.12.RELEASE</version >  </dependency > <dependency >     <groupId > com.baomidou</groupId >      <artifactId > mybatis-plus-boot-starter</artifactId >      <version > 3.3.2</version >  </dependency > <dependency >     <groupId > mysql</groupId >      <artifactId > mysql-connector-java</artifactId >      <version > 8.0.19</version >  </dependency > 
 
yaml 配置(使用其自动装配机制) 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 spring:   datasource:      driver-class-name:  com.mysql.cj.jdbc.Driver      url:  jdbc:mysql://localhost:3306/quartz?serverTimezone=GMT%2B8      username:  root      password:  123456    quartz:           job-store-type:  jdbc           scheduler-name:  hyhScheduler           wait-for-jobs-to-complete-on-shutdown:  true      jdbc:               initialize-schema:  never                    properties:        org:          quartz:                       startup-delay:  60                       overwrite-existing-jobs:  false            scheduler:                           instanceId:  AUTO              instanceName:  hyhScheduler                       jobStore:                                                                  class:  org.quartz.impl.jdbcjobstore.JobStoreTX                           driverDelegateClass:  org.quartz.impl.jdbcjobstore.StdJDBCDelegate                           tablePrefix:  QRTZ_                           isClustered:  true                           clusterCheckinInterval:  1000                           useProperties:  false                           misfireThreshold:  60000                       threadPool:                           threadCount:  25                           threadPriority:  5                           class:  org.quartz.simpl.SimpleThreadPool  
 
注意:同一集群下,instanceName 必须相同,instanceId 可自动生成,isClustered 为 true,持久化存储,指定数据库类型对应的驱动类和数据源连接。
自定义 properties 和配置类(使用 Quartz 的 initialize()方法去查找对应的 quartz.properties 文件) 
 
quartz.properties 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 org.quartz.scheduler.instanceName  = hyhScheduler org.quartz.scheduler.instanceId  = AUTO org.quartz.jobStore.class =org.springframework.scheduling.quartz.JobStoreTX org.quartz.jobStore.tablePrefix  = QRTZ_ org.quartz.jobStore.useProperties  = true org.quartz.jobStore.isClustered  = true org.quartz.threadPool.class  = org.quartz.simpl.SimpleThreadPool org.quartz.jobStore.clusterCheckinInterval  = 20000 org.quartz.jobStore.misfireThreshold  = 60000 org.quartz.threadPool.threadCount  = 10 org.quartz.threadPool.threadPriority  = 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread  = true 
 
配置类(参考 QuartzAutoConfiguration) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Component public  class  JobFactory  extends  AdaptableJobFactory  {    @Resource      private  AutowireCapableBeanFactory capableBeanFactory;               @Override      protected  Object createJobInstance (TriggerFiredBundle bundle)  throws  Exception {                  Object  jobInstance  =  super .createJobInstance(bundle);                  capableBeanFactory.autowireBean(jobInstance);         return  jobInstance;     } } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Configuration public  class  JobConfig  {    @Resource      private  JobFactory jobFactory;     @Resource      private  DataSource dataSource;     @Bean      public  SchedulerFactoryBean schedulerFactoryBean ()  {         SchedulerFactoryBean  schedulerFactoryBean  =  new  SchedulerFactoryBean ();         schedulerFactoryBean.setJobFactory(jobFactory);                  schedulerFactoryBean.setDataSource(dataSource);                  schedulerFactoryBean.setOverwriteExistingJobs(true );                  schedulerFactoryBean.setStartupDelay(1 );                  schedulerFactoryBean.setConfigLocation(new  ClassPathResource ("quartz.properties" ));         return  schedulerFactoryBean;     }     @Bean      public  Scheduler scheduler ()  {         return  schedulerFactoryBean().getScheduler();     } } 
 
基本使用 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Component public  class  JobInit  implements  ApplicationRunner  {    private  static  final  String  ID  =  "SUMMERDAY" ;     @Autowired      private  Scheduler scheduler;     @Override      public  void  run (ApplicationArguments args)  throws  Exception {         JobDetail  jobDetail  =  JobBuilder.newJob(SecondJob.class)                 .withIdentity(ID + " 02" )                 .storeDurably()                 .build();         CronScheduleBuilder  scheduleBuilder  =                  CronScheduleBuilder.cronSchedule("0/10 * * * * ? *" );                  Trigger  trigger  =  TriggerBuilder.newTrigger()                 .forJob(jobDetail)                 .withIdentity(ID + " 02Trigger" )                 .withSchedule(scheduleBuilder)                 .startNow()                  .build();         Set<Trigger> set = new  HashSet <>();         set.add(trigger);                  scheduler.scheduleJob(jobDetail, set, true );     } }