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 classJobDetail 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_POLICYMISFIRE_INSTRUCTION_DO_NOTHINGMISFIRE_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.
@Componentpublic class SpringApplicationContextUtil implements ApplicationContextAware{public static ApplicationContext applicationContext;@Overridepublic 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: DefaultQuartzSchedulerorg.quartz.scheduler.rmi.export: falseorg.quartz.scheduler.rmi.proxy: falseorg.quartz.scheduler.wrapJobExecutionInUserTransaction: falseorg.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPoolorg.quartz.threadPool.threadCount: 10org.quartz.threadPool.threadPriority: 5org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: trueorg.quartz.jobStore.misfireThreshold: 60000#============================================================================# Configure JobStore#============================================================================#默认配置,数据保存到内存#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore#持久化配置org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTXorg.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegateorg.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.Driverorg.quartz.dataSource.qzDS.URL:jdbc:mysql://localhost:3306/quartzorg.quartz.dataSource.qzDS.user:rootorg.quartz.dataSource.qzDS.password:rootorg.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>
