Quartz定时任务-笔记
一、Quartz的体系结构
体系结构
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中取出数据。
// define the job and tie it to our DumbJob class
JobDetail job = newJob(DumbJob.class)
.withIdentity("myJob", "group1") // name "myJob", group "group1"
.usingJobData("jobSays", "Hello World!") // 放入数据
.usingJobData("myFloatValue", 3.141f)
.build();
public class DumbJob implements Job {
public DumbJob() {
}
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey key = context.getJobDetail().getKey();
JobDataMap dataMap = context.getJobDetail().getJobDataMap(); // 获取JobDataMap实例
String jobSays = dataMap.getString("jobSays"); // 取出数据
float myFloatValue = dataMap.getFloat("myFloatValue");
System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
}
}
如果使用了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
静态导入:
import static org.quartz.TriggerBuilder.*;
import static org.quartz.SimpleScheduleBuilder.*;
import static org.quartz.DateBuilder.*:
指定时间开始触发,不重复:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.startAt(myTimeToStartFiring) // if a start time is not given (if this line were omitted), "now" is implied
.withSchedule(simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(10)) // note that 10 repeats will give a total of 11 firings
.forJob(myJob) // identify job with handle to its JobDetail itself
.build();
指定时间触发,每隔10秒执行一次,重复10次:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.startAt(myTimeToStartFiring) // if a start time is not given (if this line were omitted), "now" is implied
.withSchedule(simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(10)) // note that 10 repeats will give a total of 11 firings
.forJob(myJob) // identify job with handle to its JobDetail itself
.build();
5分钟以后开始触发,仅执行一次:
trigger = (SimpleTrigger) newTrigger()
.withIdentity("trigger5", "group1")
.startAt(futureDate(5, IntervalUnit.MINUTE)) // use DateBuilder to create a date in the future
.forJob(myJobKey) // identify job with its JobKey
.build();
立即触发,每个5分钟执行一次,直到22:00:
trigger = newTrigger()
.withIdentity("trigger7", "group1")
.withSchedule(simpleSchedule()
.withIntervalInMinutes(5)
.repeatForever())
.endAt(dateOf(22, 0, 0))
.build();
建立一个触发器,将在下一个小时的整点触发,然后每2小时重复一次:
trigger = newTrigger()
.withIdentity("trigger8") // because group is not specified, "trigger8" will be in the default group
.startAt(evenHourDate(null)) // get the next even-hour (minutes and seconds zero ("00:00"))
.withSchedule(simpleSchedule()
.withIntervalInHours(2)
.repeatForever())
// note that in this example, 'forJob(..)' is not called which is valid
// if the trigger is passed to the scheduler along with the job
.build();
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)进行配置。
trigger = newTrigger()
.withIdentity("trigger7", "group1")
.withSchedule(simpleSchedule()
.withIntervalInMinutes(5)
.repeatForever()
.withMisfireHandlingInstructionNextWithExistingCount())
.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是由七个子表达式组成的字符串,用于描述日程表的各个细节。这些子表达式用空格分隔,并表示:
- Seconds
- Minutes
- Hours
- Day-of-Month
- Month
- Day-of-Week
- Year (optional field)
一个完整的Cron-Expressions的例子是字符串“0 0 12?* WED“ - 这意味着”每个星期三下午12:00“。
构建CronTriggers
静态导入:
import static org.quartz.TriggerBuilder.*;
import static org.quartz.CronScheduleBuilder.*;
import static org.quartz.DateBuilder.*:
建立一个触发器,每隔一分钟,每天上午8点至下午5点之间:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 0/2 8-17 * * ?"))
.forJob("myJob", "group1")
.build();
建立一个触发器,将在上午10:42每天发射:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(dailyAtHourAndMinute(10, 42))
.forJob(myJobKey)
.build();
或者:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 42 10 * * ?"))
.forJob(myJobKey)
.build();
建立一个触发器,将在星期三上午10:42在TimeZone(系统默认值)之外触发:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(weeklyOnDayAndHourAndMinute(DateBuilder.WEDNESDAY, 10, 42))
.forJob(myJobKey)
.inTimeZone(TimeZone.getTimeZone("America/Los_Angeles"))
.build();
或者:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 42 10 ? * WED"))
.inTimeZone(TimeZone.getTimeZone("America/Los_Angeles"))
.forJob(myJobKey)
.build();
CronTrigger Misfire
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
MISFIRE_INSTRUCTION_DO_NOTHING
MISFIRE_INSTRUCTION_FIRE_NOW
在构建CronTriggers时,可以将misfire指令指定为简单计划的一部分(通过CronSchedulerBuilder):
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 0/2 8-17 * * ?")
..withMisfireHandlingInstructionFireAndProceed())
.forJob("myJob", "group1")
.build();
三、Spring整合Quartz
当前的Java项目中使用Quartz一般都是与Spring整合使用。
Spring整合Quartz进行配置遵循下面的步骤:
- 定义工作的Job
- 定义触发器Trigger,并将触发器与工作任务绑定
- 定义调度器,并将Trigger注册到schedule
必须的maven依赖文件
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.29</version>
</dependency>
</dependencies>
配置JobDetail
提供了两种方式来配置job,分别是:MethodInvokingJobDetailFactoryBean和JobDetailFactoryBean
<!--定义任务bean,使用JobDetailFactoryBean或者MethodInvokingJobDetailFactoryBean配置-->
<bean name="Job1" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<!--设置job名称-->
<property name="name" value="job1"/>
<!--设置job分组-->
<property name="group" value="group1"/>
<!--设置数据-->
<property name="jobDataAsMap">
<map>
<entry key="id" value="1"/>
</map>
</property>
<!--指定具体的job类-->
<property name="jobClass" value="top.ywlog.task.HelloJob"/>
<!--可选:如果为false,当没有活动的触发器与之关联时会在触发器中删除该任务-->
<property name="durability" value="false"/>
<!--
可选:指定Spring的容器key,如果不设定在job中的jobMp是获取不到spring容器的,
其实现了ApplicationContextAware,其中的setApplicationContext方法会得到当前工厂对象,
且将工厂对象存在了类的一个熟悉applicationContext中,源码如下:
getJobDataMap.put(this.applicationContextJobDataKey, this.applicationContext);
所以在Job的jobDataMap中可以获取工厂对象:
(ApplicationContext) jobDataMap.get("applicationContext2020");
.usingJobData("data1", "hello world");
.usingJobData("applicationContext2020", spring工厂对象);
jobDataMap.get("data1");
-->
<property name="applicationContextJobDataKey" value="applicationContext2020"/>
</bean>
配置Trigger
SimpleTrigger配置
<!--定义触发器 定义一个simpleTrigger,一个触发器只能绑定一个任务-->
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="name" value="simple"/>
<property name="group" value="group1"/>
<property name="jobDetail" ref="Job1"/>
<property name="jobDataAsMap">
<map>
<entry key="number" value="1000"/>
</map>
</property>
<property name="startDelay" value="1000"/>
<!--repeatInterval的单位是ms-->
<property name="repeatInterval" value="1000"/>
<!--除去起始的一次,之后的重复次数-->
<property name="repeatCount" value="2"/>
</bean>
CronTrigger配置
<!--定义触发器 定义一个CronTrigger,一个触发器只能绑定一个任务-->
<bean id="cronTrigger1" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="name" value="cronTrigger"/>
<property name="group" value="group2"/>
<property name="jobDetail" ref="Job1"/>
<property name="cronExpression" value="*/5 * * * * ?"/>
<property name="jobDataAsMap">
<map>
<entry key="number" value="1000"/>
</map>
</property>
</bean>
配置scheduler
<!--定义调度器-->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<!--注册触发器-->
<property name="triggers">
<list>
<ref bean="simpleTrigger"/>
<ref bean="cronTrigger"/>
</list>
</property>
<!--添加quartz配置,直接配置或者引入配置文件-->
<property name="configLocation" value="classpath:quartz.properties"/>
<!-- 直接配置时其值将覆盖 quartz.properties配置文件中的设置-->
<property name="quartzProperties">
<props>
<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
<prop key="org.quartz.threadPool.threadCount">5</prop>
</props>
</property>
<!--SchedulerFactoryBean 在初始化后是否马上启动 Scheduler-->
<property name="autoStartup" value="true"/>
<!--
SchedulerFactoryBean 初始化完成后,延迟多少秒启动 Scheduler,默认值为0,表示马上启动。
除非拥有需要立即执行的任务,
一般情况下,可以通过属性让 Scheduler 延迟一小段时间后启动,以便让 Spring 能够更快初始化容器中剩余的 Bean。
单位:s
-->
<property name="startupDelay" value="0"/>
</bean>
至此,Spring整合quartz的配置就完成了。
在job中进行依赖注入
不能直接在JOB中使用Spring的方式获取bean,而是需要通过ApplicationContext.getBean()的方式获取,通过一个工具类可以获取ApplicationContext.
@Component
public class SpringApplicationContextUtil implements ApplicationContextAware
{
public static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
SpringApplicationContextUtil.applicationContext = applicationContext;
}
}
在job中使用:
TestService testService = SpringApplicationContextUtil.applicationContext.getBean(StringUtils.
uncapitalize(TestService.class.getSimpleName()), TestService.class);
四、持久化
Quartz提供两种基本作业存储类型。第一种类型叫做RAMJobStore,第二种类型叫做JDBC作业存储。在默认情况下Quartz将任务调度的运行信息保存在内存中,这种方法提供了最佳的性能,因为内存中数据访问最快。不足之处是缺乏数据的持久性,当程序路途停止或系统崩溃时,所有运行的信息都会丢失。
因为需要把quartz的数据保存到数据库,所以要建立相关的数据库。这个可以从下载到的quartz包里面找到对应的sql脚本,目前可以支持mysql,DB2,oracle等主流的数据库,自己可以根据项目需要选择合适的脚本运行。
数据库脚本
持久化配置
数据库表建立完成后,对quartz进行JobStore的配置
有两种方式,一种是直接使用quartz.properties的方式配置JDBC等信息,如下所示:
Quartz的属性配置文件主要包括三方面的信息:
集群信息;
调度器线程池;
任务调度现场数据的保存。
# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#
#集群配置
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
org.quartz.jobStore.misfireThreshold: 60000
#============================================================================
# Configure JobStore
#============================================================================
#默认配置,数据保存到内存
#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
#持久化配置
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties:true
#数据库表前缀
#org.quartz.jobStore.tablePrefix:qrtz_
#org.quartz.jobStore.dataSource:qzDS
#============================================================================
# Configure Datasources
#============================================================================
#JDBC驱动
org.quartz.dataSource.qzDS.driver:com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL:jdbc:mysql://localhost:3306/quartz
org.quartz.dataSource.qzDS.user:root
org.quartz.dataSource.qzDS.password:root
org.quartz.dataSource.qzDS.maxConnection:10
然后Spring-quartz配置为:
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="configLocation" value="classpath:quartz.properties"/>
<!--如果没有固定的任务,可以不再定义JobDetail和Trigger,可以动态添加任务-->
<property name="triggers">
<list>
</list>
</property>
</bean>
另一种方式是直接在Spring-quartz.xml文件中配置,如下:
<!--DataBase JobStore -->
<!--datasource-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/quartz"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="minIdle" value="1"/>
<property name="initialSize" value="1"/>
<property name="maxActive" value="1"/>
<property name="maxWait" value="3000"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="validationQuery" value="SELECT *"/>
<property name="testWhileIdle" value="true"/>
<property name="testOnBorrow" value="false"/>
<property name="testOnReturn" value="false"/>
</bean>
<!--scheduler-->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="quartzProperties">
<props>
<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
<prop key="org.quartz.threadPool.threadCount">5</prop>
</props>
</property>
<!--如果没有固定的任务,可以不再定义JobDetail和Trigger,可以动态添加任务-->
<property name="triggers">
<list>
</list>
</property>
</bean>