Quartz定时任务-笔记

一、Quartz的体系结构

体系结构

Quartz定时任务 - 图1

Quartz API的关键接口

  • Scheduler - 与调度程序交互的主要API。

  • Job - 你想要调度器执行的任务组件需要实现的接口

  • JobDetail - 用于定义作业的实例。

  • Trigger(即触发器) - 定义执行给定作业的计划的组件。

  • JobBuilder - 用于定义/构建 JobDetail 实例,用于定义作业的实例。

  • TriggerBuilder - 用于定义/构建触发器实例。

Scheduler 的生命期,从 SchedulerFactory 创建它时开始,到 Scheduler 调用shutdown() 方法时结束;Scheduler 被创建后,可以增加、删除和列举 Job 和 Trigger,以及执行其它与调度相关的操作(如暂停 Trigger)。但是,Scheduler 只有在调用 start() 方法后,才会真正地触发 trigger(即执行 job)

二、Quartz详解

1、Job与JobDetail介绍

当定义一个实现Job接口的类时,这个类仅仅表明该job需要完成什么类型的任务,除此之外,Quartz还需要知道该Job实例所包含的属性;这将由JobDetail类来完成,JobDetail实例是通过JobBuilder类创建的。

通过JobDetail对象,可以给job实例配置的其它属性有:

  • Durability:如果一个job是非持久的,当没有活跃的trigger与之关联的时候,会被自动地从scheduler中删除。也就是说,非持久的job的生命期是由trigger的存在与否决定的;
  • RequestsRecovery:如果一个job是可恢复的,并且在其执行的时候,scheduler发生硬关闭(hard shutdown)(比如运行的进程崩溃了,或者关机了),则当scheduler重新启动的时候,该job会被重新执行。此时,该job的JobExecutionContext.isRecovering() 返回true。

Job实例

可以只创建一个job类,然后创建多个与该job关联的JobDetail实例,每一个实例都有自己的属性集和JobDataMap,最后,将所有的实例都加到scheduler中。当一个trigger被触发时,与之关联的JobDetail实例会被加载,JobDetail引用的job类通过配置在Scheduler上的JobFactory进行初始化。默认的JobFactory实现,仅仅是调用job类的newInstance()方法,然后尝试调用JobDataMap中的key的setter方法。你也可以创建自己的JobFactory实现,比如让你的IOC或DI容器可以创建/初始化job实例。

Key

将 Job 和 Trigger 注册到 Scheduler 时,可以为它们设置 key,配置其身份属性。 Job 和 Trigger 的 key(JobKey 和 TriggerKey)可以用于将 Job 和 Trigger 放到不同的分组(group)里,然后基于分组进行操作。同一个分组下的 Job 或 Trigger 的名称必须唯一,即一个 Job 或 Trigger 的 key 由名称(name)和分组(group)组成。

JobDataMap

创建定时任务时,传给scheduler一个JobDetail实例,在创建JobDetail时,将要执行的job的类名传给了JobDetail,所以scheduler就知道了要执行的具体job,每次当scheduler执行job时,在调用其execute(…)方法之前会创建该类的一个新的实例;执行完毕,对该实例的引用就被丢弃了,实例会被垃圾回收。这种执行策略带来的一个后果是,job必须有一个无参的构造函数(当使用默认的JobFactory时);另一个后果是,在job类中,不应该定义有状态的数据属性,因为在job的多次执行中,这些属性的值不会保留。

因为每次Schedule执行job任务都会创建新的实例,所以Job中不能定义有状态的数据属性,这时便可以使用JobDataMap(JobDetail对象的一部分),JobDataMap中可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用其中的数据;JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本类型的数据的方法。

将job添加到schedule前,在构建JobDetail时,可以将数据放入JobDataMap中,然后可以在Job执行过程中,从JobDataMap中取出数据。

  1. // define the job and tie it to our DumbJob class
  2. JobDetail job = newJob(DumbJob.class)
  3. .withIdentity("myJob", "group1") // name "myJob", group "group1"
  4. .usingJobData("jobSays", "Hello World!") // 放入数据
  5. .usingJobData("myFloatValue", 3.141f)
  6. .build();
  1. public class DumbJob implements Job {
  2. public DumbJob() {
  3. }
  4. public void execute(JobExecutionContext context) throws JobExecutionException {
  5. JobKey key = context.getJobDetail().getKey();
  6. JobDataMap dataMap = context.getJobDetail().getJobDataMap(); // 获取JobDataMap实例
  7. String jobSays = dataMap.getString("jobSays"); // 取出数据
  8. float myFloatValue = dataMap.getFloat("myFloatValue");
  9. System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
  10. }
  11. }

如果使用了JobStore,需要注意JobDataMap中的数据会被序列化

JobDataMap中的数据不仅可以在构建JobDetail时放入,还可以在Trigger构建时放入,在Job实例中使用context提供的getMergedJobDataMap()方法即可获取它们提供的JobDataMap的并集,但是如果存在相同的数据,则后者会覆盖前者的值。

Job状态与并发

在job类上可以加入一些注解,这些注解会影响job的状态和并发性:

@DisallowConcurrentExecution:将该注解加到job类上,告诉Quartz不要并发地执行同一个job定义(这里指特定的job类)的多个实例。比如“SalesReportJob”类上有该注解,则同一时刻仅允许执行一个“SalesReportForJoe”实例,但可以并发地执行“SalesReportForMike”类的一个实例。所以该限制是针对JobDetail的,而不是job类的(如果定义两个JobDetail,引用同一个Job类,是会并发执行的)。但可以认为应该将该注解放在job类上,因为job类的改变经常会导致其行为发生变化。

@PersistJobDataAfterExecution:将该注解加在job类上,告诉Quartz在成功执行了job类的execute方法后(没s有发生任何异常),更新JobDetail中JobDataMap的数据,使得该job(即JobDetail)在下一次执行的时候,JobDataMap中是更新后的数据,而不是更新前的旧数据。和 @DisallowConcurrentExecution注解一样,尽管注解是加在job类上的,但其限制作用是针对job实例的,而不是job类的。由job类来承载注解,是因为job类的内容经常会影响其行为状态(比如,job类的execute方法需要显式地“理解”其”状态“)。

如果使用了@PersistJobDataAfterExecution注解,强烈建议同时使用@DisallowConcurrentExecution注解,因为当同一个job(JobDetail)的两个实例被并发执行时,由于竞争,JobDataMap中存储的数据很可能是不确定的。

execute方法中仅允许抛出一种类型的异常(包括RuntimeExceptions),即JobExecutionException。因此,你应该将execute方法中的所有内容都放到一个”try-catch”块中

2、Trigger(触发器)

Trigger的公共属性

所有类型的trigger都有TriggerKey这个属性,表示trigger的身份;除此之外,trigger还有很多其它的公共属性。这些属性,在构建trigger的时候可以通过TriggerBuilder设置。

trigger的公共属性有:

  • jobKey属性:当trigger触发时被执行的job的身份;
  • startTime属性:设置trigger第一次触发的时间;该属性的值是java.util.Date类型,表示某个指定的时间点;有些类型的trigger,会在设置的startTime时立即触发,有些类型的trigger,表示其触发是在startTime之后开始生效。比如,现在是1月份,你设置了一个trigger–“在每个月的第5天执行”,然后将startTime属性设置为4月1号,则该trigger第一次触发会是在几天以后了(即4月5号)。
  • endTime属性:表示trigger失效的时间点。比如,”每月第5天执行”的trigger,如果其endTime是7月1号,则其最后一次执行时间是6月5号。

优先级(priority)

如果trigger很多(或者Quartz线程池的工作线程太少),Quartz可能没有足够的资源同时触发所有的trigger;这种情况下,r如果希望控制哪些trigger优先使用Quartz的工作线程,要达到该目的,可以在trigger上设置priority属性。

比如,有N个trigger需要同时触发,但只有Z个工作线程,优先级最高的Z个trigger会被首先触发。如果没有为trigger设置优先级,trigger使用默认优先级,值为5;priority属性的值可以是任意整数,正数、负数都可以。

只有同时触发的trigger之间才会比较优先级。10:59触发的trigger总是在11:00触发的trigger之前执行。

如果trigger是可恢复的,在恢复后再调度时,优先级与原trigger是一样的。

错过触发(misfire Instructions)

trigger还有一个重要的属性misfire;如果scheduler关闭了,或者Quartz线程池中没有可用的线程来执行job,此时持久性的trigger就会错过(miss)其触发时间,即错过触发(misfire)。

不同类型的trigger,有不同的misfire机制。它们默认都使用“智能机制(smart policy)”,即根据trigger的类型和配置动态调整行为。当scheduler启动的时候,查询所有错过触发(misfire)的持久性trigger。然后根据它们各自的misfire机制更新trigger的信息。

日历(calendar)

Quartz的Calendar对象(不是java.util.Calendar对象)可以在定义和存储trigger的时候与trigger进行关联。Calendar用于从trigger的调度计划中排除时间段。比如,可以创建一个trigger,每个工作日的上午9:30执行,然后增加一个Calendar,排除掉所有的商业节日。任何实现了Calendar接口的可序列化对象都可以作为Calendar对象

3、SimpleTrigger

SimpleTrigger的属性包括:开始时间、结束时间、重复次数以及重复的间隔。

SimpleTrigger可以满足的调度需求是:在具体的时间点执行一次,或者在具体的时间点执行,并且以指定的间隔重复执行若干次。比如,你有一个trigger,你可以设置它在2015年1月13日的上午11:23:54准时触发,或者在这个时间点触发,并且每隔2秒触发一次,一共重复5次。

SimpleTrigger实例通过TriggerBuilder设置主要的属性,通过SimpleScheduleBuilder设置与SimpleTrigger相关的属性。

构建SimpleTrigger

静态导入:

  1. import static org.quartz.TriggerBuilder.*;
  2. import static org.quartz.SimpleScheduleBuilder.*;
  3. import static org.quartz.DateBuilder.*:

指定时间开始触发,不重复:

  1. trigger = newTrigger()
  2. .withIdentity("trigger3", "group1")
  3. .startAt(myTimeToStartFiring) // if a start time is not given (if this line were omitted), "now" is implied
  4. .withSchedule(simpleSchedule()
  5. .withIntervalInSeconds(10)
  6. .withRepeatCount(10)) // note that 10 repeats will give a total of 11 firings
  7. .forJob(myJob) // identify job with handle to its JobDetail itself
  8. .build();

指定时间触发,每隔10秒执行一次,重复10次:

  1. trigger = newTrigger()
  2. .withIdentity("trigger3", "group1")
  3. .startAt(myTimeToStartFiring) // if a start time is not given (if this line were omitted), "now" is implied
  4. .withSchedule(simpleSchedule()
  5. .withIntervalInSeconds(10)
  6. .withRepeatCount(10)) // note that 10 repeats will give a total of 11 firings
  7. .forJob(myJob) // identify job with handle to its JobDetail itself
  8. .build();

5分钟以后开始触发,仅执行一次:

  1. trigger = (SimpleTrigger) newTrigger()
  2. .withIdentity("trigger5", "group1")
  3. .startAt(futureDate(5, IntervalUnit.MINUTE)) // use DateBuilder to create a date in the future
  4. .forJob(myJobKey) // identify job with its JobKey
  5. .build();

立即触发,每个5分钟执行一次,直到22:00:

  1. trigger = newTrigger()
  2. .withIdentity("trigger7", "group1")
  3. .withSchedule(simpleSchedule()
  4. .withIntervalInMinutes(5)
  5. .repeatForever())
  6. .endAt(dateOf(22, 0, 0))
  7. .build();

建立一个触发器,将在下一个小时的整点触发,然后每2小时重复一次:

  1. trigger = newTrigger()
  2. .withIdentity("trigger8") // because group is not specified, "trigger8" will be in the default group
  3. .startAt(evenHourDate(null)) // get the next even-hour (minutes and seconds zero ("00:00"))
  4. .withSchedule(simpleSchedule()
  5. .withIntervalInHours(2)
  6. .repeatForever())
  7. // note that in this example, 'forJob(..)' is not called which is valid
  8. // if the trigger is passed to the scheduler along with the job
  9. .build();
  10. scheduler.scheduleJob(trigger, job);

SimpleTrigger Misfire策略

MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY MISFIRE_INSTRUCTION_FIRE_NOW MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT

构建使用SimpleTrigger构建Trigger时,misfire作为基本调度的一部分(simple schedule)进行配置。

  1. trigger = newTrigger()
  2. .withIdentity("trigger7", "group1")
  3. .withSchedule(simpleSchedule()
  4. .withIntervalInMinutes(5)
  5. .repeatForever()
  6. .withMisfireHandlingInstructionNextWithExistingCount())
  7. .build();

4、CronTrigger

CronTrigger通常比Simple Trigger更有用,如果需要基于日历的概念而不是按照SimpleTrigger的精确指定间隔进行重新启动的作业启动计划。

使用CronTrigger,您可以指定号时间表,例如“每周五中午”或“每个工作日和上午9:30”,甚至“每周一至周五上午9:00至10点之间每5分钟”和1月份的星期五“。

即使如此,和SimpleTrigger一样,CronTrigger有一个startTime,它指定何时生效,以及一个(可选的)endTime,用于指定何时停止计划。

Cron Expressions

Cron-Expressions用于配置CronTrigger的实例。Cron Expressions是由七个子表达式组成的字符串,用于描述日程表的各个细节。这些子表达式用空格分隔,并表示:

  1. Seconds
  2. Minutes
  3. Hours
  4. Day-of-Month
  5. Month
  6. Day-of-Week
  7. Year (optional field)

一个完整的Cron-Expressions的例子是字符串“0 0 12?* WED“ - 这意味着”每个星期三下午12:00“。

构建CronTriggers

静态导入:

  1. import static org.quartz.TriggerBuilder.*;
  2. import static org.quartz.CronScheduleBuilder.*;
  3. import static org.quartz.DateBuilder.*:

建立一个触发器,每隔一分钟,每天上午8点至下午5点之间:

  1. trigger = newTrigger()
  2. .withIdentity("trigger3", "group1")
  3. .withSchedule(cronSchedule("0 0/2 8-17 * * ?"))
  4. .forJob("myJob", "group1")
  5. .build();

建立一个触发器,将在上午10:42每天发射:

  1. trigger = newTrigger()
  2. .withIdentity("trigger3", "group1")
  3. .withSchedule(dailyAtHourAndMinute(10, 42))
  4. .forJob(myJobKey)
  5. .build();

或者:

  1. trigger = newTrigger()
  2. .withIdentity("trigger3", "group1")
  3. .withSchedule(cronSchedule("0 42 10 * * ?"))
  4. .forJob(myJobKey)
  5. .build();

建立一个触发器,将在星期三上午10:42在TimeZone(系统默认值)之外触发:

  1. trigger = newTrigger()
  2. .withIdentity("trigger3", "group1")
  3. .withSchedule(weeklyOnDayAndHourAndMinute(DateBuilder.WEDNESDAY, 10, 42))
  4. .forJob(myJobKey)
  5. .inTimeZone(TimeZone.getTimeZone("America/Los_Angeles"))
  6. .build();

或者:

  1. trigger = newTrigger()
  2. .withIdentity("trigger3", "group1")
  3. .withSchedule(cronSchedule("0 42 10 ? * WED"))
  4. .inTimeZone(TimeZone.getTimeZone("America/Los_Angeles"))
  5. .forJob(myJobKey)
  6. .build();

CronTrigger Misfire

  1. MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
  2. MISFIRE_INSTRUCTION_DO_NOTHING
  3. MISFIRE_INSTRUCTION_FIRE_NOW

在构建CronTriggers时,可以将misfire指令指定为简单计划的一部分(通过CronSchedulerBuilder):

  1. trigger = newTrigger()
  2. .withIdentity("trigger3", "group1")
  3. .withSchedule(cronSchedule("0 0/2 8-17 * * ?")
  4. ..withMisfireHandlingInstructionFireAndProceed())
  5. .forJob("myJob", "group1")
  6. .build();

三、Spring整合Quartz

当前的Java项目中使用Quartz一般都是与Spring整合使用。

Spring整合Quartz进行配置遵循下面的步骤:

  1. 定义工作的Job
  2. 定义触发器Trigger,并将触发器与工作任务绑定
  3. 定义调度器,并将Trigger注册到schedule

必须的maven依赖文件

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-core</artifactId>
  5. <version>4.3.5.RELEASE</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.springframework</groupId>
  9. <artifactId>spring-context-support</artifactId>
  10. <version>4.3.5.RELEASE</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.springframework</groupId>
  14. <artifactId>spring-tx</artifactId>
  15. <version>4.3.5.RELEASE</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.springframework</groupId>
  19. <artifactId>spring-jdbc</artifactId>
  20. <version>4.3.5.RELEASE</version>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.quartz-scheduler</groupId>
  24. <artifactId>quartz</artifactId>
  25. <version>2.2.3</version>
  26. </dependency>
  27. <dependency>
  28. <groupId>mysql</groupId>
  29. <artifactId>mysql-connector-java</artifactId>
  30. <version>5.1.29</version>
  31. </dependency>
  32. </dependencies>

配置JobDetail

提供了两种方式来配置job,分别是:MethodInvokingJobDetailFactoryBean和JobDetailFactoryBean

  1. <!--定义任务bean,使用JobDetailFactoryBean或者MethodInvokingJobDetailFactoryBean配置-->
  2. <bean name="Job1" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
  3. <!--设置job名称-->
  4. <property name="name" value="job1"/>
  5. <!--设置job分组-->
  6. <property name="group" value="group1"/>
  7. <!--设置数据-->
  8. <property name="jobDataAsMap">
  9. <map>
  10. <entry key="id" value="1"/>
  11. </map>
  12. </property>
  13. <!--指定具体的job类-->
  14. <property name="jobClass" value="top.ywlog.task.HelloJob"/>
  15. <!--可选:如果为false,当没有活动的触发器与之关联时会在触发器中删除该任务-->
  16. <property name="durability" value="false"/>
  17. <!--
  18. 可选:指定Spring的容器key,如果不设定在job中的jobMp是获取不到spring容器的,
  19. 其实现了ApplicationContextAware,其中的setApplicationContext方法会得到当前工厂对象,
  20. 且将工厂对象存在了类的一个熟悉applicationContext中,源码如下:
  21. getJobDataMap.put(this.applicationContextJobDataKey, this.applicationContext);
  22. 所以在Job的jobDataMap中可以获取工厂对象:
  23. (ApplicationContext) jobDataMap.get("applicationContext2020");
  24. .usingJobData("data1", "hello world");
  25. .usingJobData("applicationContext2020", spring工厂对象);
  26. jobDataMap.get("data1");
  27. -->
  28. <property name="applicationContextJobDataKey" value="applicationContext2020"/>
  29. </bean>

配置Trigger

SimpleTrigger配置

  1. <!--定义触发器 定义一个simpleTrigger,一个触发器只能绑定一个任务-->
  2. <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
  3. <property name="name" value="simple"/>
  4. <property name="group" value="group1"/>
  5. <property name="jobDetail" ref="Job1"/>
  6. <property name="jobDataAsMap">
  7. <map>
  8. <entry key="number" value="1000"/>
  9. </map>
  10. </property>
  11. <property name="startDelay" value="1000"/>
  12. <!--repeatInterval的单位是ms-->
  13. <property name="repeatInterval" value="1000"/>
  14. <!--除去起始的一次,之后的重复次数-->
  15. <property name="repeatCount" value="2"/>
  16. </bean>

CronTrigger配置

  1. <!--定义触发器 定义一个CronTrigger,一个触发器只能绑定一个任务-->
  2. <bean id="cronTrigger1" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
  3. <property name="name" value="cronTrigger"/>
  4. <property name="group" value="group2"/>
  5. <property name="jobDetail" ref="Job1"/>
  6. <property name="cronExpression" value="*/5 * * * * ?"/>
  7. <property name="jobDataAsMap">
  8. <map>
  9. <entry key="number" value="1000"/>
  10. </map>
  11. </property>
  12. </bean>

配置scheduler

  1. <!--定义调度器-->
  2. <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  3. <!--注册触发器-->
  4. <property name="triggers">
  5. <list>
  6. <ref bean="simpleTrigger"/>
  7. <ref bean="cronTrigger"/>
  8. </list>
  9. </property>
  10. <!--添加quartz配置,直接配置或者引入配置文件-->
  11. <property name="configLocation" value="classpath:quartz.properties"/>
  12. <!-- 直接配置时其值将覆盖 quartz.properties配置文件中的设置-->
  13. <property name="quartzProperties">
  14. <props>
  15. <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
  16. <prop key="org.quartz.threadPool.threadCount">5</prop>
  17. </props>
  18. </property>
  19. <!--SchedulerFactoryBean 在初始化后是否马上启动 Scheduler-->
  20. <property name="autoStartup" value="true"/>
  21. <!--
  22. SchedulerFactoryBean 初始化完成后,延迟多少秒启动 Scheduler,默认值为0,表示马上启动。
  23. 除非拥有需要立即执行的任务,
  24. 一般情况下,可以通过属性让 Scheduler 延迟一小段时间后启动,以便让 Spring 能够更快初始化容器中剩余的 Bean。
  25. 单位:s
  26. -->
  27. <property name="startupDelay" value="0"/>
  28. </bean>

至此,Spring整合quartz的配置就完成了。

在job中进行依赖注入

不能直接在JOB中使用Spring的方式获取bean,而是需要通过ApplicationContext.getBean()的方式获取,通过一个工具类可以获取ApplicationContext.

  1. @Component
  2. public class SpringApplicationContextUtil implements ApplicationContextAware
  3. {
  4. public static ApplicationContext applicationContext;
  5. @Override
  6. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
  7. {
  8. SpringApplicationContextUtil.applicationContext = applicationContext;
  9. }
  10. }

在job中使用:

  1. TestService testService = SpringApplicationContextUtil.applicationContext.getBean(StringUtils.
  2. uncapitalize(TestService.class.getSimpleName()), TestService.class);

四、持久化

Quartz提供两种基本作业存储类型。第一种类型叫做RAMJobStore,第二种类型叫做JDBC作业存储。在默认情况下Quartz将任务调度的运行信息保存在内存中,这种方法提供了最佳的性能,因为内存中数据访问最快。不足之处是缺乏数据的持久性,当程序路途停止或系统崩溃时,所有运行的信息都会丢失。

因为需要把quartz的数据保存到数据库,所以要建立相关的数据库。这个可以从下载到的quartz包里面找到对应的sql脚本,目前可以支持mysql,DB2,oracle等主流的数据库,自己可以根据项目需要选择合适的脚本运行。

数据库脚本

持久化配置

数据库表建立完成后,对quartz进行JobStore的配置

有两种方式,一种是直接使用quartz.properties的方式配置JDBC等信息,如下所示:

  1. Quartz的属性配置文件主要包括三方面的信息:
  • 集群信息;

  • 调度器线程池;

  • 任务调度现场数据的保存。

  1. # Default Properties file for use by StdSchedulerFactory
  2. # to create a Quartz Scheduler Instance, if a different
  3. # properties file is not explicitly specified.
  4. #
  5. #集群配置
  6. org.quartz.scheduler.instanceName: DefaultQuartzScheduler
  7. org.quartz.scheduler.rmi.export: false
  8. org.quartz.scheduler.rmi.proxy: false
  9. org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
  10. org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
  11. org.quartz.threadPool.threadCount: 10
  12. org.quartz.threadPool.threadPriority: 5
  13. org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
  14. org.quartz.jobStore.misfireThreshold: 60000
  15. #============================================================================
  16. # Configure JobStore
  17. #============================================================================
  18. #默认配置,数据保存到内存
  19. #org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
  20. #持久化配置
  21. org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
  22. org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  23. org.quartz.jobStore.useProperties:true
  24. #数据库表前缀
  25. #org.quartz.jobStore.tablePrefix:qrtz_
  26. #org.quartz.jobStore.dataSource:qzDS
  27. #============================================================================
  28. # Configure Datasources
  29. #============================================================================
  30. #JDBC驱动
  31. org.quartz.dataSource.qzDS.driver:com.mysql.jdbc.Driver
  32. org.quartz.dataSource.qzDS.URL:jdbc:mysql://localhost:3306/quartz
  33. org.quartz.dataSource.qzDS.user:root
  34. org.quartz.dataSource.qzDS.password:root
  35. org.quartz.dataSource.qzDS.maxConnection:10

然后Spring-quartz配置为:

  1. <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  2. <property name="configLocation" value="classpath:quartz.properties"/>
  3. <!--如果没有固定的任务,可以不再定义JobDetail和Trigger,可以动态添加任务-->
  4. <property name="triggers">
  5. <list>
  6. </list>
  7. </property>
  8. </bean>

另一种方式是直接在Spring-quartz.xml文件中配置,如下:

  1. <!--DataBase JobStore -->
  2. <!--datasource-->
  3. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
  4. destroy-method="close">
  5. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  6. <property name="url" value="jdbc:mysql://localhost:3306/quartz"/>
  7. <property name="username" value="root"/>
  8. <property name="password" value="root"/>
  9. <property name="minIdle" value="1"/>
  10. <property name="initialSize" value="1"/>
  11. <property name="maxActive" value="1"/>
  12. <property name="maxWait" value="3000"/>
  13. <property name="timeBetweenEvictionRunsMillis" value="60000"/>
  14. <property name="validationQuery" value="SELECT *"/>
  15. <property name="testWhileIdle" value="true"/>
  16. <property name="testOnBorrow" value="false"/>
  17. <property name="testOnReturn" value="false"/>
  18. </bean>
  19. <!--scheduler-->
  20. <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  21. <property name="dataSource" ref="dataSource"/>
  22. <property name="quartzProperties">
  23. <props>
  24. <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
  25. <prop key="org.quartz.threadPool.threadCount">5</prop>
  26. </props>
  27. </property>
  28. <!--如果没有固定的任务,可以不再定义JobDetail和Trigger,可以动态添加任务-->
  29. <property name="triggers">
  30. <list>
  31. </list>
  32. </property>
  33. </bean>