Java SpringBoot 中,动态执行 bean 对象中的方法
源代码地址 => https://gitee.com/VipSoft/VipBoot/tree/develop/vipsoft-quartz
工作原理解读
只要配置好 DataSource Quartz 会自动进行表的数据操作,
添加 Quartz Job 任务
保存 QRTZ_JOB_DETAILS、QRTZ_TRIGGERS => QRTZ_CRON_TRIGGERS
public void addJob(QuartzJob job) throws SchedulerException { .... JobDetail jobDetail = JobBuilder.newJob(jobClass) .withIdentity(jobKey) .build(); // 放入参数,运行时的方法可以获取 jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job); //该行代码执行后,会将定时任务插入 QRTZ_JOB_DETAILS 等相关表 scheduler.scheduleJob(jobDetail, trigger); .... }
//org.quartz.impl.jdbcjobstore.JobStoreSupport public void storeJobAndTrigger(final JobDetail newJob, final OperableTrigger newTrigger) throws JobPersistenceException { this.executeInLock(this.isLockOnInsert() ? "TRIGGER_ACCESS" : null, new JobStoreSupport.VoidTransactionCallback() { public void executeVoid(Connection conn) throws JobPersistenceException { JobStoreSupport.this.storeJob(conn, newJob, false); //数据保存 QRTZ_JOB_DETAILS 表 JobStoreSupport.this.storeTrigger(conn, newTrigger, newJob, false, "WAITING", false, false); //数据保存 QRTZ_TRIGGERS 表 } }); } public int insertTrigger(...){ INSERT_TRIGGER insertExtendedTriggerProperties => INSERT_CRON_TRIGGER OR INSERT_BLOB_TRIGGER }
详见:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
将 job.getJobDataMap(),对像序列化后,存入 JOB_DETAILS.JOB_DATA
字段,可以是一个对像,以执行定时任务时,会把该字段反序列化,根据前期设定的内容进行业务处理
获取 Quartz Job 任务
执行计划任务时,获取 Job Detail
QuartzSchedulerThread.run() => qsRsrcs.getJobStore().acquireNextTriggers() => txCallback.execute(conn) => JobStoreSupport.acquireNextTriggers() => JobStoreSupport.retrieveJob() => StdJDBCDelegate.selectJobDetail()
删除 Quartz Job 任务
/** * <p> * Delete the base trigger data for a trigger. * </p> * * @param conn * the DB Connection * @return the number of rows deleted */ public int deleteTrigger(Connection conn, TriggerKey triggerKey) throws SQLException { PreparedStatement ps = null; deleteTriggerExtension(conn, triggerKey); try { ps = conn.prepareStatement(rtp(DELETE_TRIGGER)); ps.setString(1, triggerKey.getName()); ps.setString(2, triggerKey.getGroup()); return ps.executeUpdate(); } finally { closeStatement(ps); } }
清除数据
/** * 清任务顺序 */ public void clearData(Connection conn) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_ALL_SIMPLE_TRIGGERS)); ps.executeUpdate(); ps.close(); ps = conn.prepareStatement(rtp(DELETE_ALL_SIMPROP_TRIGGERS)); ps.executeUpdate(); ps.close(); ps = conn.prepareStatement(rtp(DELETE_ALL_CRON_TRIGGERS)); ps.executeUpdate(); ps.close(); ps = conn.prepareStatement(rtp(DELETE_ALL_BLOB_TRIGGERS)); ps.executeUpdate(); ps.close(); ps = conn.prepareStatement(rtp(DELETE_ALL_TRIGGERS)); ps.executeUpdate(); ps.close(); ps = conn.prepareStatement(rtp(DELETE_ALL_JOB_DETAILS)); ps.executeUpdate(); ps.close(); ps = conn.prepareStatement(rtp(DELETE_ALL_CALENDARS)); ps.executeUpdate(); ps.close(); ps = conn.prepareStatement(rtp(DELETE_ALL_PAUSED_TRIGGER_GRPS)); ps.executeUpdate(); } finally { closeStatement(ps); } }
Demo 代码
MySQL 脚本
清除数据
DELETE FROM qrtz_simple_triggers ; DELETE FROM qrtz_simprop_triggers ; DELETE FROM qrtz_cron_triggers ; DELETE FROM qrtz_blob_triggers ; DELETE FROM qrtz_triggers ; DELETE FROM qrtz_job_details ; DELETE FROM qrtz_calendars ; DELETE FROM qrtz_paused_trigger_grps ; DELETE FROM qrtz_scheduler_state ; DELETE FROM qrtz_locks ; DELETE FROM qrtz_fired_triggers
Pom.xml
如果SpringBoot版本是2.0.0以后的,则在spring-boot-starter中已经包含了quart的依赖,则可以直接使用spring-boot-starter-quartz依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> <!--Quartz 集成需要和数据库交互--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.0.8</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.20</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <!--hutool 工具类--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.3.7</version> </dependency>
QuartzJob 参考上图,建立实体
点击查看代码
核心代码:QuartzJobServiceImpl
点击查看代码
ScheduleConfig
点击查看代码
暂停后,qrtz_triggers 表的 TRIGGER_STATE = PAUSED
运行效果
http://localhost:8088/schedule/add
http://localhost:8088/schedule/pause
http://localhost:8088/schedule/restart
{"jobName":"测试","jobGroup":"DEFAULT","invokeTarget":"scheduletask.execute('VipSoft Quartz')","cronExpression":"0/10 * * * * ?","misfirePolicy":2,"concurrent":1,"status":"0"}
参考
实现方式参考:若依(RuoYi),他是新建了一张中间表,通过 init() 方法,利用中心中间表进行定时任务的初始化
/** * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据) */ @PostConstruct public void init() throws SchedulerException, TaskException { scheduler.clear(); List<SysJob> jobList = jobMapper.selectJobAll(); for (SysJob job : jobList) { ScheduleUtils.createScheduleJob(scheduler, job); } }