SpringBoot系列---【SpringBoot集成定时任务框架Quartz】
1.引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
2.新增配置
server: port: 8082 #优雅停机 shutdown: graceful spring: application: name: fast-boot quartz: job-store-type: jdbc datasource: # driver-class-name: com.p6spy.engine.spy.P6SpyDriver # url: jdbc:p6spy:mysql://101.43.140.7:3307/home-finance?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://101.43.140.7:3307/home-finance?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&characterEncoding=utf-8 username: root password: root # druid: # initial-size: 5 #初始化连接大小 # min-idle: 5 #最小连接池数量 # max-active: 20 #最大连接池数量 # max-wait: 60000 #获取连接时最大等待时间,单位毫秒 # time-between-eviction-runs-millis: 60000 #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 # min-evictable-idle-time-millis: 300000 #配置一个连接在池中最小生存的时间,单位是毫秒 # validation-query: SELECT 1 FROM DUAL #测试连接 # test-while-idle: true #申请连接的时候检测,建议配置为true,不影响性能,并且保证安全性 # test-on-borrow: false #获取连接时执行检测,建议关闭,影响性能 # test-on-return: false #归还连接时执行检测,建议关闭,影响性能 # pool-prepared-statements: false #是否开启PSCache,PSCache对支持游标的数据库性能提升巨大,oracle建议开启,mysql下建议关闭 # max-pool-prepared-statement-per-connection-size: 20 #开启poolPreparedStatements后生效 # filters: stat,wall #配置扩展插件,常用的插件有=>stat:监控统计 wall:防御sql注入 # connection-properties: 'druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000' #通过connectProperties属性来打开mergeSql功能;慢SQL记录 # # # StatViewServlet配置,说明请参考Druid Wiki,配置_StatViewServlet配置 # stat-view-servlet: # enabled: true #是否开启内置监控页面,默认值为 false # url-pattern: '/druid/*' #StatViewServlet 的映射路径,即内置监控页面的访问地址 # reset-enable: true #是否启用重置按钮 # login-username: admin #内置监控页面的登录页用户名 username # login-password: admin #内置监控页面的登录页密码 password # # WebStatFilter配置,说明请参考Druid Wiki,配置_配置WebStatFilter # web-stat-filter: # enabled: true #是否开启内置监控中的 Web-jdbc 关联监控的数据 # url-pattern: '/*' #匹配路径 # exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' #排除路径 # session-stat-enable: true #是否监控session # # Spring监控配置,说明请参考Druid Github Wiki,配置_Druid和Spring关联监控配置 # aop-patterns: com.fast.dao.*,com.fast.mapper #Spring监控AOP切入点,如x.y.z.abc.*,配置多个英文逗号分隔 # # # 对配置已开启的 filters 即 stat(sql 监控) wall(防火墙) # filter: # #配置StatFilter (SQL监控配置) # stat: # enabled: true #开启 SQL 监控 # slow-sql-millis: 1000 #慢查询 # log-slow-sql: true #记录慢查询 SQL # #配置WallFilter (防火墙配置) # wall: # enabled: true #开启防火墙 # config: # update-allow: true #允许更新操作 # drop-table-allow: false #禁止删表操作 # insert-allow: true #允许插入操作 # delete-allow: true #删除数据操作 knife4j: enable: true # production: true #热部署模块 #debug: false ##设置开启热部署 #spring.devtools.restart.enabled: true ##页面不加载缓存,修改即时生效 #spring.freemarker.cache: false ##设置修改文件后需要重启的目录 #spring.devtools.restart.additional-paths: src/main/java #logging: # com.fast.common.config: classpath:log4j2.xml mybatis-plus: mapper-locations: classpath:com/fast/mapper/*.xml type-aliases-package: com.fast.entity.pojo # configuration: # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3.编写接口
package com.fast.controller; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fast.common.result.common.Constant; import com.fast.common.result.common.R; import com.fast.entity.param.SysJobParam; import com.fast.entity.pojo.SysJob; import com.fast.entity.vo.SysJobVo; import com.fast.service.SysJobService; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.quartz.*; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.time.LocalDateTime; /** * (SysJob)表控制层 * * @author hjw * @since 2022-03-30 17:47:52 */ @RestController @Api(tags = "定时任务管理") @Slf4j @RequestMapping("sysJob") public class SysJobController extends BaseController { @Resource private SysJobService sysJobService; @Resource private Scheduler scheduler; @ApiOperation(value = "条件分页查询") @PostMapping("/getAll") public R<Page<SysJobVo>> getAll(@RequestBody SysJobParam param) { return select(this.sysJobService.getAll(buildPage(param), param)); } @ApiOperation(value = "根据id查") @GetMapping("/getDetailById/{id}") public R<SysJob> selectOne(@PathVariable("id") Long id) { return select(this.sysJobService.getById(id)); } @ApiOperationSupport(ignoreParameters = {"sysJob.id", "sysJob.status"}) @ApiOperation(value = "添加") @PostMapping("/add") public R<Boolean> insert(@RequestBody SysJob sysJob) { return add(this.sysJobService.save(sysJob)); } @ApiOperation(value = "更新") @PutMapping("/update") public R<Boolean> update(@RequestBody SysJob sysJob) throws SchedulerException { //获取修改前的任务组名,停止触发器,再进行移除旧的任务 SysJob oldSysJob = sysJobService.getById(sysJob.getId()); if (StrUtil.isNotBlank(oldSysJob.getJobgroup())) { TriggerKey triggerKey = TriggerKey.triggerKey(oldSysJob.getName(), oldSysJob.getJobgroup()); //停止触发器 scheduler.pauseTrigger(triggerKey); //移除触发器 scheduler.unscheduleJob(triggerKey); JobKey jobKey = new JobKey(oldSysJob.getName(), oldSysJob.getJobgroup()); //删除任务 scheduler.deleteJob(jobKey); } sysJob.setStatus(Constant.JOB_STATUS_STOP); sysJob.setUpdateTime(LocalDateTime.now()); sysJob.setUpdateBy("管理员"); return update(this.sysJobService.updateById(sysJob)); } @ApiOperation(value = "删除定时任务") @DeleteMapping("/del/{id}") public R<Boolean> delete(@PathVariable("id") Long id) { SysJob sysJob = sysJobService.getById(id); if (sysJob == null) { return R.fail("要删除的任务不存在,请检查id"); } TriggerKey triggerKey = TriggerKey.triggerKey(sysJob.getName(), sysJob.getJobgroup()); try { //停止触发器 scheduler.pauseTrigger(triggerKey); //移除触发器 scheduler.unscheduleJob(triggerKey); //删除任务 scheduler.deleteJob(JobKey.jobKey(sysJob.getName(), sysJob.getJobgroup())); } catch (Exception e) { return R.fail(e.getMessage()); } sysJob.setDel(Constant.DEL_DELETED); return delete(sysJobService.updateById(sysJob)); } @ApiOperation(value = "启动定时任务") @PostMapping("/start") public R<Boolean> start(Long id) throws Exception { SysJob sysJob = sysJobService.getById(id); if (sysJob.getStatus().equals(Constant.JOB_STATUS_RUN)) { return R.fail("任务正在执行"); } else if (sysJob.getStatus().equals(Constant.JOB_STATUS_STOP)) { return R.fail("任务处于停止状态,只能继续执行"); } //启动任务 addJob(sysJob); sysJob.setStatus(Constant.JOB_STATUS_RUN); sysJobService.updateById(sysJob); return R.success("启动成功"); } public void addJob(SysJob sysJob) throws Exception { Class cls = Class.forName(sysJob.getInvoketarget()); cls.newInstance(); //构建JobDetail JobDetail jobDetail = JobBuilder.newJob(cls) .withIdentity(sysJob.getName(), sysJob.getJobgroup()) .withDescription(sysJob.getRemark()) .build(); //执行时间 CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(sysJob.getCronexpression()); //构建trigger,触发器 Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger" + sysJob.getName(), sysJob.getJobgroup()) .withDescription(sysJob.getRemark()) .startNow() .withSchedule(cronScheduleBuilder) .build(); //交由调度器调度 scheduler.scheduleJob(jobDetail, trigger); } @ApiOperation(value = "停止任务") @PostMapping("/pause") public R<Boolean> pause(Long id) throws Exception { SysJob sysJob = sysJobService.getById(id); if (sysJob.getStatus().equals(Constant.JOB_STATUS_RUN)) { JobKey key = new JobKey(sysJob.getName(), sysJob.getJobgroup()); scheduler.pauseJob(key); sysJob.setStatus(Constant.JOB_STATUS_STOP); } else if (sysJob.getStatus().equals(Constant.JOB_STATUS_STOP)) { return R.fail("任务已处于停止状态"); } else if (sysJob.getStatus().equals(Constant.JOB_STATUS_INIT)) { return R.fail("任务还未执行"); } sysJobService.updateById(sysJob); return R.success("停止成功"); } @ApiOperation(value = "继续任务") @PostMapping("/keep") public R<Boolean> keep(Long id) throws Exception { SysJob sysJob = sysJobService.getById(id); if (sysJob.getStatus().equals(Constant.JOB_STATUS_STOP)) { JobKey key = new JobKey(sysJob.getName(), sysJob.getJobgroup()); scheduler.resumeJob(key); sysJob.setStatus(Constant.JOB_STATUS_STOP); } else if (sysJob.getStatus().equals(Constant.JOB_STATUS_RUN)) { return R.fail("任务正在运行"); } else if (sysJob.getStatus().equals(Constant.JOB_STATUS_INIT)) { return R.fail("任务还未执行"); } sysJobService.updateById(sysJob); return R.success("停止成功"); } @ApiOperation(value = "手动执行一次", tags = "不管任务是处于运行状态还是暂停,都会手动执行一次,未运行的任务,无法执行") @PostMapping("/triggerJob") public R<Boolean> triggerJob(@RequestBody SysJob sysJob) throws Exception { JobKey key = new JobKey(sysJob.getName(), sysJob.getJobgroup()); scheduler.triggerJob(key); return R.success("手动执行一次成功"); } }
4.创建表
create table sys_job ( id bigint not null comment '任务id' primary key, name varchar(256) null comment '任务名称', invokeTarget varchar(256) null comment '调用目标字符串', jobGroup varchar(100) null comment '任务组名', cronExpression varchar(100) null comment '执行表达式', misfirPolicy varchar(1) null comment '计划策略(0-默认,1-立即执行,2-触发一次执行,3-不触发立即执行)', concurrent varchar(1) null comment '是否并发执行(0-允许,1-禁止)', status varchar(1) null comment '状态(0正常,1暂停)', create_time datetime null comment '创建时间', create_by varchar(32) null comment '创建人', update_time datetime null comment '更新时间', update_by varchar(32) null comment '更新人', del varchar(1) null comment '删除标志', remark varchar(256) null comment '备注' );
愿你走出半生,归来仍是少年!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?