springboot創建的web項目整合Quartz框架的項目實踐
介紹
Quartz是OpenSymphony開源組織在Job scheduling領域又一個開源項目,它可以與J2EE與J2SE應用程序相結合也可以單獨使用。Quartz可以用來創建簡單或為運行十個,百個,甚至是好幾萬個Jobs這樣復雜的程序。Jobs可以做成標準的Java組件或EJBs。Quartz的最新版本為Quartz 2.3.2。
quartz可以在某一個有規律的時間點幹某件事,是一個任務調度框架,可以被集成到java的各種應用中使用,quartz框架可以支持分佈式任務處理
基於springboot創建的web項目整合Quartz框架
整體代碼結構
導入依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
創建quartz_demo數據庫,執行tables_mysql_innodb.sql文件創建對應表,下面是文件內容
# # In your Quartz properties file, you'll need to set # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate # # # By: Ron Cordell - roncordell # I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM. 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(190) NOT NULL, JOB_GROUP VARCHAR(190) 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(190) NOT NULL, TRIGGER_GROUP VARCHAR(190) NOT NULL, JOB_NAME VARCHAR(190) NOT NULL, JOB_GROUP VARCHAR(190) 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(190) 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(190) NOT NULL, TRIGGER_GROUP VARCHAR(190) 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(190) NOT NULL, TRIGGER_GROUP VARCHAR(190) NOT NULL, CRON_EXPRESSION VARCHAR(120) NOT NULL, TIME_ZONE_ID VARCHAR(80), 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_SIMPROP_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(190) NOT NULL, TRIGGER_GROUP VARCHAR(190) 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(190) NOT NULL, TRIGGER_GROUP VARCHAR(190) 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(190) 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(190) 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(190) NOT NULL, TRIGGER_GROUP VARCHAR(190) NOT NULL, INSTANCE_NAME VARCHAR(190) 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(190) NULL, JOB_GROUP VARCHAR(190) 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(190) 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;
配置quartz
server: port: ${PORT:12011} servlet: session: timeout: 86400s # session有效期(xxx秒) cookie: http-only: false # 如果為true, 瀏覽器腳本將無法訪問cookie secure: false # 如果為true, 則僅通過https連接發送cookie, http無法攜帶cookie context-path: # 程序上下文路徑配置 spring: application: name: quartz-demo aop: proxy-target-class: true auto: true datasource: url: jdbc:mysql://localhost:3306/quartz_demo username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver # 配置日期格式化 jackson: date-format: yyyy-MM-dd HH:mm:ss #時間戳統一轉換為指定格式 time-zone: GMT+8 # 時區修改為東8區 quartz: # 將任務等保存化到數據庫 job-store-type: jdbc # 程序結束時會等待quartz相關的內容結束 wait-for-jobs-to-complete-on-shutdown: true # QuartzScheduler啟動時更新己存在的Job,這樣就不用每次修改targetObject後刪除qrtz_job_details表對應記錄 overwrite-existing-jobs: true properties: org: quartz: # scheduler相關 scheduler: # scheduler的實例名 instanceName: scheduler instanceId: AUTO # 持久化相關 jobStore: class: org.quartz.impl.jdbcjobstore.JobStoreTX driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate # 表示數據庫中相關表是QRTZ_開頭的 tablePrefix: QRTZ_ useProperties: false # 配置集群 # 是否加入集群 isClustered: true # 容許的最大作業延長時間 clusterCheckinInterval: 20000 # 線程池相關 threadPool: class: org.quartz.simpl.SimpleThreadPool # 線程數 threadCount: 10 # 線程優先級 threadPriority: 5 threadsInheritContextClassLoaderOfInitializingThread: true mybatis-plus: global-config: db-config: logic-delete-field: is_deleted logic-delete-value: 1 logic-not-delete-value: 0 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl logging: level: com.my.quartz: debug
添加sys_job表
CREATE TABLE `sys_job` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `job_id` varchar(100) DEFAULT NULL, `job_name` varchar(255) DEFAULT NULL, `job_group_name` varchar(255) DEFAULT NULL, `invoke_target` varchar(255) NOT NULL, `cron_expression` varchar(50) DEFAULT NULL, `misfire_policy` varchar(255) DEFAULT NULL COMMENT 'cron計劃策略0=默認,1=立即觸發執行,2=觸發一次執行,3=不觸發立即執行', `status` varchar(10) DEFAULT NULL COMMENT '任務狀態(0正常 1暫停)', `concurrent` varchar(10) DEFAULT NULL COMMENT '是否並發執行(0允許 1禁止)', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
寫兩條數據
依次實現mvc三層
mapper層的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.my.quartz.boot.mapper.SysJobMapper"> <resultMap id="BaseResultMap" type="com.my.quartz.boot.SysJob"> <id column="id" jdbcType="BIGINT" property="id" /> <result column="job_id" jdbcType="VARCHAR" property="jobId" /> <result column="job_name" jdbcType="VARCHAR" property="jobName" /> <result column="job_group_name" jdbcType="VARCHAR" property="jobGroupName" /> <result column="invoke_target" jdbcType="VARCHAR" property="invokeTarget" /> <result column="cron_expression" jdbcType="VARCHAR" property="cronExpression" /> <result column="misfire_policy" jdbcType="VARCHAR" property="misfirePolicy" /> <result column="status" jdbcType="VARCHAR" property="status" /> <result column="concurrent" jdbcType="VARCHAR" property="concurrent" /> </resultMap> <sql id="Base_Column_List"> t.id, t.job_id jobId, t.job_name jobName, t.job_group_name jobGroupName, t.invoke_target invokeTarget, t.cron_expression cronExpression, t.misfire_policy misfirePolicy, t.status, t.concurrent </sql> </mapper>
mapper接口
/** * Auto Generated * * @author :Boy * @date :Created in 2022-06-18 * @description: * @modified By: */ public interface SysJobMapper extends BaseMapper<SysJob> { }
service接口
public interface SysJobService extends IService<SysJob> { }
service接口實現
@Service public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements SysJobService { @Autowired private Scheduler scheduler; @Autowired private ScheduleUtils scheduleUtils; /** * 項目啟動時,初始化定時器 * 主要是防止手動修改數據庫導致未同步到定時任務處理(註:不能手動修改數據庫ID和任務組名,否則會導致臟數據) */ @PostConstruct public void init() throws SchedulerException, TaskException { scheduler.clear(); List<SysJob> jobList = baseMapper.selectList(null); for (SysJob job : jobList) { scheduleUtils.createScheduleJob(scheduler, job); } } }
controller代碼
@RestController @RequestMapping("/job") public class SysJobController { @Resource private SysJobService sysJobService; @Resource private ScheduleUtils scheduleUtils; @Resource private Scheduler scheduler; @PostMapping("/add") public Object add(@RequestBody SysJob entity) throws SchedulerException, TaskException { sysJobService.save(entity); scheduleUtils.createScheduleJob(scheduler, entity); return "ok"; } }
ScheduleUtils 定時任務工具類
/** * @Author ScholarTang * @Date 2021/7/15 下午3:50 * @Desc 定時任務工具類 */ @Component public class ScheduleUtils { @Autowired private Scheduler scheduler; /** * 構建任務觸發對象 * * @param jobName * @param jobGroup * @return */ public TriggerKey getTriggerKey(String jobName, String jobGroup) { return TriggerKey.triggerKey(jobName, jobGroup); } /** * 構建任務鍵對象 * * @param jobName * @param jobGroup * @return */ public JobKey getJobKey(String jobName, String jobGroup) { return JobKey.jobKey(jobName, jobGroup); } /** * 創建定時調度任務 * * * @param scheduler * @param job * @throws SchedulerException * @throws TaskException */ public void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException { // 構建job信息 String jobName = job.getJobId() + "_" + job.getJobName(); String jobGroupName = job.getJobGroupName(); //TODO 反射動態獲取Job實現類 //構建job實例 JobDetail jobDetail = JobBuilder.newJob(QuartzJobImpl.class) .withIdentity(getJobKey(jobName, jobGroupName)) .build(); jobDetail.getJobDataMap().put("QuartzJobExecutionData", job); // 表達式調度構建器 CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder); // 按新的cronExpression表達式構建一個新的trigger CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobName, jobGroupName)) .withSchedule(cronScheduleBuilder).build(); // 放入參數,運行時的方法可以獲取 jobDetail.getJobDataMap().put("QuartzJobExecutionData", job); // 判斷是否存在 if (scheduler.checkExists(getJobKey(jobName, jobGroupName))) { // 防止創建時存在數據問題 先移除,然後在執行創建操作 scheduler.deleteJob(getJobKey(jobName, jobGroupName)); } //創建定時任務調度 scheduler.scheduleJob(jobDetail, trigger); // 暫停任務 規定 0啟動 1暫停 if (job.getStatus().equals("1")) { scheduler.pauseJob(getJobKey(jobName, jobGroupName)); } } /** * 刪除定時調度任務 * * @param sysJob * @throws SchedulerException */ public void deleteScheduleJob(SysJob sysJob) throws SchedulerException { scheduler.deleteJob(getJobKey(sysJob.getJobId() + "_" + sysJob.getJobName(), sysJob.getJobGroupName())); } /** * 設置定時任務策略 * * @param sysJob * @param cronScheduleBuilder * @return */ public CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob sysJob, CronScheduleBuilder cronScheduleBuilder) throws TaskException { switch (sysJob.getMisfirePolicy()) { case "0": return cronScheduleBuilder; case "1": return cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires(); case "2": return cronScheduleBuilder.withMisfireHandlingInstructionFireAndProceed(); case "3": return cronScheduleBuilder.withMisfireHandlingInstructionDoNothing(); default: throw new TaskException("任務失敗策略 '" + sysJob.getMisfirePolicy() + "' 不能在cron計劃任務中使用", TaskException.Code.CONFIG_ERROR); } } }
創建一個Job實現類,隻是一個簡單實現類,沒有對方法添加參數
@Slf4j @Component public class QuartzJobImpl implements Job { @Autowired private ApplicationContext context; @SneakyThrows @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { SysJob sysJob = new SysJob(); BeanUtils.copyProperties(jobExecutionContext.getMergedJobDataMap().get("QuartzJobExecutionData"), sysJob); log.info("正在執行任務"); String invokeTarget = sysJob.getInvokeTarget(); String beanName = invokeTarget.split("\\.")[0]; String methodName = invokeTarget.split("\\.")[1]; Object bean = context.getBean(beanName); Class<?> clazz = bean.getClass(); Method method = clazz.getMethod(methodName); method.invoke(bean); log.info("已結束任務"); System.out.println(); } }
創建最終任務執行類,執行函數級別的定時任務,數據庫中的invoke_target是和這裡放入spring中的名稱相同的,quartz_target.hello最終執行的函數就是hello函數
@Slf4j @Component("quartz_target") public class QuartzJobExe { @Resource private Scheduler scheduler; public void hello() throws Exception { System.out.println("[job]實例執行...hello..." + System.currentTimeMillis()); } public void action() throws Exception { System.out.println("[job]實例執行...action..." + System.currentTimeMillis()); } }
最終代碼結構
啟動spring boot之後的任務狀態
使用postman工具類添加一個任務
然後在代碼中添加一個對應的方法
重新啟動springboot的運行結果
理論上使用這樣的反射方式可以運行任何代碼的
到此這篇關於springboot創建的web項目整合Quartz框架的項目實踐的文章就介紹到這瞭,更多相關springboot整合Quartz框架內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- SpringBoot2.6.3集成quartz的方式
- springboot整合quartz定時任務框架的完整步驟
- SpringBoot集成quartz實現定時任務詳解
- SpringBoot實現quartz定時任務可視化管理功能
- Spring Boot 配置 Quartz 定時任務的方法