定制app开发SpringBoot整合Quartz

目录

1. Quartz

1.1. Quartz 简介

QuartzOpenSymphony 定制app开发开源组织在 Job Scheduling 定制app开发领域又一个开源项目,是完全由 Java 定制app开发开发的一个开源任务日定制app开发程管理系统,“定制app开发任务进度管理器”定制app开发就是一个在预先确定(定制app开发被纳入日程)定制app开发的时间到达时,负责执行(或者通知)定制app开发其他软件组件的系统。 Quartz 定制app开发是一个开源的作业调度框架,它完全由 Java 写成,定制app开发并设计用于 J2SEJ2EE 应用中,定制app开发它提供了巨大的灵活性定制app开发而不牺牲简单性

定制app开发当定时任务愈加复杂时,使用 Spring 注解 @Schedule 定制app开发已经不能满足业务需要

定制app开发在项目开发中,定制app开发经常需要定时任务来帮定制app开发助我们来做一些内容,如定时派息、跑批对账、将任务纳入日程或者从日程中取消,开始,停止,暂停日程进度等。SpringBoot 中现在有两种方案可以选择,第一种是 SpringBoot 内置的方式简单注解就可以使用,当然如果需要更复杂的应用场景还是得 Quartz 上场,Quartz 目前是 Java 体系中最完善的定时方案

官方网站:

1.2. Quartz 优点

  • 丰富的 Job 操作 API
  • 支持多种配置
  • SpringBoot 无缝集成
  • 支持持久化
  • 支持集群
  • Quartz 还支持开源,是一个功能丰富的开源作业调度库,可以集成到几乎任何 Java 应用程序中

1.3. 核心概念

  • SchedulerQuartz 中的任务调度器,通过 TriggerJobDetail 可以用来调度、暂停和删除任务。调度器就相当于一个容器,装载着任务和触发器,该类是一个接口,代表一个 Quartz 的独立运行容器,TriggerJobDetail 可以注册到 Scheduler 中,两者在 Scheduler 中拥有各自的组及名称,组及名称是 Scheduler 查找定位容器中某一对象的依据,Trigger 的组及名称必须唯一,JobDetail 的组和名称也必须唯一(但可以和 Trigger 的组和名称相同,因为它们是不同类型的)
  • TriggerQuartz 中的触发器,是一个类,描述触发 Job 执行的时间触发规则,主要有 SimpleTriggerCronTrigger 这两个子类。当且仅当需调度一次或者以固定时间间隔周期执行调度,SimpleTrigger 是最适合的选择;而 CronTrigger 则可以通过 Cron 表达式定义出各种复杂时间规则的调度方案:如工作日周一到周五的 15:00 ~ 16:00 执行调度等
  • JobDetailQuartz 中需要执行的任务详情,包括了任务的唯一标识和具体要执行的任务,可以通过 JobDataMap 往任务中传递数据
  • JobQuartz 中具体的任务,包含了执行任务的具体方法。是一个接口,只定义一个方法 execute() 方法,在实现接口的 execute() 方法中编写所需要定时执行的 Job

当然可以这样快速理解:

  • job:任务 - 你要做什么事
  • Trigger:触发器 - 你什么时候去做
  • Scheduler:任务调度 - 你什么时候需要做什么事

四者其关系如下图所示


Job 为作业的接口,为任务调度的对象;JobDetail 用来描述 Job 的实现类及其他相关的静态信息;Trigger 做为作业的定时管理工具,一个 Trigger 只能对应一个作业实例,而一个作业实例可对应多个触发器;Scheduler 做为定时任务容器,是 Quartz 最上层的东西,它提携了所有触发器和作业,使它们协调工作,每个 Scheduler 都存有 JobDetailTrigger 的注册,一个 Scheduler 中可以注册多个 JobDetail 和多个 Trigger

1.4. Quartz 的作业存储类型

  • RAMJobStoreRAM 也就是内存,默认情况下 Quartz 会将任务调度存储在内存中,这种方式性能是最好的,因为内存的速度是最快的。不好的地方就是数据缺乏持久性,但程序崩溃或者重新发布的时候,所有运行信息都会丢失
  • JDBC 作业存储:存到数据库之后,可以做单点也可以做集群,当任务多了之后,可以统一进行管理,随时停止、暂停、修改任务。关闭或者重启服务器,运行的信息都不会丢失。缺点就是运行速度快慢取决于连接数据库的快慢

1.5. Cron 表达式

Cron 表达式是一个字符串,包括 6~7 个时间元素,在 Quartz 中可以用于指定任务的执行时间

1.5.1. Cron 语法

Seconds Minutes Hours DayofMonth Month DayofWeek  秒	  分钟	 小时   日期天/日  日期月份  星期
  • 1
  • 2

1.5.2. Cron 语法中每个时间元素的说明

时间元素可出现的字符有效数值范围
Seconds, - * /0-59
Minutes, - * /0-59
Hours, - * /0-23
DayofMonth, - * / ? L W0-31
Month, - * /1-12
DayofWeek, - * / ? L #1-7或SUN-SAT

1.5.3. Cron 语法中特殊字符说明

字符作用举例
,列出枚举值在Minutes域使用5,10,表示在5分和10分各触发一次
-表示触发范围在Minutes域使用5-10,表示从5分到10分钟每分钟触发一次
*匹配任意值在Minutes域使用*, 表示每分钟都会触发一次
/起始时间开始触发,每隔固定时间触发一次在Minutes域使用5/10,表示5分时触发一次,每10分钟再触发一次
?在DayofMonth和DayofWeek中,用于匹配任意值在DayofMonth域使用?,表示每天都触发一次
#在DayofMonth中,确定第几个星期几1#3表示第三个星期日
L表示最后在DayofWeek中使用5L,表示在最后一个星期四触发
W表示有效工作日(周一到周五)在DayofMonth使用5W,如果5日是星期六,则将在最近的工作日4日触发一次

1.5.4. 在线 Cron 表达式生成器

其实 Cron 表达式无需多记,需要使用的时候直接使用在线生成器就可以了,地址:

2. Springboot 整合 Quartz

  • SpringBoot 版本:2.0.9.RELEASE
  • MySQL 版本:5.7.35

2.1. 数据库表准备

Quartz 存储任务信息有两种方式,使用内存或者使用数据库来存储,这里我们采用 MySQL 数据库存储的方式,首先需要新建 Quartz 的相关表,sql 脚本下载地址:,名称为 tables_mysql.sql,创建成功后数据库中多出 11 张表

2.2. Maven 主要依赖

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-quartz</artifactId></dependency><!-- 5.1.* 版本适用于MySQL Server的5.6.*、5.7.*和8.0.* --><dependency>	<groupId>mysql</groupId>    <artifactId>mysql-connector-java</artifactId>	<version>5.1.38</version></dependency><dependency>	<groupId>com.alibaba</groupId>	<artifactId>druid-spring-boot-starter</artifactId>    <version>1.1.10</version></dependency><!--mybatis--><dependency>	<groupId>org.mybatis.spring.boot</groupId>	<artifactId>mybatis-spring-boot-starter</artifactId>	<version>1.3.2</version></dependency><!--pagehelper分页--><dependency>    <groupId>com.github.pagehelper</groupId>    <artifactId>pagehelper-spring-boot-starter</artifactId>	<version>1.3.0</version></dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

这里使用 druid 作为数据库连接池,Quartz 默认使用 c3p0

2.3. 配置文件

2.3.1. quartz.properties

默认情况下,Quartz 会加载 classpath 下的 quartz.properties 作为配置文件。如果找不到,则会使用 quartz 框架自己 jar 包下 org/quartz 底下的 quartz.properties 文件

#主要分为scheduler、threadPool、jobStore、dataSource等部分org.quartz.scheduler.instanceId=AUTOorg.quartz.scheduler.instanceName=DefaultQuartzScheduler#如果您希望Quartz Scheduler通过RMI作为服务器导出本身,则将“rmi.export”标志设置为true#在同一个配置文件中为'org.quartz.scheduler.rmi.export'和'org.quartz.scheduler.rmi.proxy'指定一个'true'值是没有意义的,如果你这样做'export'选项将被忽略org.quartz.scheduler.rmi.export=false#如果要连接(使用)远程服务的调度程序,则将“org.quartz.scheduler.rmi.proxy”标志设置为true。您还必须指定RMI注册表进程的主机和端口 - 通常是“localhost”端口1099org.quartz.scheduler.rmi.proxy=falseorg.quartz.scheduler.wrapJobExecutionInUserTransaction=false#实例化ThreadPool时,使用的线程类为SimpleThreadPoolorg.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool#threadCount和threadPriority将以setter的形式注入ThreadPool实例#并发个数  如果你只有几个工作每天触发几次 那么1个线程就可以,如果你有成千上万的工作,每分钟都有很多工作 那么久需要50-100之间.#只有1到100之间的数字是非常实用的org.quartz.threadPool.threadCount=5#优先级 默认值为5org.quartz.threadPool.threadPriority=5#可以是“true”或“false”,默认为falseorg.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true#在被认为“misfired”(失火)之前,调度程序将“tolerate(容忍)”一个Triggers(触发器)将其下一个启动时间通过的毫秒数。默认值(如果您在配置中未输入此属性)为60000(60秒)org.quartz.jobStore.misfireThreshold=5000# 默认存储在内存中,RAMJobStore快速轻便,但是当进程终止时,所有调度信息都会丢失#org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore#持久化方式,默认存储在内存中,此处使用数据库方式org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX#您需要为JobStore选择一个DriverDelegate才能使用。DriverDelegate负责执行特定数据库可能需要的任何JDBC工作# StdJDBCDelegate是一个使用“vanilla”JDBC代码(和SQL语句)来执行其工作的委托,用于完全符合JDBC的驱动程序org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate#可以将“org.quartz.jobStore.useProperties”配置参数设置为“true”(默认为false),以指示JDBCJobStore将JobDataMaps中的所有值都作为字符串,#因此可以作为名称 - 值对存储而不是在BLOB列中以其序列化形式存储更多复杂的对象。从长远来看,这是更安全的,因为您避免了将非String类序列化为BLOB的类版本问题org.quartz.jobStore.useProperties=true#表前缀org.quartz.jobStore.tablePrefix=QRTZ_#数据源别名,自定义org.quartz.jobStore.dataSource=qzDS#使用阿里的druid作为数据库连接池org.quartz.dataSource.qzDS.connectionProvider.class=org.example.config.DruidPoolingconnectionProviderorg.quartz.dataSource.qzDS.URL=jdbc:mysql://127.0.0.1:3306/test_quartz?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTCorg.quartz.dataSource.qzDS.user=rootorg.quartz.dataSource.qzDS.password=123456org.quartz.dataSource.qzDS.driver=com.mysql.jdbc.Driverorg.quartz.dataSource.qzDS.maxConnections=10#设置为“true”以打开群集功能。如果您有多个Quartz实例使用同一组数据库表,则此属性必须设置为“true”,否则您将遇到破坏#org.quartz.jobStore.isClustered=false
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

关于配置详细解释:

也可以查看官网:

2.3.2. application.properties

server.port=8080#JDBC 配置:MySQL Server 版本为 5.7.35spring.datasource.druid.url=jdbc:mysql://127.0.0.1:3306/test_quartz?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTCspring.datasource.druid.username=rootspring.datasource.druid.password=123456spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driverspring.datasource.type=com.alibaba.druid.pool.DruidDataSource#druid 连接池配置spring.datasource.druid.initial-size=3spring.datasource.druid.min-idle=3spring.datasource.druid.max-active=10spring.datasource.druid.max-wait=60000#指定 mapper 文件路径mybatis.mapper-locations=classpath:org/example/mapper/*.xmlmybatis.configuration.cache-enabled=true#开启驼峰命名mybatis.configuration.map-underscore-to-camel-case=true#打印 SQL 语句mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

2.4. quartz 配置类 QuartzConfig

@Configurationpublic class QuartzConfig implements SchedulerFactoryBeanCustomizer {    @Bean    public Properties properties() throws IOException {        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();        // 对quartz.properties文件进行读取        propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));        // 在quartz.properties中的属性被读取并注入后再初始化对象        propertiesFactoryBean.afterPropertiesSet();        return propertiesFactoryBean.getObject();    }    @Bean    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();        schedulerFactoryBean.setQuartzProperties(properties());        return schedulerFactoryBean;    }    /*     * quartz初始化监听器     */    @Bean    public QuartzInitializerListener executorListener() {        return new QuartzInitializerListener();    }    /*     * 通过SchedulerFactoryBean获取Scheduler的实例     */    @Bean    public Scheduler scheduler() throws IOException {        return schedulerFactoryBean().getScheduler();    }    /**     * 使用阿里的druid作为数据库连接池     */    @Override    public void customize(@NotNull SchedulerFactoryBean schedulerFactoryBean) {        schedulerFactoryBean.setStartupDelay(2);        schedulerFactoryBean.setAutoStartup(true);        schedulerFactoryBean.setOverwriteExistingJobs(true);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

2.5. 创建任务类 HelloJob

@Slf4jpublic class HelloJob implements Job {    @Override    public void execute(JobExecutionContext jobExecutionContext) {        QuartzService quartzService = (QuartzService) SpringUtil.getBean("quartzServiceImpl");        PageInfo<JobAndTriggerDto> jobAndTriggerDetails = quartzService.getJobAndTriggerDetails(1, 10);        log.info("任务列表总数为:" + jobAndTriggerDetails.getTotal());        log.info("Hello Job执行时间: " + DateUtil.now());    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.6. 业务 Service

具体的 QuartzService 接口这里不在赘述,可以查看后面的源码

@Slf4j@Servicepublic class QuartzServiceImpl implements QuartzService {    @Autowired    private JobDetailMapper jobDetailMapper;    @Autowired    private Scheduler scheduler;    @Override    public PageInfo<JobAndTriggerDto> getJobAndTriggerDetails(Integer pageNum, Integer pageSize) {        PageHelper.startPage(pageNum, pageSize);        List<JobAndTriggerDto> list = jobDetailMapper.getJobAndTriggerDetails();        PageInfo<JobAndTriggerDto> pageInfo = new PageInfo<>(list);        return pageInfo;    }    /**     * 新增定时任务     *     * @param jName 任务名称     * @param jGroup 任务组     * @param tName 触发器名称     * @param tGroup 触发器组     * @param cron cron表达式     */    @Override    public void addjob(String jName, String jGroup, String tName, String tGroup, String cron) {        try {            // 构建JobDetail            JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)                    .withIdentity(jName, jGroup)                    .build();            // 按新的cronExpression表达式构建一个新的trigger            CronTrigger trigger = TriggerBuilder.newTrigger()                    .withIdentity(tName, tGroup)                    .startNow()                    .withSchedule(CronScheduleBuilder.cronSchedule(cron))                    .build();            // 启动调度器            scheduler.start();            scheduler.scheduleJob(jobDetail, trigger);        } catch (Exception e) {            log.info("创建定时任务失败" + e);        }    }    @Override    public void pausejob(String jName, String jGroup) throws SchedulerException {        scheduler.pauseJob(JobKey.jobKey(jName, jGroup));    }    @Override    public void resumejob(String jName, String jGroup) throws SchedulerException {        scheduler.resumeJob(JobKey.jobKey(jName, jGroup));    }    @Override    public void rescheduleJob(String jName, String jGroup, String cron) throws SchedulerException {        TriggerKey triggerKey = TriggerKey.triggerKey(jName, jGroup);        // 表达式调度构建器        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);        // 按新的cronExpression表达式重新构建trigger        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();        // 按新的trigger重新设置job执行,重启触发器        scheduler.rescheduleJob(triggerKey, trigger);    }    @Override    public void deletejob(String jName, String jGroup) throws SchedulerException {        scheduler.pauseTrigger(TriggerKey.triggerKey(jName, jGroup));        scheduler.unscheduleJob(TriggerKey.triggerKey(jName, jGroup));        scheduler.deleteJob(JobKey.jobKey(jName, jGroup));    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

2.7. Controller

@Slf4j@Controller@RequestMapping(path = "/quartz")public class QuartzController {    @Autowired    private QuartzService quartzService;    /**     * 新增定时任务     *     * @param jName 任务名称     * @param jGroup 任务组     * @param tName 触发器名称     * @param tGroup 触发器组     * @param cron cron表达式     * @return ResultMap     */    @PostMapping(path = "/addjob")    @ResponseBody    public ResultMap addjob(String jName, String jGroup, String tName, String tGroup, String cron) {        try {            quartzService.addjob(jName, jGroup, tName, tGroup, cron);            return new ResultMap().success().message("添加任务成功");        } catch (Exception e) {            e.printStackTrace();            return new ResultMap().error().message("添加任务失败");        }    }    /**     * 暂停任务     *     * @param jName 任务名称     * @param jGroup 任务组     * @return ResultMap     */    @PostMapping(path = "/pausejob")    @ResponseBody    public ResultMap pausejob(String jName, String jGroup) {        try {            quartzService.pausejob(jName, jGroup);            return new ResultMap().success().message("暂停任务成功");        } catch (SchedulerException e) {            e.printStackTrace();            return new ResultMap().error().message("暂停任务失败");        }    }    /**     * 恢复任务     *     * @param jName 任务名称     * @param jGroup 任务组     * @return ResultMap     */    @PostMapping(path = "/resumejob")    @ResponseBody    public ResultMap resumejob(String jName, String jGroup) {        try {            quartzService.resumejob(jName, jGroup);            return new ResultMap().success().message("恢复任务成功");        } catch (SchedulerException e) {            e.printStackTrace();            return new ResultMap().error().message("恢复任务失败");        }    }    /**     * 重启任务     *     * @param jName 任务名称     * @param jGroup 任务组     * @param cron cron表达式     * @return ResultMap     */    @PostMapping(path = "/reschedulejob")    @ResponseBody    public ResultMap rescheduleJob(String jName, String jGroup, String cron) {        try {            quartzService.rescheduleJob(jName, jGroup, cron);            return new ResultMap().success().message("重启任务成功");        } catch (SchedulerException e) {            e.printStackTrace();            return new ResultMap().error().message("重启任务失败");        }    }    /**     * 删除任务     *     * @param jName 任务名称     * @param jGroup 任务组     * @return ResultMap     */    @PostMapping(path = "/deletejob")    @ResponseBody    public ResultMap deletejob(String jName, String jGroup) {        try {            quartzService.deletejob(jName, jGroup);            return new ResultMap().success().message("删除任务成功");        } catch (SchedulerException e) {            e.printStackTrace();            return new ResultMap().error().message("删除任务失败");        }    }    /**     * 查询任务     *     * @param pageNum 页码     * @param pageSize 每页显示多少条数据     * @return Map     */    @GetMapping(path = "/queryjob")    @ResponseBody    public ResultMap queryjob(Integer pageNum, Integer pageSize) {        PageInfo<JobAndTriggerDto> pageInfo = quartzService.getJobAndTriggerDetails(pageNum, pageSize);        Map<String, Object> map = new HashMap<>();        if (!StringUtils.isEmpty(pageInfo.getTotal())) {            map.put("JobAndTrigger", pageInfo);            map.put("number", pageInfo.getTotal());            return new ResultMap().success().data(map).message("查询任务成功");        }        return new ResultMap().fail().message("查询任务成功失败,没有数据");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127

2.8. 接口测试

2.8.1. 新增定时任务

postman 测试如下


数据库数据展示如下



同样,我们的任务类 HelloJob 也开始执行了,控制台日志如下

2.8.2. 停止项目,再启动运行

可以看到项目中 HelloJob 的任务依然在运行,这就是 quartz 数据库持久化的好处


源码:

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发