1. Quartz简介

  Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目。

  Quartz是一个完全由Java编写的开源作业调度框架,为在Java应用程序中进行作业调度提供了简单却强大的机制。

  Quartz可以与J2EE与J2SE应用程序相结合也可以单独使用。

  Quartz允许程序开发人员根据时间的间隔来调度作业。

  Quartz实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。

  Quartz官网:http://www.quartz-scheduler.org/

2. Quartz核心概念

  • Job

      Job表示一个工作,要执行的具体内容。
  • JobDetail

      JobDetail表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail还包含了这个任务调度的方案和策略。
  • Trigger

      Trigger代表一个调度参数的配置,什么时候去调。
  • Scheduler

      Scheduler代表一个调度容器,一个调度容器中可以注册多个JobDetail和Trigger。当Trigger与JobDetail组合,就可以被Scheduler容器调度了。

3. 初始化数据库

  Quartz采用持久化到数据库方式,需要创建官网提供的11张表。因此,可以在官网下载对应的版本,根据路径src\org\quartz\impl\jdbcjobstore找到对应数据库类型的脚本,例如Mysql为:tables_mysql.sql

  Mysql相关的表及系统需要的表脚本如下,请先创建数据库:quartzdemo,并初始化数据库表结构及数据。

  1. SET NAMES utf8mb4;
  2. SET FOREIGN_KEY_CHECKS = 0;
  3. -- ----------------------------
  4. -- Table structure for t_qrtz_blob_triggers
  5. -- ----------------------------
  6. DROP TABLE IF EXISTS `t_qrtz_blob_triggers`;
  7. CREATE TABLE `t_qrtz_blob_triggers` (
  8. `sched_name` varchar(120) NOT NULL,
  9. `trigger_name` varchar(190) NOT NULL,
  10. `trigger_group` varchar(190) NOT NULL,
  11. `blob_data` blob NULL,
  12. PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
  13. INDEX `sched_name`(`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
  14. CONSTRAINT `t_qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `t_qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
  15. );
  16. -- ----------------------------
  17. -- Records of t_qrtz_blob_triggers
  18. -- ----------------------------
  19. -- ----------------------------
  20. -- Table structure for t_qrtz_calendars
  21. -- ----------------------------
  22. DROP TABLE IF EXISTS `t_qrtz_calendars`;
  23. CREATE TABLE `t_qrtz_calendars` (
  24. `sched_name` varchar(120) NOT NULL,
  25. `calendar_name` varchar(190) NOT NULL,
  26. `calendar` blob NOT NULL,
  27. PRIMARY KEY (`sched_name`, `calendar_name`) USING BTREE
  28. );
  29. -- ----------------------------
  30. -- Records of t_qrtz_calendars
  31. -- ----------------------------
  32. -- ----------------------------
  33. -- Table structure for t_qrtz_cron_triggers
  34. -- ----------------------------
  35. DROP TABLE IF EXISTS `t_qrtz_cron_triggers`;
  36. CREATE TABLE `t_qrtz_cron_triggers` (
  37. `sched_name` varchar(120) NOT NULL,
  38. `trigger_name` varchar(190) NOT NULL,
  39. `trigger_group` varchar(190) NOT NULL,
  40. `cron_expression` varchar(120) NOT NULL,
  41. `time_zone_id` varchar(80) NULL DEFAULT NULL,
  42. PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
  43. CONSTRAINT `t_qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `t_qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
  44. );
  45. -- ----------------------------
  46. -- Records of t_qrtz_cron_triggers
  47. -- ----------------------------
  48. -- ----------------------------
  49. -- Table structure for t_qrtz_fired_triggers
  50. -- ----------------------------
  51. DROP TABLE IF EXISTS `t_qrtz_fired_triggers`;
  52. CREATE TABLE `t_qrtz_fired_triggers` (
  53. `sched_name` varchar(120) NOT NULL,
  54. `entry_id` varchar(95) NOT NULL,
  55. `trigger_name` varchar(190) NOT NULL,
  56. `trigger_group` varchar(190) NOT NULL,
  57. `instance_name` varchar(190) NOT NULL,
  58. `fired_time` bigint(0) NOT NULL,
  59. `sched_time` bigint(0) NOT NULL,
  60. `priority` int(0) NOT NULL,
  61. `state` varchar(16) NOT NULL,
  62. `job_name` varchar(190) NULL DEFAULT NULL,
  63. `job_group` varchar(190) NULL DEFAULT NULL,
  64. `is_nonconcurrent` varchar(1) NULL DEFAULT NULL,
  65. `requests_recovery` varchar(1) NULL DEFAULT NULL,
  66. PRIMARY KEY (`sched_name`, `entry_id`) USING BTREE,
  67. INDEX `idx_qrtz_ft_trig_inst_name`(`sched_name`, `instance_name`) USING BTREE,
  68. INDEX `idx_qrtz_ft_inst_job_req_rcvry`(`sched_name`, `instance_name`, `requests_recovery`) USING BTREE,
  69. INDEX `idx_qrtz_ft_j_g`(`sched_name`, `job_name`, `job_group`) USING BTREE,
  70. INDEX `idx_qrtz_ft_jg`(`sched_name`, `job_group`) USING BTREE,
  71. INDEX `idx_qrtz_ft_t_g`(`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
  72. INDEX `idx_qrtz_ft_tg`(`sched_name`, `trigger_group`) USING BTREE
  73. );
  74. -- ----------------------------
  75. -- Records of t_qrtz_fired_triggers
  76. -- ----------------------------
  77. -- ----------------------------
  78. -- Table structure for t_qrtz_job_details
  79. -- ----------------------------
  80. DROP TABLE IF EXISTS `t_qrtz_job_details`;
  81. CREATE TABLE `t_qrtz_job_details` (
  82. `sched_name` varchar(120) NOT NULL,
  83. `job_name` varchar(190) NOT NULL,
  84. `job_group` varchar(190) NOT NULL,
  85. `description` varchar(250) NULL DEFAULT NULL,
  86. `job_class_name` varchar(250) NOT NULL,
  87. `is_durable` varchar(1) NOT NULL,
  88. `is_nonconcurrent` varchar(1) NOT NULL,
  89. `is_update_data` varchar(1) NOT NULL,
  90. `requests_recovery` varchar(1) NOT NULL,
  91. `job_data` blob NULL,
  92. PRIMARY KEY (`sched_name`, `job_name`, `job_group`) USING BTREE,
  93. INDEX `idx_qrtz_j_req_recovery`(`sched_name`, `requests_recovery`) USING BTREE,
  94. INDEX `idx_qrtz_j_grp`(`sched_name`, `job_group`) USING BTREE
  95. );
  96. -- ----------------------------
  97. -- Records of t_qrtz_job_details
  98. -- ----------------------------
  99. -- ----------------------------
  100. -- Table structure for t_qrtz_locks
  101. -- ----------------------------
  102. DROP TABLE IF EXISTS `t_qrtz_locks`;
  103. CREATE TABLE `t_qrtz_locks` (
  104. `sched_name` varchar(120) NOT NULL,
  105. `lock_name` varchar(40) NOT NULL,
  106. PRIMARY KEY (`sched_name`, `lock_name`) USING BTREE
  107. );
  108. -- ----------------------------
  109. -- Records of t_qrtz_locks
  110. -- ----------------------------
  111. INSERT INTO `t_qrtz_locks` VALUES ('clusteredScheduler', 'STATE_ACCESS');
  112. INSERT INTO `t_qrtz_locks` VALUES ('clusteredScheduler', 'TRIGGER_ACCESS');
  113. -- ----------------------------
  114. -- Table structure for t_qrtz_paused_trigger_grps
  115. -- ----------------------------
  116. DROP TABLE IF EXISTS `t_qrtz_paused_trigger_grps`;
  117. CREATE TABLE `t_qrtz_paused_trigger_grps` (
  118. `sched_name` varchar(120) NOT NULL,
  119. `trigger_group` varchar(190) NOT NULL,
  120. PRIMARY KEY (`sched_name`, `trigger_group`) USING BTREE
  121. );
  122. -- ----------------------------
  123. -- Records of t_qrtz_paused_trigger_grps
  124. -- ----------------------------
  125. -- ----------------------------
  126. -- Table structure for t_qrtz_scheduler_state
  127. -- ----------------------------
  128. DROP TABLE IF EXISTS `t_qrtz_scheduler_state`;
  129. CREATE TABLE `t_qrtz_scheduler_state` (
  130. `sched_name` varchar(120) NOT NULL,
  131. `instance_name` varchar(190) NOT NULL,
  132. `last_checkin_time` bigint(0) NOT NULL,
  133. `checkin_interval` bigint(0) NOT NULL,
  134. PRIMARY KEY (`sched_name`, `instance_name`) USING BTREE
  135. );
  136. -- ----------------------------
  137. -- Records of t_qrtz_scheduler_state
  138. -- ----------------------------
  139. INSERT INTO `t_qrtz_scheduler_state` VALUES ('clusteredScheduler', 'C3Stones-PC', 1600918524362, 10000);
  140. -- ----------------------------
  141. -- Table structure for t_qrtz_simple_triggers
  142. -- ----------------------------
  143. DROP TABLE IF EXISTS `t_qrtz_simple_triggers`;
  144. CREATE TABLE `t_qrtz_simple_triggers` (
  145. `sched_name` varchar(120) NOT NULL,
  146. `trigger_name` varchar(190) NOT NULL,
  147. `trigger_group` varchar(190) NOT NULL,
  148. `repeat_count` bigint(0) NOT NULL,
  149. `repeat_interval` bigint(0) NOT NULL,
  150. `times_triggered` bigint(0) NOT NULL,
  151. PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
  152. CONSTRAINT `t_qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `t_qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
  153. );
  154. -- ----------------------------
  155. -- Records of t_qrtz_simple_triggers
  156. -- ----------------------------
  157. -- ----------------------------
  158. -- Table structure for t_qrtz_simprop_triggers
  159. -- ----------------------------
  160. DROP TABLE IF EXISTS `t_qrtz_simprop_triggers`;
  161. CREATE TABLE `t_qrtz_simprop_triggers` (
  162. `sched_name` varchar(120) NOT NULL,
  163. `trigger_name` varchar(190) NOT NULL,
  164. `trigger_group` varchar(190) NOT NULL,
  165. `str_prop_1` varchar(512) NULL DEFAULT NULL,
  166. `str_prop_2` varchar(512) NULL DEFAULT NULL,
  167. `str_prop_3` varchar(512) NULL DEFAULT NULL,
  168. `int_prop_1` int(0) NULL DEFAULT NULL,
  169. `int_prop_2` int(0) NULL DEFAULT NULL,
  170. `long_prop_1` bigint(0) NULL DEFAULT NULL,
  171. `long_prop_2` bigint(0) NULL DEFAULT NULL,
  172. `dec_prop_1` decimal(13, 4) NULL DEFAULT NULL,
  173. `dec_prop_2` decimal(13, 4) NULL DEFAULT NULL,
  174. `bool_prop_1` varchar(1) NULL DEFAULT NULL,
  175. `bool_prop_2` varchar(1) NULL DEFAULT NULL,
  176. PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
  177. CONSTRAINT `t_qrtz_simprop_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `t_qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
  178. );
  179. -- ----------------------------
  180. -- Records of t_qrtz_simprop_triggers
  181. -- ----------------------------
  182. -- ----------------------------
  183. -- Table structure for t_qrtz_triggers
  184. -- ----------------------------
  185. DROP TABLE IF EXISTS `t_qrtz_triggers`;
  186. CREATE TABLE `t_qrtz_triggers` (
  187. `sched_name` varchar(120) NOT NULL,
  188. `trigger_name` varchar(190) NOT NULL,
  189. `trigger_group` varchar(190) NOT NULL,
  190. `job_name` varchar(190) NOT NULL,
  191. `job_group` varchar(190) NOT NULL,
  192. `description` varchar(250) NULL DEFAULT NULL,
  193. `next_fire_time` bigint(0) NULL DEFAULT NULL,
  194. `prev_fire_time` bigint(0) NULL DEFAULT NULL,
  195. `priority` int(0) NULL DEFAULT NULL,
  196. `trigger_state` varchar(16) NOT NULL,
  197. `trigger_type` varchar(8) NOT NULL,
  198. `start_time` bigint(0) NOT NULL,
  199. `end_time` bigint(0) NULL DEFAULT NULL,
  200. `calendar_name` varchar(190) NULL DEFAULT NULL,
  201. `misfire_instr` smallint(0) NULL DEFAULT NULL,
  202. `job_data` blob NULL,
  203. PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
  204. INDEX `idx_qrtz_t_j`(`sched_name`, `job_name`, `job_group`) USING BTREE,
  205. INDEX `idx_qrtz_t_jg`(`sched_name`, `job_group`) USING BTREE,
  206. INDEX `idx_qrtz_t_c`(`sched_name`, `calendar_name`) USING BTREE,
  207. INDEX `idx_qrtz_t_g`(`sched_name`, `trigger_group`) USING BTREE,
  208. INDEX `idx_qrtz_t_state`(`sched_name`, `trigger_state`) USING BTREE,
  209. INDEX `idx_qrtz_t_n_state`(`sched_name`, `trigger_name`, `trigger_group`, `trigger_state`) USING BTREE,
  210. INDEX `idx_qrtz_t_n_g_state`(`sched_name`, `trigger_group`, `trigger_state`) USING BTREE,
  211. INDEX `idx_qrtz_t_next_fire_time`(`sched_name`, `next_fire_time`) USING BTREE,
  212. INDEX `idx_qrtz_t_nft_st`(`sched_name`, `trigger_state`, `next_fire_time`) USING BTREE,
  213. INDEX `idx_qrtz_t_nft_misfire`(`sched_name`, `misfire_instr`, `next_fire_time`) USING BTREE,
  214. INDEX `idx_qrtz_t_nft_st_misfire`(`sched_name`, `misfire_instr`, `next_fire_time`, `trigger_state`) USING BTREE,
  215. INDEX `idx_qrtz_t_nft_st_misfire_grp`(`sched_name`, `misfire_instr`, `next_fire_time`, `trigger_group`, `trigger_state`) USING BTREE,
  216. CONSTRAINT `t_qrtz_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `job_name`, `job_group`) REFERENCES `t_qrtz_job_details` (`sched_name`, `job_name`, `job_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
  217. );
  218. -- ----------------------------
  219. -- Records of t_qrtz_triggers
  220. -- ----------------------------
  221. -- ----------------------------
  222. -- Table structure for t_sys_job
  223. -- ----------------------------
  224. DROP TABLE IF EXISTS `t_sys_job`;
  225. CREATE TABLE `t_sys_job` (
  226. `id` int(0) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  227. `job_name` varchar(100) NULL DEFAULT NULL COMMENT '任务名称',
  228. `cron_expression` varchar(255) NULL DEFAULT NULL COMMENT 'cron表达式',
  229. `bean_class` varchar(255) NULL DEFAULT NULL COMMENT '任务执行类(包名+类名)',
  230. `status` varchar(10) NULL DEFAULT NULL COMMENT '任务状态',
  231. `job_group` varchar(50) NULL DEFAULT NULL COMMENT '任务分组',
  232. `job_data_map` varchar(1000) NULL DEFAULT NULL COMMENT '参数',
  233. `create_user_id` int(0) NULL DEFAULT NULL COMMENT '创建人ID',
  234. `create_date` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  235. `update_user_id` int(0) NULL DEFAULT NULL COMMENT '更新人ID',
  236. `update_date` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
  237. `remarks` varchar(255) NULL DEFAULT NULL COMMENT '描述',
  238. PRIMARY KEY (`id`) USING BTREE
  239. ) AUTO_INCREMENT = 3 COMMENT = '定时任务';
  240. -- ----------------------------
  241. -- Records of t_sys_job
  242. -- ----------------------------
  243. INSERT INTO `t_sys_job` VALUES (1, 'TestJob', '0/5 * * * * ?', 'com.c3stones.job.biz.TestJob', 'NONE', 'default', '{\"username\":\"zhangsan\", \"age\":18}', 1, '2020-09-25 15:22:32', 1, '2020-09-25 15:22:32', '测试定时任务1');
  244. INSERT INTO `t_sys_job` VALUES (2, 'Test2Job', '0 * * * * ?', 'com.c3stones.job.biz.Test2Job', 'NONE', 'default', '{\"username\":\"lisi\", \"age\":20}', 1, '2020-09-25 15:22:54', 1, '2020-09-25 15:22:54', '测试定时任务2');
  245. -- ----------------------------
  246. -- Table structure for t_sys_user
  247. -- ----------------------------
  248. DROP TABLE IF EXISTS `t_sys_user`;
  249. CREATE TABLE `t_sys_user` (
  250. `id` int(0) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  251. `username` varchar(50) NULL DEFAULT NULL COMMENT '用户名称',
  252. `nickname` varchar(100) NULL DEFAULT NULL COMMENT '用户昵称',
  253. `password` varchar(255) NULL DEFAULT NULL COMMENT '用户密码',
  254. PRIMARY KEY (`id`) USING BTREE
  255. )AUTO_INCREMENT = 3 COMMENT = '系统用户';
  256. -- ----------------------------
  257. -- Records of t_sys_user
  258. -- ----------------------------
  259. INSERT INTO `t_sys_user` VALUES (1, 'user', 'C3Stones', '$2a$10$WXEPqxjMwY6d6A0hkeBtGu.acRRWUOJmX7oLUuYMHF1VWWUm4EqOC');
  260. INSERT INTO `t_sys_user` VALUES (2, 'system', '管理员', '$2a$10$dmO7Uk9/lo1D5d1SvCGgWuB050a0E2uuBDNITEpWFiIfCg.3UbA8y');
  261. SET FOREIGN_KEY_CHECKS = 1;

4. 示例代码

  本文在之前博客SpringBoot + Layui +Mybatis-plus实现简单后台管理系统(内置安全过滤器)的示例项目spring-boot-layui-demo基础上增加了任务调度菜单,因此请先下载相关工程。

  • 修改pom.xml

      引入依赖spring-boot-starter-quartz即可实现SpringBoot与Quartz集成。
  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.c3stones</groupId>
  6. <artifactId>spring-boot-quartz-demo</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <name>spring-boot-quartz-demo</name>
  9. <description>Spring Boot Quartz Demo</description>
  10. <parent>
  11. <groupId>org.springframework.boot</groupId>
  12. <artifactId>spring-boot-starter-parent</artifactId>
  13. <version>2.2.8.RELEASE</version>
  14. <relativePath />
  15. </parent>
  16. <dependencies>
  17. <dependency>
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot-starter-quartz</artifactId>
  20. </dependency>
  21. <dependency>
  22. <groupId>com.baomidou</groupId>
  23. <artifactId>mybatis-plus-boot-starter</artifactId>
  24. <version>3.3.1</version>
  25. </dependency>
  26. <dependency>
  27. <groupId>mysql</groupId>
  28. <artifactId>mysql-connector-java</artifactId>
  29. <scope>runtime</scope>
  30. </dependency>
  31. <dependency>
  32. <groupId>cn.hutool</groupId>
  33. <artifactId>hutool-all</artifactId>
  34. <version>5.4.1</version>
  35. </dependency>
  36. <dependency>
  37. <groupId>org.projectlombok</groupId>
  38. <artifactId>lombok</artifactId>
  39. <optional>true</optional>
  40. </dependency>
  41. <dependency>
  42. <groupId>org.jsoup</groupId>
  43. <artifactId>jsoup</artifactId>
  44. <version>1.11.3</version>
  45. </dependency>
  46. <dependency>
  47. <groupId>org.springframework.boot</groupId>
  48. <artifactId>spring-boot-configuration-processor</artifactId>
  49. <optional>true</optional>
  50. </dependency>
  51. <dependency>
  52. <groupId>org.springframework.boot</groupId>
  53. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  54. </dependency>
  55. <dependency>
  56. <groupId>org.springframework.boot</groupId>
  57. <artifactId>spring-boot-starter-web</artifactId>
  58. </dependency>
  59. <dependency>
  60. <groupId>org.springframework.boot</groupId>
  61. <artifactId>spring-boot-starter-test</artifactId>
  62. <scope>test</scope>
  63. </dependency>
  64. </dependencies>
  65. <build>
  66. <plugins>
  67. <plugin>
  68. <groupId>org.springframework.boot</groupId>
  69. <artifactId>spring-boot-maven-plugin</artifactId>
  70. </plugin>
  71. </plugins>
  72. </build>
  73. </project>
  • 配置文件application.yml添加quartz相关配置
  1. server:
  2. port: 8080
  3. servlet:
  4. session:
  5. timeout: 1800s
  6. spring:
  7. jackson:
  8. time-zone: GMT+8
  9. date-format: yyyy-MM-dd HH:mm:ss
  10. datasource:
  11. driverClassName: com.mysql.cj.jdbc.Driver
  12. url: jdbc:mysql://127.0.0.1:3306/quartzdemo?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
  13. username: root
  14. password: 123456
  15. thymeleaf:
  16. prefix: classpath:/view/
  17. suffix: .html
  18. encoding: UTF-8
  19. servlet:
  20. content-type: text/html
  21. # 生产环境设置true
  22. cache: false
  23. quartz:
  24. properties:
  25. org:
  26. quartz:
  27. scheduler:
  28. instanceName: clusteredScheduler
  29. instanceId: AUTO
  30. jobStore:
  31. class: org.quartz.impl.jdbcjobstore.JobStoreTX
  32. driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  33. tablePrefix: t_qrtz_
  34. isClustered: false
  35. clusterCheckinInterval: 10000
  36. useProperties: false
  37. threadPool:
  38. class: org.quartz.simpl.SimpleThreadPool
  39. threadCount: 10
  40. threadPriority: 5
  41. threadsInheritContextClassLoaderOfInitializingThread: true
  42. job-store-type: jdbc
  43. # Mybatis-plus配置
  44. mybatis-plus:
  45. mapper-locations: classpath:mapper/*.xml
  46. global-config:
  47. db-config:
  48. id-type: AUTO
  49. # configuration:
  50. # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  51. # 日志配置
  52. logging:
  53. config: classpath:logback-spring.xml
  54. # 信息安全
  55. security:
  56. web:
  57. excludes:
  58. - /login
  59. - /logout
  60. - /images/**
  61. - /jquery/**
  62. - /layui/**
  63. xss:
  64. enable: true
  65. excludes:
  66. - /login
  67. - /logout
  68. - /images/*
  69. - /jquery/*
  70. - /layui/*
  71. sql:
  72. enable: true
  73. excludes:
  74. - /images/*
  75. - /jquery/*
  76. - /layui/*
  77. csrf:
  78. enable: true
  79. excludes:
  • 创建调度器配置类
  1. import javax.sql.DataSource;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.scheduling.quartz.SchedulerFactoryBean;
  6. /**
  7. * 调度器配置类
  8. *
  9. * @author CL
  10. *
  11. */
  12. @Configuration
  13. public class SchedulerConfig implements SchedulerFactoryBeanCustomizer {
  14. @Autowired
  15. private DataSource dataSource;
  16. @Override
  17. public void customize(SchedulerFactoryBean schedulerFactoryBean) {
  18. // 启动延时
  19. schedulerFactoryBean.setStartupDelay(10);
  20. // 自动启动任务调度
  21. schedulerFactoryBean.setAutoStartup(true);
  22. // 是否覆盖现有作业定义
  23. schedulerFactoryBean.setOverwriteExistingJobs(true);
  24. // 配置数据源
  25. schedulerFactoryBean.setDataSource(dataSource);
  26. }
  27. }
  • 创建全局用户工具类
  1. import javax.servlet.http.HttpServletRequest;
  2. import javax.servlet.http.HttpSession;
  3. import org.springframework.web.context.request.RequestContextHolder;
  4. import org.springframework.web.context.request.ServletRequestAttributes;
  5. import com.c3stones.sys.entity.User;
  6. /**
  7. * 用户工具类
  8. *
  9. * @author CL
  10. *
  11. */
  12. public class UserUtils {
  13. /**
  14. * 获取当前用户
  15. *
  16. * @return
  17. */
  18. public static User get() {
  19. return (User) getSession().getAttribute("user");
  20. }
  21. /**
  22. * 获取session
  23. *
  24. * @return
  25. */
  26. public static HttpSession getSession() {
  27. return getRequest().getSession();
  28. }
  29. /**
  30. * 获取request
  31. *
  32. * @return
  33. */
  34. public static HttpServletRequest getRequest() {
  35. ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
  36. .getRequestAttributes();
  37. return requestAttributes.getRequest();
  38. }
  39. }
  • 创建实体
  1. import java.io.Serializable;
  2. import java.time.LocalDateTime;
  3. import com.baomidou.mybatisplus.annotation.IdType;
  4. import com.baomidou.mybatisplus.annotation.TableField;
  5. import com.baomidou.mybatisplus.annotation.TableId;
  6. import com.baomidou.mybatisplus.annotation.TableName;
  7. import com.baomidou.mybatisplus.extension.activerecord.Model;
  8. import lombok.AllArgsConstructor;
  9. import lombok.Data;
  10. import lombok.EqualsAndHashCode;
  11. import lombok.NoArgsConstructor;
  12. /**
  13. * 定时任务
  14. *
  15. * @author CL
  16. *
  17. */
  18. @Data
  19. @NoArgsConstructor
  20. @AllArgsConstructor
  21. @TableName(value = "t_sys_job")
  22. @EqualsAndHashCode(callSuper = false)
  23. public class Job extends Model<Job> implements Serializable {
  24. private static final long serialVersionUID = 1L;
  25. /**
  26. * ID
  27. */
  28. @TableId(type = IdType.AUTO)
  29. private Integer id;
  30. /**
  31. * 任务名称
  32. */
  33. private String jobName;
  34. /**
  35. * cron表达式
  36. */
  37. private String cronExpression;
  38. /**
  39. * 任务执行类(包名+类名)
  40. */
  41. private String beanClass;
  42. /**
  43. * 任务状态(0-停止,1-运行)
  44. */
  45. private String status;
  46. /**
  47. * 任务分组
  48. */
  49. private String jobGroup;
  50. /**
  51. * 参数
  52. */
  53. private String jobDataMap;
  54. /**
  55. * 下一次执行时间
  56. */
  57. @TableField(exist = false)
  58. private LocalDateTime nextfireDate;
  59. /**
  60. * 创建人ID
  61. */
  62. private Integer createUserId;
  63. /**
  64. * 创建时间
  65. */
  66. private LocalDateTime createDate;
  67. /**
  68. * 更新人ID
  69. */
  70. private Integer updateUserId;
  71. /**
  72. * 更新时间
  73. */
  74. private LocalDateTime updateDate;
  75. /**
  76. * 描述
  77. */
  78. private String remarks;
  79. }
  • 创建定时任务处理器
  1. import java.text.ParseException;
  2. import java.time.Instant;
  3. import java.time.LocalDateTime;
  4. import java.time.ZoneId;
  5. import java.util.Date;
  6. import java.util.HashMap;
  7. import java.util.Map;
  8. import org.quartz.CronExpression;
  9. import org.quartz.CronScheduleBuilder;
  10. import org.quartz.CronTrigger;
  11. import org.quartz.JobBuilder;
  12. import org.quartz.JobDataMap;
  13. import org.quartz.JobDetail;
  14. import org.quartz.JobKey;
  15. import org.quartz.Scheduler;
  16. import org.quartz.SchedulerException;
  17. import org.quartz.Trigger;
  18. import org.quartz.Trigger.TriggerState;
  19. import org.quartz.TriggerBuilder;
  20. import org.quartz.TriggerKey;
  21. import org.springframework.beans.factory.annotation.Autowired;
  22. import org.springframework.stereotype.Component;
  23. import com.c3stones.job.entity.Job;
  24. import cn.hutool.core.util.StrUtil;
  25. import cn.hutool.json.JSONUtil;
  26. import lombok.extern.slf4j.Slf4j;
  27. /**
  28. * 定时任务管理器
  29. *
  30. * @author CL
  31. *
  32. */
  33. @Slf4j
  34. @Component
  35. public class QuartzHandler {
  36. @Autowired
  37. private Scheduler scheduler;
  38. /**
  39. * 新增定义任务
  40. *
  41. * @param job 定义任务
  42. * @param clazz 任务执行类
  43. * @return
  44. */
  45. @SuppressWarnings({ "rawtypes", "unchecked" })
  46. public boolean start(Job job, Class clazz) {
  47. boolean result = true;
  48. try {
  49. String jobName = job.getJobName();
  50. String jobGroup = job.getJobGroup();
  51. TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
  52. CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
  53. if (null == cronTrigger) {
  54. // 处理参数
  55. Map<String, String> map = new HashMap<>(5);
  56. String jobDataMap = job.getJobDataMap();
  57. if (StrUtil.isNotBlank(jobDataMap)) {
  58. if (JSONUtil.isJson(jobDataMap)) {
  59. Map parseMap = JSONUtil.toBean(jobDataMap, Map.class);
  60. parseMap.forEach((k, v) -> {
  61. map.put(String.valueOf(k), String.valueOf(v));
  62. });
  63. }
  64. }
  65. // 启动定时任务
  66. JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(jobName, jobGroup)
  67. .setJobData(new JobDataMap(map)).build();
  68. cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup)
  69. .withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression())).build();
  70. scheduler.scheduleJob(jobDetail, cronTrigger);
  71. if (!scheduler.isShutdown()) {
  72. scheduler.start();
  73. }
  74. } else {
  75. // 重启定时任务
  76. cronTrigger = cronTrigger.getTriggerBuilder().withIdentity(triggerKey)
  77. .withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression())).build();
  78. scheduler.rescheduleJob(triggerKey, cronTrigger);
  79. }
  80. } catch (SchedulerException e) {
  81. log.info("新增定时任务异常:{}", e.getMessage());
  82. result = false;
  83. }
  84. return result;
  85. }
  86. /**
  87. * 暂停定时任务
  88. *
  89. * @param job 定时任务
  90. * @return
  91. */
  92. public boolean pasue(Job job) {
  93. boolean result = true;
  94. try {
  95. String jobName = job.getJobName();
  96. String jobGroup = job.getJobGroup();
  97. TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
  98. Trigger trigger = scheduler.getTrigger(triggerKey);
  99. JobKey jobKey = trigger.getJobKey();
  100. scheduler.pauseJob(jobKey);
  101. } catch (SchedulerException e) {
  102. log.info("暂停定时任务异常:{}", e.getMessage());
  103. result = false;
  104. }
  105. return result;
  106. }
  107. /**
  108. * 重启定时任务
  109. *
  110. * @param job 定时任务
  111. * @return
  112. */
  113. public boolean restart(Job job) {
  114. boolean result = true;
  115. try {
  116. String jobName = job.getJobName();
  117. String jobGroup = job.getJobGroup();
  118. TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
  119. Trigger trigger = scheduler.getTrigger(triggerKey);
  120. scheduler.rescheduleJob(triggerKey, trigger);
  121. } catch (SchedulerException e) {
  122. log.info("重启定时任务异常:{}", e.getMessage());
  123. result = false;
  124. }
  125. return result;
  126. }
  127. /**
  128. * 立即执行一次
  129. *
  130. * @param job 定时任务
  131. * @return
  132. */
  133. public boolean trigger(Job job) {
  134. boolean result = true;
  135. try {
  136. String jobName = job.getJobName();
  137. String jobGroup = job.getJobGroup();
  138. TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
  139. Trigger trigger = scheduler.getTrigger(triggerKey);
  140. JobKey jobKey = trigger.getJobKey();
  141. scheduler.triggerJob(jobKey);
  142. } catch (SchedulerException e) {
  143. log.info("立即执行一次异常:{}", e.getMessage());
  144. result = false;
  145. }
  146. return result;
  147. }
  148. /**
  149. * 修改触发时间表达式
  150. *
  151. * @param job 定时任务
  152. * @param newCronExpression 新的cron表达式
  153. * @return
  154. */
  155. public boolean updateCronExpression(Job job, String newCronExpression) {
  156. boolean result = true;
  157. try {
  158. String jobName = job.getJobName();
  159. String jobGroup = job.getJobGroup();
  160. TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
  161. CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
  162. job.setCronExpression(newCronExpression);
  163. CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
  164. cronTrigger = cronTrigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder)
  165. .build();
  166. scheduler.rescheduleJob(triggerKey, cronTrigger);
  167. } catch (SchedulerException e) {
  168. log.info("修改触发时间表达式异常:{}", e.getMessage());
  169. result = false;
  170. }
  171. return result;
  172. }
  173. /**
  174. * 删除定时任务
  175. *
  176. * @param job 定时任务
  177. * @return
  178. */
  179. public boolean delete(Job job) {
  180. boolean result = true;
  181. try {
  182. String jobName = job.getJobName();
  183. String jobGroup = job.getJobGroup();
  184. TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
  185. Trigger trigger = scheduler.getTrigger(triggerKey);
  186. JobKey jobKey = trigger.getJobKey();
  187. // 停止触发器
  188. scheduler.pauseTrigger(triggerKey);
  189. // 移除触发器
  190. scheduler.unscheduleJob(triggerKey);
  191. // 删除任务
  192. scheduler.deleteJob(jobKey);
  193. } catch (SchedulerException e) {
  194. log.info("删除定时任务异常:{}", e.getMessage());
  195. result = false;
  196. }
  197. return result;
  198. }
  199. /***
  200. * 判断是否存在定时任务
  201. *
  202. * @param job 定时任务
  203. * @return
  204. */
  205. public boolean has(Job job) {
  206. boolean result = true;
  207. try {
  208. if (!scheduler.isShutdown()) {
  209. String jobName = job.getJobName();
  210. String jobGroup = job.getJobGroup();
  211. TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
  212. Trigger trigger = scheduler.getTrigger(triggerKey);
  213. result = (trigger != null) ? true : false;
  214. } else {
  215. result = false;
  216. }
  217. } catch (SchedulerException e) {
  218. log.info("判断是否存在定时任务异常:{}", e.getMessage());
  219. result = false;
  220. }
  221. return result;
  222. }
  223. /**
  224. * 获得定时任务状态
  225. *
  226. * @param job 定时任务
  227. * @return
  228. */
  229. public String getStatus(Job job) {
  230. String status = StrUtil.EMPTY;
  231. try {
  232. String jobName = job.getJobName();
  233. String jobGroup = job.getJobGroup();
  234. TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
  235. TriggerState triggerState = scheduler.getTriggerState(triggerKey);
  236. status = triggerState.toString();
  237. } catch (Exception e) {
  238. log.info("获得定时任务状态异常:{}", e.getMessage());
  239. }
  240. return StrUtil.isNotEmpty(status) ? status : TriggerState.NONE.toString();
  241. }
  242. /**
  243. * 启动调度器
  244. *
  245. * @return
  246. */
  247. public boolean startScheduler() {
  248. boolean result = true;
  249. try {
  250. scheduler.start();
  251. } catch (SchedulerException e) {
  252. log.info("启动调度器异常:{}", e.getMessage());
  253. result = false;
  254. }
  255. return result;
  256. }
  257. /**
  258. * 关闭调度器
  259. *
  260. * @return
  261. */
  262. public boolean standbyScheduler() {
  263. boolean result = true;
  264. try {
  265. if (!scheduler.isShutdown()) {
  266. scheduler.standby();
  267. }
  268. } catch (SchedulerException e) {
  269. log.info("关闭调度器异常:{}", e.getMessage());
  270. result = false;
  271. }
  272. return result;
  273. }
  274. /**
  275. * 判断调度器是否为开启状态
  276. *
  277. * @return
  278. */
  279. public boolean isStarted() {
  280. boolean result = true;
  281. try {
  282. result = scheduler.isStarted();
  283. } catch (SchedulerException e) {
  284. log.info("判断调度器是否为开启状态异常:{}", e.getMessage());
  285. }
  286. return result;
  287. }
  288. /**
  289. * 判断调度器是否为关闭状态
  290. *
  291. * @return
  292. */
  293. public boolean isShutdown() {
  294. boolean result = true;
  295. try {
  296. result = scheduler.isShutdown();
  297. } catch (SchedulerException e) {
  298. log.info("判断调度器是否为关闭状态异常:{}", e.getMessage());
  299. }
  300. return result;
  301. }
  302. /**
  303. * 判断调度器是否为待机状态
  304. *
  305. * @return
  306. */
  307. public boolean isInStandbyMode() {
  308. boolean result = true;
  309. try {
  310. result = scheduler.isInStandbyMode();
  311. } catch (SchedulerException e) {
  312. log.info("判断调度器是否为待机状态异常:{}", e.getMessage());
  313. }
  314. return result;
  315. }
  316. /**
  317. * 获得下一次执行时间
  318. *
  319. * @param cronExpression cron表达式
  320. * @return
  321. */
  322. public LocalDateTime nextfireDate(String cronExpression) {
  323. LocalDateTime localDateTime = null;
  324. try {
  325. if (StrUtil.isNotEmpty(cronExpression)) {
  326. CronExpression ce = new CronExpression(cronExpression);
  327. Date nextInvalidTimeAfter = ce.getNextInvalidTimeAfter(new Date());
  328. localDateTime = Instant.ofEpochMilli(nextInvalidTimeAfter.getTime()).atZone(ZoneId.systemDefault())
  329. .toLocalDateTime();
  330. }
  331. } catch (ParseException e) {
  332. log.info("获得下一次执行时间异常:{}", e.getMessage());
  333. }
  334. return localDateTime;
  335. }
  336. }
  • 创建Mapper
  1. import org.apache.ibatis.annotations.Mapper;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import com.c3stones.job.entity.Job;
  4. /**
  5. * 定时任务Mapper
  6. *
  7. * @author CL
  8. *
  9. */
  10. @Mapper
  11. public interface JobMapper extends BaseMapper<Job> {
  12. }
  • 创建Service
  1. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  2. import com.baomidou.mybatisplus.extension.service.IService;
  3. import com.c3stones.job.entity.Job;
  4. /**
  5. * 定时任务Service
  6. *
  7. * @author CL
  8. *
  9. */
  10. public interface JobService extends IService<Job> {
  11. /**
  12. * 查询列表数据
  13. *
  14. * @param job 系统用户
  15. * @param current 当前页
  16. * @param size 每页显示条数
  17. * @return
  18. */
  19. public Page<Job> listData(Job job, long current, long size);
  20. }
  • 创建Service实现类
  1. import java.util.List;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Service;
  4. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  5. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  6. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  7. import com.c3stones.job.config.QuartzHandler;
  8. import com.c3stones.job.entity.Job;
  9. import com.c3stones.job.mapper.JobMapper;
  10. import com.c3stones.job.service.JobService;
  11. import cn.hutool.core.util.StrUtil;
  12. /**
  13. * 定时任务Service实现
  14. *
  15. * @author CL
  16. *
  17. */
  18. @Service
  19. public class JobServiceImpl extends ServiceImpl<JobMapper, Job> implements JobService {
  20. @Autowired
  21. private QuartzHandler quartzHandler;
  22. /**
  23. * 查询列表数据
  24. *
  25. * @param job 系统用户
  26. * @param current 当前页
  27. * @param size 每页显示条数
  28. * @return
  29. */
  30. @Override
  31. public Page<Job> listData(Job job, long current, long size) {
  32. QueryWrapper<Job> queryWrapper = new QueryWrapper<>();
  33. if (StrUtil.isNotBlank(job.getJobName())) {
  34. queryWrapper.like("job_name", job.getJobName());
  35. }
  36. Page<Job> page = baseMapper.selectPage(new Page<>(current, size), queryWrapper);
  37. List<Job> records = page.getRecords();
  38. // 处理定时任务数据
  39. for (int i = 0; i < records.size(); i++) {
  40. Job j = records.get(i);
  41. // 获取下一次执行时间
  42. j.setNextfireDate(quartzHandler.nextfireDate(j.getCronExpression()));
  43. // 更新状态
  44. String status = quartzHandler.getStatus(j);
  45. if (!(status).equals(j.getStatus())) {
  46. j.setStatus(status);
  47. super.updateById(j);
  48. }
  49. records.set(i, j);
  50. }
  51. page.setRecords(records);
  52. return page;
  53. }
  54. }
  • 创建Controller
  1. import java.time.LocalDateTime;
  2. import org.quartz.Trigger.TriggerState;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Controller;
  5. import org.springframework.util.Assert;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RequestParam;
  8. import org.springframework.web.bind.annotation.ResponseBody;
  9. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  10. import com.c3stones.common.vo.Response;
  11. import com.c3stones.job.config.QuartzHandler;
  12. import com.c3stones.job.entity.Job;
  13. import com.c3stones.job.service.JobService;
  14. import com.c3stones.sys.entity.User;
  15. import com.c3stones.sys.utils.UserUtils;
  16. /**
  17. * 定时任务Controller
  18. *
  19. * @author CL
  20. *
  21. */
  22. @Controller
  23. @RequestMapping(value = "job")
  24. public class JobController {
  25. @Autowired
  26. private QuartzHandler quartzHandler;
  27. @Autowired
  28. private JobService jobService;
  29. /**
  30. * 查询列表
  31. *
  32. * @return
  33. */
  34. @RequestMapping(value = "list")
  35. public String list() {
  36. return "pages/job/jobList";
  37. }
  38. /**
  39. * 查询列表数据
  40. *
  41. * @param user 系统用户
  42. * @param current 当前页
  43. * @param size 每页显示条数
  44. * @return
  45. */
  46. @RequestMapping(value = "listData")
  47. @ResponseBody
  48. public Response<Page<Job>> listData(Job job, @RequestParam(name = "page") long current,
  49. @RequestParam(name = "limit") long size) {
  50. Page<Job> page = jobService.listData(job, current, size);
  51. return Response.success(page);
  52. }
  53. /**
  54. * 更新
  55. *
  56. * @param job 定时任务
  57. * @return
  58. */
  59. @RequestMapping(value = "update")
  60. @ResponseBody
  61. public Response<Boolean> update(Job job) {
  62. Assert.notNull(job.getId(), "ID不能为空");
  63. User user = UserUtils.get();
  64. if (user != null) {
  65. job.setUpdateUserId(user.getId());
  66. }
  67. LocalDateTime now = LocalDateTime.now();
  68. job.setUpdateDate(now);
  69. boolean result = jobService.updateById(job);
  70. Job queryJob = jobService.getById(job.getId());
  71. String status = quartzHandler.getStatus(queryJob);
  72. if (!(TriggerState.NONE.toString()).equals(status)) {
  73. result = quartzHandler.updateCronExpression(queryJob, queryJob.getCronExpression());
  74. }
  75. return Response.success("更新" + (result ? "成功" : "失败"), result);
  76. }
  77. /**
  78. * 删除
  79. *
  80. * @param job 定时任务
  81. * @return
  82. */
  83. @RequestMapping(value = "delete")
  84. @ResponseBody
  85. public Response<Boolean> delete(Job job) {
  86. Assert.notNull(job.getId(), "ID不能为空");
  87. Job queryJob = jobService.getById(job.getId());
  88. boolean result = true;
  89. if (!(TriggerState.NONE.toString()).equals(queryJob.getStatus())) {
  90. result = quartzHandler.delete(queryJob);
  91. }
  92. if (result) {
  93. result = jobService.removeById(job.getId());
  94. }
  95. return Response.success("删除" + (result ? "成功" : "失败"), result);
  96. }
  97. /**
  98. * 启动
  99. *
  100. * @param job 定时任务
  101. * @return
  102. * @throws ClassNotFoundException
  103. */
  104. @RequestMapping(value = "start")
  105. @ResponseBody
  106. public Response<Boolean> start(Job job) throws ClassNotFoundException {
  107. Assert.notNull(job.getId(), "ID不能为空");
  108. Job queryJob = jobService.getById(job.getId());
  109. Assert.notNull(queryJob, "定时任务不存在");
  110. Class<?> clazz = Class.forName(queryJob.getBeanClass());
  111. Assert.notNull(clazz, "未找到任务执行类");
  112. boolean result = quartzHandler.start(queryJob, clazz);
  113. return Response.success("启动" + (result ? "成功" : "失败"), result);
  114. }
  115. /**
  116. * 暂停
  117. *
  118. * @param job 定时任务
  119. * @return
  120. */
  121. @RequestMapping(value = "pasue")
  122. @ResponseBody
  123. public Response<Boolean> pasue(Job job) {
  124. Assert.notNull(job.getId(), "ID不能为空");
  125. Job queryJob = jobService.getById(job.getId());
  126. Assert.notNull(queryJob, "定时任务不存在");
  127. String status = quartzHandler.getStatus(queryJob);
  128. if (!((TriggerState.NORMAL.toString()).equals(status) || (TriggerState.PAUSED.toString()).equals(status)
  129. || (TriggerState.BLOCKED.toString()).equals(status))) {
  130. return Response.success("当前状态不可暂停", false);
  131. }
  132. if ((TriggerState.PAUSED.toString()).equals(status)) {
  133. return Response.success("已暂停", false);
  134. }
  135. boolean result = quartzHandler.pasue(queryJob);
  136. return Response.success("暂停" + (result ? "成功" : "失败"), result);
  137. }
  138. /**
  139. * 立即执行
  140. *
  141. * @param job 定时任务
  142. * @return
  143. */
  144. @RequestMapping(value = "trigger")
  145. @ResponseBody
  146. public Response<Boolean> trigger(Job job) {
  147. Assert.notNull(job.getId(), "ID不能为空");
  148. Job queryJob = jobService.getById(job.getId());
  149. Assert.notNull(queryJob, "定时任务不存在");
  150. String status = quartzHandler.getStatus(queryJob);
  151. if (!((TriggerState.NORMAL.toString()).equals(status) || (TriggerState.PAUSED.toString()).equals(status)
  152. || (TriggerState.COMPLETE.toString()).equals(status))) {
  153. return Response.success("当前状态不可立即执行", false);
  154. }
  155. boolean result = quartzHandler.trigger(queryJob);
  156. return Response.success("立即执行" + (result ? "成功" : "失败"), result);
  157. }
  158. /**
  159. * 判断定时器是否为待机模式
  160. */
  161. @RequestMapping(value = "isInStandbyMode")
  162. @ResponseBody
  163. public Response<Boolean> isInStandbyMode() {
  164. boolean result = quartzHandler.isInStandbyMode();
  165. return Response.success(result);
  166. }
  167. /**
  168. * 启动定时器
  169. *
  170. * @return
  171. */
  172. @RequestMapping(value = "startScheduler")
  173. @ResponseBody
  174. public Response<Boolean> startScheduler() {
  175. boolean result = quartzHandler.startScheduler();
  176. return Response.success("启动定时器" + (result ? "成功" : "失败"), result);
  177. }
  178. /**
  179. * 待机定时器
  180. *
  181. * @return
  182. */
  183. @RequestMapping(value = "standbyScheduler")
  184. @ResponseBody
  185. public Response<Boolean> standbyScheduler() {
  186. boolean result = quartzHandler.standbyScheduler();
  187. return Response.success("关闭定时器" + (result ? "成功" : "失败"), result);
  188. }
  189. /**
  190. * 新增
  191. *
  192. * @return
  193. */
  194. @RequestMapping(value = "add")
  195. public String add() {
  196. return "pages/job/jobAdd";
  197. }
  198. /**
  199. * 保存
  200. *
  201. * @return
  202. */
  203. @RequestMapping(value = "save")
  204. @ResponseBody
  205. public Response<Boolean> save(Job job) {
  206. User user = UserUtils.get();
  207. if (user != null) {
  208. job.setCreateUserId(user.getId());
  209. job.setUpdateUserId(user.getId());
  210. }
  211. LocalDateTime now = LocalDateTime.now();
  212. job.setCreateDate(now);
  213. job.setUpdateDate(now);
  214. boolean result = jobService.save(job);
  215. return Response.success(result);
  216. }
  217. }
  • 主页index.html配置菜单
  1. <!DOCTYPE html>
  2. <html xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  5. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  7. <title>C3Stones</title>
  8. <link th:href="@{/images/favicon.ico}" rel="icon">
  9. <link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
  10. <link th:href="@{/layui/css/admin.css}" rel="stylesheet" />
  11. <script th:src="@{/layui/layui.js}"></script>
  12. <script th:src="@{/layui/js/index.js}" data-main="home"></script>
  13. </head>
  14. <body class="layui-layout-body">
  15. <div class="layui-layout layui-layout-admin">
  16. <div class="layui-header custom-header">
  17. <ul class="layui-nav layui-layout-left">
  18. <li class="layui-nav-item slide-sidebar" lay-unselect>
  19. <a href="javascript:;" class="icon-font"><i class="ai ai-menufold"></i></a>
  20. </li>
  21. </ul>
  22. <ul class="layui-nav layui-layout-right">
  23. <li class="layui-nav-item">
  24. <a href="javascript:;">[[${user?.nickname}]]</a>
  25. <dl class="layui-nav-child">
  26. <dd><a th:href="@{/logout}">退出</a></dd>
  27. </dl>
  28. </li>
  29. </ul>
  30. </div>
  31. <div class="layui-side custom-admin">
  32. <div class="layui-side-scroll">
  33. <div class="custom-logo">
  34. <img alt="" th:src="@{/images/logo.jpg}">
  35. <h1>C3Stones</h1>
  36. </div>
  37. <ul id="Nav" class="layui-nav layui-nav-tree">
  38. <li class="layui-nav-item">
  39. <a href="javascript:;">
  40. <i class="layui-icon"></i>
  41. <em>主页</em>
  42. </a>
  43. <dl class="layui-nav-child">
  44. <dd><a th:href="@{/view}">控制台</a></dd>
  45. </dl>
  46. </li>
  47. <li class="layui-nav-item">
  48. <a href="javascript:;">
  49. <i class="layui-icon"></i>
  50. <em>系统管理</em>
  51. </a>
  52. <dl class="layui-nav-child">
  53. <dd><a th:href="@{/user/list}">用户管理</a></dd>
  54. </dl>
  55. <dl class="layui-nav-child">
  56. <dd><a th:href="@{/job/list}">任务调度</a></dd>
  57. </dl>
  58. </li>
  59. </ul>
  60. </div>
  61. </div>
  62. <div class="layui-body">
  63. <div class="layui-tab app-container" lay-allowClose="true" lay-filter="tabs">
  64. <ul id="appTabs" class="layui-tab-title custom-tab"></ul>
  65. <div id="appTabPage" class="layui-tab-content"></div>
  66. </div>
  67. </div>
  68. <div class="layui-footer">
  69. <p> 2020 - C3Stones Blog : <a href="https://www.cnblogs.com/cao-lei/" target="_blank">https://www.cnblogs.com/cao-lei/</a></p>
  70. </div>
  71. <div class="mobile-mask"></div>
  72. </div>
  73. </body>
  74. </html>
  • 新增定时任务列表页面jobList.html
  1. <!DOCTYPE html>
  2. <html xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
  7. <link th:href="@{/layui/css/view.css}" rel="stylesheet" />
  8. <script th:src="@{/layui/layui.all.js}"></script>
  9. <script th:src="@{/jquery/jquery-2.1.4.min.js}"></script>
  10. <script th:src="@{/layui/js/view.js}"></script>
  11. <title></title>
  12. </head>
  13. <body class="layui-view-body">
  14. <div class="layui-content">
  15. <div class="layui-row">
  16. <div class="layui-card">
  17. <div class="layui-card-header">
  18. <i class="layui-icon mr5"></i>任务调度(定时器状态:<label id="schedulerStatus"></label>
  19. <button class="layui-btn layui-btn-xs layui-hide" data-type="startScheduler">启动定时器</button>
  20. <button class="layui-btn layui-btn-xs layui-btn-danger layui-hide" data-type="standbyScheduler">定时器待机</button>
  21. <button class="layui-btn layui-btn-xs layui-btn-normal pull-right mt10" data-type="add"><i class="layui-icon mr5"></i>新增</button>
  22. </div>
  23. <div class="layui-card-body">
  24. <div class="searchTable">
  25. 任务名称:
  26. <div class="layui-inline mr5">
  27. <input class="layui-input" name="jobName" autocomplete="off">
  28. </div>
  29. <button class="layui-btn" data-type="reload">查询</button>
  30. <button class="layui-btn layui-btn-primary" data-type="reset">重置</button>
  31. </div>
  32. <table class="layui-hide" id="jobDataTable" lay-filter="config"></table>
  33. <script type="text/html" id="operation">
  34. <a class="layui-btn layui-btn-xs " lay-event="start">启动</a>
  35. <a class="layui-btn layui-btn-xs layui-btn-warm" lay-event="pasue">暂停</a>
  36. <a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="trigger">立即执行</a>
  37. <a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del">删除</a>
  38. </script>
  39. </div>
  40. </div>
  41. </div>
  42. </div>
  43. </body>
  44. <script>
  45. var element = layui.element;
  46. var table = layui.table;
  47. var layer = layui.layer;
  48. table.render({
  49. id: 'jobTable'
  50. ,elem: '#jobDataTable'
  51. ,url: '[[@{/job/listData}]]'
  52. ,cellMinWidth: 100
  53. ,page: {
  54. layout: ['prev', 'page', 'next', 'count', 'skip', 'limit']
  55. ,groups: 5
  56. ,first: false
  57. ,last: false
  58. }
  59. ,cols: [
  60. [
  61. {field:'id', title: 'ID', width: 50}
  62. ,{field:'jobName', title: '任务名称', width: 120}
  63. ,{field:'cronExpression', title: '周期表达式', edit: 'text', width: 100}
  64. ,{field:'beanClass', title: '任务执行类', width: 250}
  65. ,{field:'jobDataMap', title: '参数', width: 200}
  66. ,{field:'status', title: '状态', templet: '#statusTemp', width: 80, align: 'center'}
  67. ,{field:'jobGroup', title: '分组', templet: '#groupTemp', width: 60, align: 'center'}
  68. ,{field:'nextfireDate', title: '下一次执行时间', width: 160, align: 'center'}
  69. ,{field:'remarks', title: '描述', width: 200}
  70. ,{fixed: 'right', title:'操作', align: 'center', toolbar: '#operation', width:240}
  71. ]
  72. ]
  73. ,response: {
  74. statusCode: 200
  75. }
  76. ,parseData: function(res){
  77. return {
  78. "code": res.code
  79. ,"msg": res.msg
  80. ,"count": res.data.total
  81. ,"data": res.data.records
  82. };
  83. }
  84. });
  85. active = {
  86. add: function() {
  87. layer.open({
  88. type: 2,
  89. area: ['90%', '90%'],
  90. title: '新增',
  91. content: '[[@{/}]]job/add'
  92. });
  93. },
  94. reload: function() {
  95. table.reload('jobTable', {
  96. page: {
  97. curr: 1
  98. }
  99. ,where: {
  100. jobName : $("input[name='jobName']").val()
  101. }
  102. }, 'data');
  103. },
  104. reset: function() {
  105. $(".searchTable .layui-input").val("");
  106. },
  107. startScheduler: function() {
  108. $.ajax({
  109. url : "[[@{/}]]job/startScheduler",
  110. data : {},
  111. type : "post",
  112. dataType : "json",
  113. error : function(data) {
  114. errorHandle(data);
  115. },
  116. success : function(data) {
  117. getSchedulerStatus();
  118. msg(data);
  119. refresh();
  120. }
  121. });
  122. },
  123. standbyScheduler: function() {
  124. $.ajax({
  125. url : "[[@{/}]]job/standbyScheduler",
  126. data : {},
  127. type : "post",
  128. dataType : "json",
  129. error : function(data) {
  130. errorHandle(data);
  131. },
  132. success : function(data) {
  133. getSchedulerStatus();
  134. msg(data);
  135. refresh();
  136. }
  137. });
  138. }
  139. };
  140. // 按钮事件
  141. $('.layui-btn').on('click', function(){
  142. var type = $(this).data('type');
  143. active[type] ? active[type].call(this) : '';
  144. });
  145. //监听行工具事件
  146. table.on('tool(config)', function(obj){
  147. var row = obj.data;
  148. if (obj.event === 'start') {
  149. $.ajax({
  150. url : "[[@{/}]]job/start",
  151. data : {'id': row.id},
  152. type : "post",
  153. dataType : "json",
  154. error : function(data) {
  155. errorHandle(data);
  156. },
  157. success : function(data) {
  158. msg(data);
  159. refresh();
  160. }
  161. });
  162. } if (obj.event == 'pasue') {
  163. $.ajax({
  164. url : "[[@{/}]]job/pasue",
  165. data : {'id': row.id},
  166. type : "post",
  167. dataType : "json",
  168. error : function(data) {
  169. errorHandle(data);
  170. },
  171. success : function(data) {
  172. msg(data);
  173. refresh();
  174. }
  175. });
  176. } if (obj.event == 'trigger') {
  177. $.ajax({
  178. url : "[[@{/}]]job/trigger",
  179. data : {'id': row.id},
  180. type : "post",
  181. dataType : "json",
  182. error : function(data) {
  183. errorHandle(data);
  184. },
  185. success : function(data) {
  186. msg(data);
  187. refresh();
  188. }
  189. });
  190. } else if(obj.event === 'del') {
  191. layer.confirm("确认删除吗?", {icon: 3, title:'提示'}, function(index) {
  192. layer.close(index);
  193. $.ajax({
  194. url : "[[@{/}]]job/delete",
  195. data : {'id': row.id},
  196. type : "post",
  197. dataType : "json",
  198. error : function(data) {
  199. errorHandle(data);
  200. },
  201. success : function(data) {
  202. refresh();
  203. }
  204. });
  205. });
  206. }
  207. });
  208. table.on('edit(config)', function(obj){
  209. var value = obj.value;
  210. if (isEmpty(value)) {
  211. layer.msg("不能为空", {icon: 2});
  212. refresh();
  213. return;
  214. }
  215. $.ajax({
  216. url : "[[@{/}]]job/update",
  217. data : {'id': obj.data.id, 'cronExpression' : value},
  218. type : "post",
  219. dataType : "json",
  220. error : function(data) {
  221. errorHandle(data);
  222. },
  223. success : function(data) {
  224. msg(data);
  225. refresh();
  226. }
  227. });
  228. });
  229. // 获取定时器状态
  230. $(function(){getSchedulerStatus();});
  231. function getSchedulerStatus() {
  232. $.ajax({
  233. url : "[[@{/}]]job/isInStandbyMode",
  234. data : {},
  235. type : "post",
  236. dataType : "json",
  237. error : function(data) {
  238. errorHandle(data);
  239. },
  240. success : function(data) {
  241. if (!data.data) { // 启动状态
  242. $("button[data-type='startScheduler']").addClass("layui-hide");
  243. $("button[data-type='standbyScheduler']").removeClass("layui-hide");
  244. $("#schedulerStatus").html("<span class='text-green'>启动中</span>");
  245. } else { // 待机状态
  246. $("button[data-type='startScheduler']").removeClass("layui-hide");
  247. $("button[data-type='standbyScheduler']").addClass("layui-hide");
  248. $("#schedulerStatus").html("<span class='text-orange'>待机中</span>");
  249. }
  250. }
  251. });
  252. }
  253. </script>
  254. <script type="text/html" id="statusTemp">
  255. {{# if(d.status === 'NONE'){ }}
  256. <span class="text-purple">未启动</span>
  257. {{# } else if(d.status === 'NORMAL') { }}
  258. <span class="text-green">正常</span>
  259. {{# } else if(d.status === 'PAUSED') { }}
  260. <span class="text-orange">暂停</span>
  261. {{# } else if(d.status === 'COMPLETE') { }}
  262. <span class="text-aqua">完成</span>
  263. {{# } else if(d.status === 'ERROR') { }}
  264. <span class="text-red">异常</span>
  265. {{# } else if(d.status === 'BLOCKED') { }}
  266. <span class="text-maroon">锁定</span>
  267. {{# } else { }}
  268. <span class="text-gray">未知</span>
  269. {{# } }}
  270. </script>
  271. <script type="text/html" id="groupTemp">
  272. {{# if(d.jobGroup === 'default'){ }}
  273. 默认
  274. {{# } else if(d.jobGroup === 'system') { }}
  275. 系统
  276. {{# } else { }}
  277. 未知
  278. {{# } }}
  279. </script>
  280. </html>
  • 新增定时任务新增页面jobAdd.html
  1. <!DOCTYPE html>
  2. <html xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
  7. <link th:href="@{/layui/css/view.css}" rel="stylesheet" />
  8. <script th:src="@{/layui/layui.all.js}"></script>
  9. <script th:src="@{/jquery/jquery-2.1.4.min.js}"></script>
  10. <script th:src="@{/jquery/jquery-form.js}"></script>
  11. <script th:src="@{/layui/js/view.js}"></script>
  12. <title></title>
  13. </head>
  14. <body class="layui-view-body">
  15. <div class="layui-row">
  16. <div class="layui-card">
  17. <form class="layui-form layui-card-body layui-form-pane" method="post" th:action="@{/job/save}">
  18. <input type="hidden" name="status" value="NONE">
  19. <div class="layui-form-item">
  20. <div class="layui-inline mr0" style="width: 49.7%">
  21. <label class="layui-form-label"><i>*</i>任务名称</label>
  22. <div class="layui-input-block">
  23. <input type="text" name="jobName" id="jobName" maxlength="30" lay-verify="required" class="layui-input">
  24. </div>
  25. </div>
  26. <div class="layui-inline mr0" style="width: 49.8%">
  27. <label class="layui-form-label"><i>*</i>任务分组</label>
  28. <div class="layui-input-block">
  29. <select name="jobGroup">
  30. <option value="default">默认</option>
  31. <option value="system">系统</option>
  32. </select>
  33. </div>
  34. </div>
  35. </div>
  36. <div class="layui-form-item">
  37. <label class="layui-form-label">任务描述</label>
  38. <div class="layui-input-block">
  39. <input type="text" name="remarks" maxlength="50" class="layui-input">
  40. </div>
  41. </div>
  42. <div class="layui-form-item">
  43. <label class="layui-form-label"><i>*</i>执行类</label>
  44. <div class="layui-input-inline width-460">
  45. <input type="text" name="beanClass" lay-verify="required" maxlength="200" class="layui-input">
  46. </div>
  47. <div class="layui-form-mid layui-word-aux">包名 + 类名,示例:com.c3stones.job.biz.TestJob</div>
  48. </div>
  49. <div class="layui-form-item">
  50. <label class="layui-form-label">参数</label>
  51. <div class="layui-input-inline width-460">
  52. <input type="text" name="jobDataMap" placeholder="JSON数据格式" maxlength="1000" autocomplete="off" class="layui-input">
  53. </div>
  54. <div class="layui-form-mid layui-word-aux">示例:{"username":"zhangsan", "age":18}</div>
  55. </div>
  56. <div class="layui-form-item">
  57. <label class="layui-form-label"><i>*</i>表达式</label>
  58. <div class="layui-input-inline width-460">
  59. <input type="text" name="cronExpression" placeholder="例如:0/5 * * * * ?" lay-verify="required" maxlength="200" class="layui-input">
  60. </div>
  61. <div class="layui-form-mid layui-word-aux"><a class="text-blue" href="https://cron.qqe2.com/" target="_blank">在线Cron表达式生成器</a></div>
  62. </div>
  63. <div class="layui-form-item">
  64. <button type="submit" class="layui-btn" lay-submit lay-filter="*">提交</button>
  65. <button type="reset" class="layui-btn layui-btn-primary">重置</button>
  66. </div>
  67. </form>
  68. </div>
  69. </div>
  70. </body>
  71. <script>
  72. var form = layui.form;
  73. var layer = layui.layer;
  74. form.render();
  75. // 提交表单
  76. form.on('submit(*)', function(data){
  77. $(".layui-form").ajaxForm({
  78. error: function(data){
  79. errorHandle(data);
  80. },
  81. success: function(data) {
  82. parent.location.reload();
  83. var index = parent.layer.getFrameIndex(window.name);
  84. parent.layer.close(index);
  85. }
  86. });
  87. });
  88. </script>
  89. </html>

5. 测试

  • 创建两种类型Job

    • 实现Job接口
    1. import java.time.LocalDateTime;
    2. import org.quartz.Job;
    3. import org.quartz.JobDataMap;
    4. import org.quartz.JobExecutionContext;
    5. import org.quartz.JobExecutionException;
    6. import org.springframework.beans.factory.annotation.Autowired;
    7. import com.c3stones.job.service.JobService;
    8. import lombok.extern.slf4j.Slf4j;
    9. /**
    10. * 测试定时任务
    11. *
    12. * @author CL
    13. *
    14. */
    15. @Slf4j
    16. // @DisallowConcurrentExecution //不并发执行
    17. public class TestJob implements Job {
    18. @Autowired
    19. private JobService jobService;
    20. @Override
    21. public void execute(JobExecutionContext context) throws JobExecutionException {
    22. JobDataMap jobDataMap = context.getMergedJobDataMap();
    23. log.info("定时任务1 => 定时任务定时任务数量 => {},参数值 => {},当前时间 => {}", jobService.count(),
    24. "{ username=" + jobDataMap.get("username") + ", age=" + jobDataMap.get("age") + " }",
    25. LocalDateTime.now());
    26. }
    27. }
    • 继承QuartzJobBean类
    1. import java.time.LocalDateTime;
    2. import org.quartz.JobDataMap;
    3. import org.quartz.JobExecutionContext;
    4. import org.quartz.JobExecutionException;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.scheduling.quartz.QuartzJobBean;
    7. import com.c3stones.job.service.JobService;
    8. import lombok.extern.slf4j.Slf4j;
    9. /**
    10. * 测试定时任务
    11. *
    12. * @author CL
    13. *
    14. */
    15. @Slf4j
    16. // @DisallowConcurrentExecution //不并发执行
    17. public class Test2Job extends QuartzJobBean {
    18. @Autowired
    19. private JobService jobService;
    20. @Override
    21. protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
    22. JobDataMap jobDataMap = context.getMergedJobDataMap();
    23. log.info("定时任务2 => 定时任务数量 => {},参数值 => {},当前时间 => {}", jobService.count(),
    24. "{ username=" + jobDataMap.get("username") + ", age=" + jobDataMap.get("age") + " }",
    25. LocalDateTime.now());
    26. }
    27. }
  • 配置定时任务

      浏览器访问:http://127.0.0.1:8080/login,填写用户信息user/123456登录系统,点击菜单:系统管理>任务调度,通过新增页面,添加两个定时任务。配置完成页面如下:



      顶部按钮:定时器待机启动定时器为定时器操作按钮,即对所有定时任务有效。当定时器状态为启动中时,定时器待机显示,点击定时器状态变为待机中,所有定时任务待机;反之,所有定时任务可正常触发。

      右侧操作栏按钮:启动暂停立即执行删除,仅对当前定时任务有效。新增完的定时任务为未启动状态,点击启动按钮即可触发定时任务,点击暂停按钮即可暂停定时任务,点击立即执行按钮即可立即执行一次定时任务,点击删除按钮即可删除定时任务。
  • 点击操作按钮,观察控制台日志打印

6. 项目地址

  spring-boot-quartz-demo

SpringBoot2.x集成Quartz实现定时任务管理(持久化到数据库)的更多相关文章

  1. 用abp vNext快速开发Quartz.NET定时任务管理界面

    今天这篇文章我将通过实例代码带着大家一步一步通过abp vNext这个asp.net core的快速开发框架来进行Quartz.net定时任务调度的管理界面的开发.大伙最好跟着一起敲一下代码,当然源码 ...

  2. 【Quartz】将定时任务持久化到数据库

    之前的文章所做的demo是将定时任务的信息保存在内存中的,见以下配置 org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore 如果,我们需要在 ...

  3. Springboot2.X集成Quartz集群

    为什么要使用Quzrtz集群 在项目进行集群部署时,如果业务在执行中存在互斥关系,没有对定时任务进行统一管理,就会引起业务的多次执行,不能满足业务要求.这时就需要对任务进行管理,要保证一笔业务在所有的 ...

  4. Quartz 定时任务管理

    前言 将项目中的所有定时任务都统一管理吧,使用 quartz 定时任务 设计思路 使用 quartz 的相关jar 包,懒得去升级了,我使用的是 quart 1.6 写一个定时任务管理类 用一张数据库 ...

  5. Quartz简单实现定时任务管理(SSM+Quartz)

    首先你得有一个用Maven搭好的SSM框架,数据库用的Mysql,这里只有关于Quartz的部分.其实有大神总结的很好了,但做完后总有些地方不一样,所以写这篇作为笔记.这里先把大神的写的分享给大家:h ...

  6. SpringBoot集成Quartz(解决@Autowired空指针Null问题即依赖注入的属性为null)

    使用spring-boot作为基础框架,其理念为零配置文件,所有的配置都是基于注解和暴露bean的方式. Quartz的4个核心概念: 1.Job表示一个工作,要执行的具体内容.此接口中只有一个方法v ...

  7. 完整的定时任务解决方案Spring集成+定时任务本身管理+DB持久化+集群

    完整的定时任务解决方案Spring集成+定时任务本身管理+DB持久化+集群 maven依赖 <dependency> <groupId>org.quartz-scheduler ...

  8. Spring Boot集成quartz实现定时任务并支持切换任务数据源

    org.quartz实现定时任务并自定义切换任务数据源 在工作中经常会需要使用到定时任务处理各种周期性的任务,org.quartz是处理此类定时任务的一个优秀框架.随着项目一点点推进,此时我们并不满足 ...

  9. SpringBoot集成Quartz实现定时任务

    1 需求 在我的前后端分离的实验室管理项目中,有一个功能是学生状态统计.我的设计是按天统计每种状态的比例.为了便于计算,在每天0点,系统需要将学生的状态重置,并插入一条数据作为一天的开始状态.另外,考 ...

随机推荐

  1. 我要进大厂之大数据ZooKeeper知识点(2)

    01 我们一起学大数据 接下来是大数据ZooKeeper的比较偏架构的部分,会有一点难度,老刘也花了好长时间理解和背下来,希望对想学大数据的同学有帮助,也特别希望能够得到大佬的批评和指点. 02 知识 ...

  2. 去年去阿里面试,被问到java 多线程,我是这样手撕面试官的

    1.多线程的基本概念 1.1进程与线程 程序:是为完成特定任务,用某种语言编写的一组指令的集合,即一段静态代码,静态对象. 进程:是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,每个程 ...

  3. 通过RayFire为图形添加二次破碎效果

    在完成3D建模之后,RayFire能帮助用户制作多种类型的破碎效果,如均匀碎片.放射状碎片.木碎等效果.另外,用户还可以利用RayFire的碎片选取功能,为图形进行二次破碎,以达到增加局部碎片的效果. ...

  4. 加密PDF文件,提高文件安全性

    PDF文件的一大优点是可以设置文件的安全性,不仅可以通过证书加密的形式加密文件,还可以通过pdfFactory来设置密码的形式加密文件. 我们可以通过两种方式开启"PDF加密"来为 ...

  5. spring bean注册和实例化

    1.左边3个接口定义了基本的Ioc容器的2.HierarchicalBeanFactory增加了getParentBeanFactory()具备了双亲Ioc的管理能力3.ConfigurableBea ...

  6. web.xml之servlet与filter配置

    servlet配置 一个完整的servlet配置分为两块,< servlet >块和< servlet-mapping >块 < servlet > <ser ...

  7. LeetCode 035 Search Insert Position

    题目要求:Search Insert Position Given a sorted array and a target value, return the index if the target ...

  8. 当vue.js与其他js文件同时引用导致页面不显示的问题

    作为一个萌新,昨天学习的过程中遇到了vuejs与其他js在共同页面时引用时冲突的问题 具体如下 虽然注意到了前后顺序,但是页面还是出不来东西 我知道现实开发中可能不是这么引用,但是学习中是这么引入的, ...

  9. charles抓包使用

    Proxy ---> Proxy Setting ---> HTTP Proxy (设置代理的端口) 设备和代理处于同一局域网,并在设备端配置IP,端口,然后监听请求. 抓取本机的请求

  10. 第4.4节 Python解析与推导:列表解析、字典解析、集合解析

    一.    引言 经过前几个章节的介绍,终于把与列表解析的前置内容介绍完了,本节老猿将列表解析.字典解析.集合解析进行统一的介绍. 前面章节老猿好几次说到了要介绍列表解析,但老猿认为涉及知识层面比较多 ...