Quartz动态定时任务管理
2017-08-29 22:15
357 查看
楼主小白,目前小公司实习中,进入公司一个月了差不多让我写了个定时任务管理模块。也算入坑了吧!为了纪念这一伟大时刻决定写个我人生中第一篇微博来纪念一下,楼主小白,大神勿喷。只是本着学习和记录的态度写的。
动态QUARTZ 定时任务
Quartz不多解释了好吧。一个我认为功能很强大的定时任务框架(我觉得)。
首先你需要在数据库里建好表(自带的表),嗯,不多也就11张!!!。我用的是2x版本所以是这样的。
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;
CREATE TABLE QRTZ_JOB_DETAILS(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
IS_DURABLE VARCHAR(1) NOT NULL,
IS_NONCONCURRENT VARCHAR(1) NOT NULL,
IS_UPDATE_DATA VARCHAR(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT(13) NULL,
PREV_FIRE_TIME BIGINT(13) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(13) NOT NULL,
END_TIME BIGINT(13) NULL,
CALENDAR_NAME VARCHAR(200) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
REPEAT_COUNT BIGINT(7) NOT NULL,
REPEAT_INTERVAL BIGINT(12) NOT NULL,
TIMES_TRIGGERED BIGINT(10) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_CRON_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
CRON_EXPRESSION VARCHAR(120) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (SCHED_NAM
4000
E,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
STR_PROP_1 VARCHAR(512) NULL,
STR_PROP_2 VARCHAR(512) NULL,
STR_PROP_3 VARCHAR(512) NULL,
INT_PROP_1 INT NULL,
INT_PROP_2 INT NULL,
LONG_PROP_1 BIGINT NULL,
LONG_PROP_2 BIGINT NULL,
DEC_PROP_1 NUMERIC(13,4) NULL,
DEC_PROP_2 NUMERIC(13,4) NULL,
BOOL_PROP_1 VARCHAR(1) NULL,
BOOL_PROP_2 VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_BLOB_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_CALENDARS (
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME VARCHAR(200) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
ENGINE=InnoDB;
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_FIRED_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
FIRED_TIME BIGINT(13) NOT NULL,
SCHED_TIME BIGINT(13) NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(200) NULL,
JOB_GROUP VARCHAR(200) NULL,
IS_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SCHEDULER_STATE (
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
ENGINE=InnoDB;
CREATE TABLE QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;
CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
commit;
上面是2x版本的表。当然有如果你用的是1x的表是不一样的(请去找1x的说明,这篇说明不适合你,立刻就走别耽误工夫)。当时第一次用,纠结了半天。
1.如果你发现什么……column not fount。
2.某个字段未找到,或者未验证。
那么估计你是表的版本用错了,如果是2x版本请复制上面的,从官方文档上粘贴下来的。
以上是数据库建表语句,一共有11张表。但是,你并不一定用的到全部,最主要的有:
1.qrtz_job_detales我的理解管理任务的(最重要的之一)
2.qrtz_trigges管理触发器的(最重要之一)
3.qrtz_scheduler_state调度工厂(最重要之一)
4.qrtz_locks悲观锁(最重要的之一)我并不了解这个的含义但是没有不行,是控制数据线程的大概理解为(主要这个功能暂时没有用到它把这个,这个模块我只是让他跑起来,原理没有深究,因为时间有限啊!希望有大神请给我解释解释,谢谢了!)
5.qrtz_cron_trigger cron触发器表,cron表达式是我用到的定时任务里,来确定时间触发任务的(这个表不是必须的,但是这里主要演示的是这种方式其他方式的请立刻就走,这里不适合你)
6.其他表:我认为如果你是用只的CRON这种方式其他表基本用不到。
说一下包的问题,这是在spring3.0以上版本用到的quartz的包(我的是4.0的)如果你不导入各种错误,你导入错了版本各种错误(版本可能冲突请自行更正)如果你的jar里面有了请不要继续导入。
http://upload-images.jianshu.io/upload_images/154112-e9aea83024725e94.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240
配置文件。这里说一下我配置遇到的很大问题,我们这个项目是shiro+quartz的版本冲突很严重,一开始是会话session这里出现了问题,我把这里删掉将quartz不放到session没有了问题(暂时),然后因为是maven项目,spring文件和数据库文件没在一个包下导致Data source not found这个错误,解决的方法时quartz重新开个数据源,如下所示,以为我们这个是maven项目分了好多项目包,为了不重复导入文件,将quartz配置写入了spring中,如下所示:另外因为事物中也需要开启对resume读写的权限如下图所示:
因为要求的原因我还多创建了一张表。taskScheduled这是它的实体类,表中主要的也就是这些。
调度工厂这个是核心中的核心吧,主要通过反射实例化类中方法,反射不多bb了吧最强大的机制之一(虽然我了解的不是很深。。。。。无奈)。
里面的注释部分当时我打算直接通过service直接操作数据库,但是经理说一般项目都是直接调用空参类型方法,里面怎么用需要自己去配参数,所以注释下面那个直接调用某个包里面的空参方法只是简单的每隔5秒打印出一个字符串。当然你可以自己调用复杂的方法,只要是空参就行。如果不是你需要自行配参数,就是getDeclaredMethod(tm,Interget.class)这样就能调用参数使int类型或者Integer方法了。即使是空参方法执行的功能也有很多需要你自己去写。
Xml因为是动态调用有上面那个类会加到数据库中管理
DAO层
Service层“
ServiceImpl
结束语:没有现成的代码项目,因为写在总项目里了。但是核心贴出来了,欢迎大家指正,共同进步。现在十点半了,很累.再见!
还会有其他的总结,一路坚持吧!
动态QUARTZ 定时任务
Quartz不多解释了好吧。一个我认为功能很强大的定时任务框架(我觉得)。
首先你需要在数据库里建好表(自带的表),嗯,不多也就11张!!!。我用的是2x版本所以是这样的。
这里写代码片#
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;
CREATE TABLE QRTZ_JOB_DETAILS(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
IS_DURABLE VARCHAR(1) NOT NULL,
IS_NONCONCURRENT VARCHAR(1) NOT NULL,
IS_UPDATE_DATA VARCHAR(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT(13) NULL,
PREV_FIRE_TIME BIGINT(13) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(13) NOT NULL,
END_TIME BIGINT(13) NULL,
CALENDAR_NAME VARCHAR(200) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
REPEAT_COUNT BIGINT(7) NOT NULL,
REPEAT_INTERVAL BIGINT(12) NOT NULL,
TIMES_TRIGGERED BIGINT(10) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_CRON_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
CRON_EXPRESSION VARCHAR(120) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (SCHED_NAM
4000
E,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
STR_PROP_1 VARCHAR(512) NULL,
STR_PROP_2 VARCHAR(512) NULL,
STR_PROP_3 VARCHAR(512) NULL,
INT_PROP_1 INT NULL,
INT_PROP_2 INT NULL,
LONG_PROP_1 BIGINT NULL,
LONG_PROP_2 BIGINT NULL,
DEC_PROP_1 NUMERIC(13,4) NULL,
DEC_PROP_2 NUMERIC(13,4) NULL,
BOOL_PROP_1 VARCHAR(1) NULL,
BOOL_PROP_2 VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_BLOB_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_CALENDARS (
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME VARCHAR(200) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
ENGINE=InnoDB;
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_FIRED_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
FIRED_TIME BIGINT(13) NOT NULL,
SCHED_TIME BIGINT(13) NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(200) NULL,
JOB_GROUP VARCHAR(200) NULL,
IS_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SCHEDULER_STATE (
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
ENGINE=InnoDB;
CREATE TABLE QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;
CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
commit;
上面是2x版本的表。当然有如果你用的是1x的表是不一样的(请去找1x的说明,这篇说明不适合你,立刻就走别耽误工夫)。当时第一次用,纠结了半天。
1.如果你发现什么……column not fount。
2.某个字段未找到,或者未验证。
那么估计你是表的版本用错了,如果是2x版本请复制上面的,从官方文档上粘贴下来的。
以上是数据库建表语句,一共有11张表。但是,你并不一定用的到全部,最主要的有:
1.qrtz_job_detales我的理解管理任务的(最重要的之一)
2.qrtz_trigges管理触发器的(最重要之一)
3.qrtz_scheduler_state调度工厂(最重要之一)
4.qrtz_locks悲观锁(最重要的之一)我并不了解这个的含义但是没有不行,是控制数据线程的大概理解为(主要这个功能暂时没有用到它把这个,这个模块我只是让他跑起来,原理没有深究,因为时间有限啊!希望有大神请给我解释解释,谢谢了!)
5.qrtz_cron_trigger cron触发器表,cron表达式是我用到的定时任务里,来确定时间触发任务的(这个表不是必须的,但是这里主要演示的是这种方式其他方式的请立刻就走,这里不适合你)
6.其他表:我认为如果你是用只的CRON这种方式其他表基本用不到。
说一下包的问题,这是在spring3.0以上版本用到的quartz的包(我的是4.0的)如果你不导入各种错误,你导入错了版本各种错误(版本可能冲突请自行更正)如果你的jar里面有了请不要继续导入。
http://upload-images.jianshu.io/upload_images/154112-e9aea83024725e94.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240
配置文件。这里说一下我配置遇到的很大问题,我们这个项目是shiro+quartz的版本冲突很严重,一开始是会话session这里出现了问题,我把这里删掉将quartz不放到session没有了问题(暂时),然后因为是maven项目,spring文件和数据库文件没在一个包下导致Data source not found这个错误,解决的方法时quartz重新开个数据源,如下所示,以为我们这个是maven项目分了好多项目包,为了不重复导入文件,将quartz配置写入了spring中,如下所示:另外因为事物中也需要开启对resume读写的权限如下图所示:
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <description>定时任务调度器工厂</description> <property name="schedulerName" value="schedulerName" /> <property name="applicationContextSchedulerContextKey" value="applicationContext" /> <!-- <property name="configLocation" value="classpath:quartz.properties" /> --> <property name="dataSource" ref="dataSource" /> <property name="quartzProperties"> <props> <prop key="org.quartz.scheduler.instanceName">DefaultQuartzScheduler</prop> <prop key="org.quartz.scheduler.instanceId">AUTO</prop> <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop> <prop key="org.quartz.threadPool.threadCount">3000</prop> <prop key="org.quartz.threadPool.threadPriority">5</prop> <prop key="org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread">true</prop> <prop key="org.quartz.jobStore.useProperties">true</prop> <prop key="org.quartz.jobStore.isClustered">true</prop> <prop key="org.quartz.jobStore.misfireThreshold">60000</prop> <prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop> <prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop> <prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.StdJDBCDelegate </prop> <prop key="org.quartz.jobStore.selectWithLockSQL">SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?</prop> <prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop> <prop key="org.quartz.jobStore.dataSource">qzDS</prop> <prop key="org.quartz.dataSource.qzDS.driver">com.mysql.jdbc.Driver</prop> <prop key="org.quartz.dataSource.qzDS.URL">jdbc:mysql://127.0.0.1/bosys?</prop> <prop key="org.quartz.dataSource.qzDS.user">xxx</prop> <prop key="org.quartz.dataSource.qzDS.password">xxx</prop> </props> </property> </bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 定义事务传播属性 --> <tx:attributes> <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="edit*" propagation="REQUIRED" /> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="new*" propagation="REQUIRED" /> <tx:method name="set*" propagation="REQUIRED" /> <tx:method name="merge*" propagation="REQUIRED" /> <tx:method name="remove*" propagation="REQUIRED" /> <tx:method name="delete*" propagation="REQUIRED" /> <tx:method name="change*" propagation="REQUIRED" /> <tx:method name="do*" propagation="REQUIRED" /> <tx:method name="get*" propagation="REQUIRED" /> <tx:method name="list*" propagation="REQUIRED" /> <tx:method name="find*" propagation="REQUIRED" /> <tx:method name="load*" propagation="REQUIRED" /> <tx:method name="query*" propagation="REQUIRED" /> <tx:method name="modify*" propagation="REQUIRED" /> <tx:method name="pause*" propagation="REQUIRED" /> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice>
因为要求的原因我还多创建了一张表。taskScheduled这是它的实体类,表中主要的也就是这些。
package com.boostyoung.bosys.service.impl; import java.util.Date; import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.JobKey; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.TriggerKey; import org.redisson.api.CronSchedule; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.boostyoung.bosys.service.QuartzService; @Service("quartzService") public class QuartzServiceImpl implements QuartzService{ @Autowired private Scheduler quartzScheduler; @Override public void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName,String cron) { try { //获获取调度器 Scheduler sched=quartzScheduler; //指定要执行实例化的job工厂 String c="com.boostyoung.bosys.service.impl.TimeTask"; Class cls=Class.forName(c); //创建一项作业 JobDetail job=JobBuilder.newJob(cls).withIdentity(jobName, jobGroupName).build(); // CronTrigger trigger=TriggerBuilder.newTrigger().withIdentity(triggerName, triggerGroupName) .withSchedule(CronScheduleBuilder.cronSchedule(cron)).build(); // 告诉调度器使用该触发器来安排作业 sched.scheduleJob(job,trigger); if(!sched.isShutdown()){ sched.start(); } } catch (SchedulerException | ClassNotFoundException e) { String s="定时器出错了-------------------------------------------------"; throw new RuntimeException(e+s); } } @Override public void modifyJobTime(String triggerName, String triggerGroupName, String time) { try { Scheduler sched = quartzScheduler; CronTrigger trigger = (CronTrigger) sched.getTrigger(TriggerKey .triggerKey(triggerGroupName, triggerGroupName)); if (trigger == null) { return ; } JobKey jobKey = JobKey.jobKey(triggerGroupName, triggerGroupName); TriggerKey triggerKey = TriggerKey.triggerKey(triggerGroupName, triggerGroupName); JobDetail job = sched.getJobDetail(jobKey); Class jobClass = job.getJobClass(); // 停止触发器 sched.pauseTrigger(triggerKey); // 移除触发器 sched.unscheduleJob(triggerKey); // 删除任务 sched.deleteJob(jobKey); addJob(triggerGroupName,triggerGroupName, triggerName,triggerGroupName, time); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) { try { Scheduler sched = quartzScheduler; // 停止触发器 sched.pauseTrigger(TriggerKey.triggerKey(triggerName, triggerGroupName)); // 移除触发器 sched.unscheduleJob(TriggerKey.triggerKey(triggerName, triggerGroupName)); // 删除任务 sched.deleteJob(JobKey.jobKey(jobName, jobGroupName)); } catch (Exception e) { throw new RuntimeException(e); } } /* * 暂停任务 */ @Override public void pauseJob(String jobName, String jobGroupName) { try { quartzScheduler.pauseJob(JobKey.jobKey(jobName, jobGroupName)); } catch (SchedulerException e) { e.printStackTrace(); } } /* * 恢复任务 */ @Override public void resumeJob(String jobName, String jobGroupName) { try { quartzScheduler.resumeJob(JobKey.jobKey(jobName, jobGroupName)); } catch (SchedulerException e) { e.printStackTrace(); } } /* * 立刻开始触发器 */ @Override public void runJob(String jobName,String jobGroupName) { try { quartzScheduler.triggerJob(JobKey.jobKey(jobName, jobGroupName)); } catch (Exception e) { e.printStackTrace(); } } }
调度工厂这个是核心中的核心吧,主要通过反射实例化类中方法,反射不多bb了吧最强大的机制之一(虽然我了解的不是很深。。。。。无奈)。
里面的注释部分当时我打算直接通过service直接操作数据库,但是经理说一般项目都是直接调用空参类型方法,里面怎么用需要自己去配参数,所以注释下面那个直接调用某个包里面的空参方法只是简单的每隔5秒打印出一个字符串。当然你可以自己调用复杂的方法,只要是空参就行。如果不是你需要自行配参数,就是getDeclaredMethod(tm,Interget.class)这样就能调用参数使int类型或者Integer方法了。即使是空参方法执行的功能也有很多需要你自己去写。
package com.boostyoung.bosys.service.impl; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.context.ApplicationContext; import com.boostyoung.bosys.entity.TaskScheduledEntity; import com.boostyoung.bosys.service.TaskScheduledService; public class TimeTask implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { /*System.out.println("sssssssssssssssssssssssssssssssssssssssssssssssssssss"); String tgn2=context.getTrigger().getKey().getName(); String tgnss=context.getJobDetail().getKey().getName(); System.out.println(tgn2+tgnss); System.out.println("sssssssssssssssssssssssssssssssssssssssssssssssssssss");*/ ApplicationContext applicationContext=null; try{ applicationContext=getApplicationContext(context); }catch(Exception e){ e.printStackTrace(); } try { TaskScheduledService tss=(TaskScheduledService)applicationContext.getBean("taskScheduledService"); String tgn=context.getTrigger().getKey().getName(); String jgn=context.getJobDetail().getKey().getName(); TaskScheduledEntity tse= tss.findByTriggerName(tgn); if(tse==null){ //立即执行的时候随机赋予了一个触发器名所以的不到真正的触发器名称,用jobDetile名称代替寻找 tse=tss.findByTriggerName(jgn); } String to= tse.getTargetObject(); String tm=tse.getTargetMethod(); System.out.println(tm); /* Object o=applicationContext.getBean(to); if(o!=null){ System.out.println(to); o.getClass().getDeclaredMethod(tm).invoke(o); }else if(o==null){*/ Date d=new Date(); DateFormat df=new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); String s=df.format(d); tse.setLastTime(s); tss.update(tse); Object o2=Class.forName(to).newInstance(); o2.getClass().getDeclaredMethod(tm).invoke(o2); } catch (Exception e) { System.out.println("执行定时器的时候出错啦!"); } } private static final String APPLICATION_CONTEXT_KEY = "applicationContext"; private ApplicationContext getApplicationContext(JobExecutionContext context) throws Exception { ApplicationContext appCtx = null; appCtx = (ApplicationContext) context.getScheduler().getContext().get(APPLICATION_CONTEXT_KEY); if (appCtx == null) { throw new JobExecutionException("No application context available in scheduler context for key \"" + APPLICATION_CONTEXT_KEY + "\""); } return appCtx; } }
Xml因为是动态调用有上面那个类会加到数据库中管理
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.boostyoung.bosys.DAO.TaskScheduledDao"> <resultMap type="com.boostyoung.bosys.entity.TaskScheduledEntity" id="TaskScheduledMapping"> <id property="id" column="Id"/> <result property="taskName" column="TASK_NAME"/> <result property="triggerName" column="TRIGGER_NAME"/> <result property="taskCron" column="TASK_CRON"/> <result property="previousFireTime" column="PREVIOUS_FIRE_TIME"/> <result property="taskDesc" column="TASK_DESC"/> <result property="targetObject" column="TARGET_OBJECT"/> <result property="targetMethod" column="TARGET_METHOD"/> <result property="targetId" column="TARGET_ID"/> <result property="lastTime" column="LAST_TIME"/> <result property="status" column="STATUS"/> </resultMap> <sql id="taskScheduledInfo"> <![CDATA[ SELECT TS.ID,TS.TASK_NAME,TS.TRIGGER_NAME,TS.TASK_CRON,TS.PREVIOUS_FIRE_TIME, TS.LAST_TIME,TS.TASK_DESC,TS.TARGET_OBJECT,TS.TARGET_METHOD,TS.TARGET_ID,TS.STATUS FROM BOSYS_TASKSCHEDULED TS WHERE TS.DELETED=0 ]]> </sql> <insert id="add"> INSERT INTO BOSYS_TASKSCHEDULED(ID,TASK_NAME,TRIGGER_NAME,TASK_CRON,PREVIOUS_FIRE_TIME,LAST_TIME,TASK_DESC,TARGET_OBJECT,TARGET_METHOD,TARGET_ID,STATUS) VALUES(#{id},#{taskName},#{triggerName},#{taskCron},#{previousFireTime},#{lastTime},#{taskDesc},#{targetObject},#{targetMethod},#{targetId},#{status}) </insert> <update id="update"> <if test="id!=null"> UPDATE BOSYS_TASKSCHEDULED SET TASK_NAME=#{taskName},TRIGGER_NAME=#{triggerName},TASK_CRON=#{taskCron},PREVIOUS_FIRE_TIME=#{previousFireTime},LAST_TIME=#{lastTime}, TASK_DESC=#{taskDesc},TARGET_OBJECT=#{targetObject},TARGET_METHOD=#{targetMethod},TARGET_ID=#{targetId},STATUS=#{status} WHERE ID=#{id} </if> </update> <select id="findByTriggerName" parameterType="String" resultMap="TaskScheduledMapping"> <include refid="taskScheduledInfo"/> AND TS.TRIGGER_NAME=#{triggerName} </select> <select id="findByTaskName" parameterType="String" resultMap="TaskScheduledMapping"> <include refid="taskScheduledInfo"/> AND TS.TASK_NAME=#{taskName} </select> <select id="findById" parameterType="Long" resultMap="TaskScheduledMapping"> <include refid="taskScheduledInfo" /> AND TS.ID = #{id} </select> <select id="get" parameterType="Long" resultMap="TaskScheduledMapping"> <include refid="taskScheduledInfo" /> AND TS.ID = #{id} </select> <select id="findAll" resultMap="TaskScheduledMapping"> <include refid="taskScheduledInfo" /> <![CDATA[ ORDER BY TS.ID ]]> </select> <select id="query" parameterType="java.util.HashMap" resultMap="TaskScheduledMapping"> <include refid="taskScheduledInfo"></include> </select> <update id="delete"> UPDATE BOSYS_TASKSCHEDULED SET DELETED=1,DELETE_TIME=now() WHERE ID=#{id} </update> </mapper>
DAO层
package com.boostyoung.bosys.DAO; import java.util.List; import com.boostyoung.bosys.entity.TaskScheduledEntity; public interface TaskScheduledDao extends BaseDAO<TaskScheduledEntity>{ public TaskScheduledEntity findByTriggerName(String triggerName) throws Exception; public List<TaskScheduledEntity> findAll()throws Exception; public void deleteByTaskScheduledId(Long TaskScheduledId) throws Exception; public TaskScheduledEntity findByTaskName(String taskName)throws Exception; }
Service层“
package com.boostyoung.bosys.service; import java.util.List; import com.boostyoung.bosys.common.exception.ServiceException; import com.boostyoung.bosys.entity.TaskScheduledEntity; public interface TaskScheduledService extends BaseService<TaskScheduledEntity> { public TaskScheduledEntity findByTriggerName (String triggerName) throws ServiceException; public List<TaskScheduledEntity> findAll() throws ServiceException; public TaskScheduledEntity findByTaskName (String taskName) throws ServiceException; }
ServiceImpl
package com.boostyoung.bosys.service.impl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.boostyoung.bosys.DAO.BaseDAO; import com.boostyoung.bosys.DAO.TaskScheduledDao; import com.boostyoung.bosys.common.exception.ServiceException; import com.boostyoung.bosys.entity.TaskScheduledEntity; import com.boostyoung.bosys.service.TaskScheduledService; @Service("taskScheduledService") public class TaskScheduledServiceImpl extends BaseServiceImpl<TaskScheduledEntity> implements TaskScheduledService { private static final Log logger = LogFactory.getLog(TaskScheduledServiceImpl.class); @Autowired private TaskScheduledDao taskSchduledDao; @Override public TaskScheduledEntity findByTriggerName(String triggerName) throws ServiceException { logger.info("根据触发器名称查询任务触发器信息triggerName="+triggerName); TaskScheduledEntity taskScheduled=null; try{ taskScheduled=taskSchduledDao.findByTriggerName(triggerName); }catch(Exception e){ logger.error("查询任务触发器失败",e); throw new ServiceException(e); } return taskScheduled; } @Override public TaskScheduledEntity findByTaskName(String taskName) throws ServiceException { logger.info("根据任务名称查询任务信息taskName="+taskName); TaskScheduledEntity taskScheduled=null; try { taskScheduled=taskSchduledDao.findByTaskName(taskName); } catch (Exception e) { logger.error("查询任务名称失败",e); throw new ServiceException(e); } return null; } @Override public BaseDAO<TaskScheduledEntity> getDao() { return taskSchduledDao; } }
结束语:没有现成的代码项目,因为写在总项目里了。但是核心贴出来了,欢迎大家指正,共同进步。现在十点半了,很累.再见!
还会有其他的总结,一路坚持吧!
相关文章推荐
- Spring动态对Quartz定时任务的管理,实现动态加载,停止的配置实例代码
- Quartz动态管理一次性定时任务(Spring)
- Quartz+Spring Boot实现动态管理定时任务
- [置顶] spring整合quartz实现动态定时任务的前台网页配置与管理
- [整理]在Spring MVC中使用Quartz实现定时任务动态管理
- Spring动态对Quartz定时任务的管理,实现动态加载,停止的配置实例代码
- Spring 动态管理定时任务(使用quartz) 只是管理启动时间 不能做启动和暂停
- spring mvc quartz 实现动态定时任务管理
- Spring动态对Quartz定时任务的管理,实现动态加载,停止的配置实例代码
- Spring动态对Quartz定时任务的管理,实现动态加载,停止的配置实例代码
- Quartz在Spring中动态设置cronExpression研究(spring设置动态定时任务)
- 动态定义quartz定时任务
- 关于学习SpringMvc整合QuartZ定时管理任务的初步整理
- Quartz在Spring中动态设置cronExpression (spring设置动态定时任务)------转帖
- Spring整合quartz2.2.3总结,quartz动态定时任务,Quartz定时任务集群配置
- Quartz在Spring中设置动态定时任务 .
- 任务调度开源框架Quartz动态添加、修改和删除定时任务
- Spring 3整合Quartz 2实现定时任务二:动态添加任务
- Spring 和Quartz2 整合实现动态定时任务
- 任务调度开源框架Quartz动态添加、修改和删除定时任务