在Springboot项目中使用Quartz执行定时任务

2023-12-19

所使用的jar包

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

使用默认单机模式。单机模式中,Job 和Trigger是存放在内存中Map,通过源码可以看出 quartz-2.3.0.sources.jar!/org/quartz/simpl/RAMJobStore.java

    protected HashMap<JobKey, JobWrapper> jobsByKey = new HashMap<JobKey, JobWrapper>(1000);

    protected HashMap<TriggerKey, TriggerWrapper> triggersByKey = new HashMap<TriggerKey, TriggerWrapper>(1000);

    protected HashMap<String, HashMap<JobKey, JobWrapper>> jobsByGroup = new HashMap<String, HashMap<JobKey, JobWrapper>>(25);

    protected HashMap<String, HashMap<TriggerKey, TriggerWrapper>> triggersByGroup = new HashMap<String, HashMap<TriggerKey, TriggerWrapper>>(25);

    protected TreeSet<TriggerWrapper> timeTriggers = new TreeSet<TriggerWrapper>(new TriggerWrapperComparator());

    protected HashMap<String, Calendar> calendarsByName = new HashMap<String, Calendar>(25);

    protected Map<JobKey, List<TriggerWrapper>> triggersByJob = new HashMap<JobKey, List<TriggerWrapper>>(1000);

    protected final Object lock = new Object();

    protected HashSet<String> pausedTriggerGroups = new HashSet<String>();

    protected HashSet<String> pausedJobGroups = new HashSet<String>();

    protected HashSet<JobKey> blockedJobs = new HashSet<JobKey>();

官网支持集群是把这些数据存放在mysql, 也有人改成使用 redis存放这些数据

结合Springboot

注意此处Job要扩展QuartzJobBean , 只有这样才能使用@Autowired进来的其它service实例,否则要显式地new 一个相应service的实例

@Slf4j
@Component
@DisallowConcurrentExecution
public class MyJob extends QuartzJobBean {

    @Autowired
    ApplicationService applicationService;


    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        log.info("触发MyJob=========");

        applicationService.doSomething();

        Trigger trigger = jobExecutionContext.getTrigger();
        log.info("触发MyJob==========the trigger time : {}, 当前时间: {}",
                trigger.getStartTime(), new Date());

    }
}

设置Schedule

public interface QuartzService {

    void startSchedule ();

    void deployMySchedule (Myparams params) throws SchedulerException, ParseException;

}

定义trigger并设置Schedule


@Slf4j
@Service
public class QuartzServiceImpl implements QuartzService {


    private final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss", Locale.ENGLISH);

    private final String myJobName = "MyJobName";
    private final String QUARTZ_JOB_GROUP_SUFFIX = "Group";


    @Autowired
    private Scheduler scheduler;

    @Override
    public void deployMySchedule (MyParams params) throws SchedulerException, ParseException {

        if (checkMyJobExists(params)) {
            log.warn("已存在{}的任务", params.getJobName());
            updateMyJobTrigger(params);
        } else {
            log.info("不存在{}的任务, 添加任务", params.getJobName());
            arrangeNewMyJobSchedule(params);
        }
    }


    @Override
    public void startSchedule ()  {

        try {
            scheduler.start();
        } catch (SchedulerException e) {
            log.error("Quartz 启动Schedule 出现异常 ==== ", e);
        }
    }

    /**
     * {
     *     "myJobName": "my.customized.job.name",
     *     "triggerTime": "2023-09-21 23:23:23"
     * }
     * @param params
     * @throws SchedulerException
     */

    private void arrangeNewMyJobSchedule (MyParams params) throws SchedulerException, ParseException {

        String jobName = params.getMyJobName();
        String jobGroup = params.getMyJobName()+QUARTZ_JOB_GROUP_SUFFIX;
        String triggerTime = params.getTriggerTime();

        // TODO change triggerTime to Date()
        Date date = formatter.parse(triggerTime);
        log.info("{} 添加定时任务", params.getMyJobName());

        JobKey theJobKey = jobKey(jobName, jobGroup);

        JobDetail job = JobBuilder.newJob(MyJob.class)
                .usingJobData("myJobName", params.getMyJobName())
                .withIdentity(theJobKey)
                .build();

        // Simple trigger without repeating
        // withMisfireHandlingInstructionNextWithRemainingCount()
        // Does nothing, misfired execution is ignored and there is no next execution.
        // Use this instruction when you want to completely discard the misfired execution.
        // Example scenario: the trigger was suppose to start recording of a program in TV.
        // There is no point of starting recording when the trigger misfired and is already 2 hours late.
        // Discarded but job not removed
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey(jobName, jobGroup))
                .startAt(date)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withMisfireHandlingInstructionNextWithRemainingCount())
                .build();

        // 使用触发器调度任务的执行
        scheduler.scheduleJob(job, trigger);
        log.info("{} 设置任务触发时间为: {}, 配置触发器", params.getMyJobName(), triggerTime);

        log.info("schedule: scheduleName {}, scheduleInstanceId {} ", scheduler.getSchedulerName(),
                scheduler.getSchedulerInstanceId());

    }

    private void updateMyJobTrigger (MyParams params) throws SchedulerException, ParseException {
        String jobName = params.getMyJobName();
        String jobGroup = params.getMyJobName()+"Group";
        String triggerTime = params.getTriggerTime();

        updateJobTrigger(jobName, jobGroup, triggerTime);
    }

    private boolean checkMyJobExists (MyParams params) throws SchedulerException {
        String jobName = params.getMyJobName();
        String jobGroup = params.getMyJobName()+QUARTZ_JOB_GROUP_SUFFIX;
        JobKey jobKey = new JobKey(jobName, jobGroup);
        return scheduler.checkExists(jobKey);
    }


    private void updateJobTrigger (String jobName, String jobGroup, String triggerTime) throws ParseException {

        // TODO change triggerTime to Date()
        Date date = formatter.parse(triggerTime);

        try {
            TriggerKey triggerKey = triggerKey(jobName, jobGroup);
            SimpleTrigger oldTrigger = (SimpleTrigger) scheduler.getTrigger(triggerKey);

            // Simple trigger without repeating
            // withMisfireHandlingInstructionNextWithRemainingCount()
            // 如果给的时间小于当前时间, 只重新配置触发器, 并不触发, 同时 jobdetail 也没有删除
            Trigger newTrigger = oldTrigger.getTriggerBuilder().withIdentity(triggerKey)
                    .withSchedule(simpleSchedule()
                            .withMisfireHandlingInstructionNextWithRemainingCount())
                    .startAt(date)
                    .build();
            // 重启触发器
            scheduler.rescheduleJob(oldTrigger.getKey(), newTrigger);
            log.info("Job {} 任务触发时间更新为: {}, 重新配置触发器", jobName, triggerTime);

        } catch (SchedulerException e) {
            e.printStackTrace();
        }

    }
}

在项目启动时加载schedule

@Slf4j
@Component
public class MyQuartzScheduleStart {

    @Autowired
    QuartzService quartzService;

    @PostConstruct
    public void init() {
        log.info("Quartz 调度任务开始 =====");
        quartzService.startSchedule();
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在Springboot项目中使用Quartz执行定时任务 的相关文章

  • 如何从二维数组中仅打印单个列?

    我正在编写这个程序 我必须只打印二维数组的一列 而不是两者 for int i 0 i lt sjf length i for int j 0 j lt sjf i length j System out printf 5d 4s sjf
  • 使用 REST API 实现属性/字段级安全

    我正在为支持多租户授权模型的 REST API 构建概念验证 该模型不仅控制用户可以访问哪些对象 还控制对象中的字段 此模型的目标是确保租户管理员只能修改其租户并且只能查看允许的对象属性 我有一个正在开发的现有代码库 可在以下位置公开获取
  • 在 Java 中,三个 true 输入的 XOR 返回 true。为什么?

    下面的代码 System out println 1 0 0 true false false System out println 1 0 1 true false true System out println 1 1 0 true t
  • Android - 检测电容式触摸屏上的触摸压力?

    我听说过 MotionEvent e float press e getPressure 但这只会在没有触摸时返回 0 当我的手指触摸屏幕时返回 1 是否可以找到手指在触摸电容屏上施加的压力值 或者我的预感是否正确 即这只适用于电阻屏幕 M
  • 正则表达式查找两个字符之间的内部匹配

    环境 Java 我想匹配两个字符串之间的字符 这是一个例子 foo
  • 请放心,如何在 POST 请求后从响应正文中提取生成的令牌并将其设置到标头

    执行任何请求 我需要执行身份验证POST请求正文 username somename password somepass 标头Content Type application json这给了我一个带有生成令牌的响应 我需要将其粘贴为第二个标
  • 创建UML图时应该编写构造函数吗?

    我有一项作业要求我为实际的 Java 程序创建 UML 图 但程序中有几个构造函数方法 我很困惑 我是否应该将这些构造函数方法添加到图中 根据 UML 规范 2 5 版第 11 4 4 节 构造函数是一个具有所属类类型的单个返回结果参数的操
  • 如何将多个值存储到一个键(java)

    我搜索一个可以存储多个键值对的数据结构 数据基本上是这样的 1 value 1 2 value 2 于是我想到了使用HashMap 遗憾的是 这对我不起作用 因为一个键可能会出现多个值 在上面的例子中 1 value 2 可能是另一个条目
  • 如何用Java捕获音频数据

    我想访问我的麦克风用 Java 录制的音频数据 我该怎么做呢 我的目标是保存录制的音频数据并同时向用户播放 如果您不需要 JMF 中的任何附加功能 我会避免使用它 因为开发已经停止 最后一个版本是 2004 年 它与 Java 6 存在兼容
  • Java DocumentBuilder - XML 文件中的缩进错误

    我尝试使用 DocumentBuilder 用 Ja va 编写一个简单的 XML 文件 我期望 XML 文件如下所示
  • Jlist 自定义渲染器

    我正在尝试添加一个我猜你会称其为列表中每个项目的子列表 我构建了一个自定义渲染器 它提供以下输出 正如你所看到的 有些东西不对劲 我没能找到问题的答案 我猜我需要更改面板布局中的某些内容才能获得正确的结果 但不知道是什么 https i s
  • 调用本机方法时返回 java.lang.UnsatisfiedLinkError

    我正在尝试为第三方 DLL 制作 Java 包装器 我创建了自己的 DLL 充当 JNI 和第三方 DLL 之间的中间人 在java中我加载这个DLL很好但是错误java lang UnsatisfiedLinkError sixense
  • Java 需要一个 FileSet 包/类

    任何人都可以建议 Java 中的 FileSet 包 类吗 我所说的 FileSet 是指文件和目录的集合以及正则表达式支持的包含和排除规则 类似于 Apache Ant 谢谢 Apache 公共 IO文件工具 http commons a
  • Jersey:返回字符串列表

    我尝试以 JSON 和 XML 形式返回 Jersey 中的字符串列表 我以为这会是微不足道的 我的第一次尝试是写这样的东西 GET Produces MediaType APPLICATION JSON MediaType APPLICA
  • 从java小程序获取正确的本地IP地址

    我想从我的 java 小程序确定本地 IP 地址 问题是当同一台机器上有多个 IP 地址时 该机器具有 LAN 和互联网连接 掌上电脑 VMWare 这是我的测试 public static void main String args tr
  • 如何从项目文件夹中的 jlabel 上设置图像?

    我正在尝试制作一个 Java 桌面应用程序 我想设置一个图像JLabel 我正在使用 NetBeans 从我的项目文件夹中 我的目录结构是 F gt MARKET src lib src defaultpackage demo java i
  • 将菜单添加到空活动

    我在 Android Studio 中制作了一个 Android 应用程序 并想在其上创建一个选项菜单 我将其创建为一个空活动 现在意识到我最好创建一个空白活动来获取选项菜单 无论如何 是否可以在空活动中创建选项菜单 如果有人能给我指出一个
  • 寻找基于循环固定大小数组的双端队列

    我正在寻找一个Deque其具有以下特点 它有固定的大小 如果我在头 尾添加元素 则另一端的元素会丢失 它是基于数组的 所以我可以在恒定时间内访问随机元素 我可以在前面或末尾添加元素 双端队列 我检查了Deque的实施JCF但我没有找到任何合
  • 应用程序中 GC 长时间暂停

    我当前运行的应用程序需要最大堆大小为 16GB 目前我使用以下标志来处理垃圾收集 XX UseParNewGC XX UseConcMarkSweepGC XX CMSInitiatingOccupancyFraction 50 XX Di
  • JVM锯齿状空闲进程

    我目前正在进行一项涉及 JVM 及其内存使用工作原理的研究 我不明白的是 JVM在空闲时用什么填充它的内存 只是为了在堆几乎达到时释放它 为什么使用的内存不只有一条平线 顺便说一句 这个 java 应用程序托管在 glassfish 上 但

随机推荐

  • 搭建电子商务网站建设步骤

    随着目前电子商务网站开发技术的迭代 电商网站的交互设计得到了很大程度的提升 可以认为现在的新型的商城平台都呈现出交互效果 那么电子商务网站建设步骤包括什么呢 电子商务网站建设第一步 网站的规划与设计 电子商务网站算是一个比较复杂的系统 电商
  • 如何在Linux系统中删除文件或目录?

    Linux作为流行的操作系统之一 许多公司和组织都在使用Linux来运行其关键业务和服务 例如谷歌 亚马逊和Facebook等 在Linux中 删除文件和目录是基本操作 那么该如何实现这一功能呢 以下是详细的内容 一 使用命令行删除文件 如
  • 渗透测试报告怎么写?

    1 准备好渗透测试记录 测试记录是执行过程的日志 在每日测试工作结束后 应将当日的成果做成记录 虽然内容不必太过细致 但测试的重点必须记录在案 拟检测的项目 使用的工具或方法 检测过程描述 检测结果说明 过程的重点截图 有结果的画面 2 撰
  • 什么是深度学习的无监督学习与有监督学习

    无监督学习 深度学习中的无监督学习方法是一种训练算法 它在没有标注输出的情况下从输入数据中学习模式和特征 这种方法的核心是探索和理解数据的内在结构和分布 而不是通过已知的输出来指导学习过程 无监督学习在深度学习领域有许多不同的形式和应用 以
  • binlog日志,二进制日志的简介

    binlog的bin就暴露了他是二进制的文件 你用vi或者vim是没办法读的 得用专门的方式 比如mysqlbinlog工具 那么binlog其实只要了解几点应该就足够了 Q 首先 binlog记录的是啥呢 A 记录的是数据库的修改过程 注
  • 2023_Spark_实验二十八:Flume部署及配置

    实验目的 熟悉掌握Flume部署及配置 实验方法 通过在集群中部署Flume 掌握Flume配置 实验步骤 一 Flume简介 Flume是一种分布式的 可靠的和可用的服务 用于有效地收集 聚合和移动大量日志数据 它有一个简单灵活的基于流数
  • 2023_Spark_实验二十九:Flume配置KafkaSink

    实验目的 掌握Flume采集数据发送到Kafka的方法 实验方法 通过配置Flume的KafkaSink采集数据到Kafka中 实验步骤 一 明确日志采集方式 一般Flume采集日志source有两种方式 1 Exec类型的Source 可
  • 线性连续控制系统

    线性连续控制系统 可以用线性微分方程表示 形式为 上式中 是被控制量 是系统的输入量 线性定常连续系统 当系数 和 常数时 称为定常系统 线性时变连续系统 当 和 随时间变化时 称为时变系统 线性定常连续系统按照输入量 的变化规律不同 又分
  • PyCharm中缓存有何作用?如何清理?

    在使用pycharm开发软件的过程中 我们经常会遇到卡顿 运行慢等问题 这时第一应对措施就是清理缓存 从而提高效率 那么pycharm如何清理缓存 以下是常用方法介绍 PyCharm缓存的作用 在使用PyCharm进行开发时 PyCharm
  • 推动行业未来的八个数字化转型趋势

    根据 Gartner 最新估计 在2023 年 已有40 的组织把虚拟体验与物理体验结合起来 以提高员工生产力和客户覆盖范围 而到 2024 年 工业企业将通过将自我管理技术与重新设计的运营流程相结合 将运营成本降低 30 到 2025 年
  • 低代码助力全栈开发

    目录 低代码功能展示 1 拖拽式 UI 组件 2 更快的开发速度 3 敏捷原型设计 4 与数据库集成 低代码开发工具正变得日益强大 它正不断弥合着前后端开发之间的差距 对于后端来说 基于低代码平台开发应用时 完全不用担心 前端的打包 部署
  • JavaOOP篇----第三篇

    系列文章目录 文章目录 系列文章目录 前言 一 标识符的命名规则 二 instanceof关键字的作用 三 什么是隐式转换 什么是显式转换 前言 前些天发现了一个巨牛的人工智能学习网站 通俗易懂 风趣幽默 忍不住分享一下给大家 点击跳转到网
  • Linux中动态路由协议有哪些?

    Linux动态路由是一种在Linux操作系统中实现动态路由的机制 动态路由是指路由器能够根据网络的变化自动更新路由表 以实现更高效的数据传输 在Linux中 动态路由可以通过配置路由规则来实现 那么Linux中动态路由协议有哪些 以下是具体
  • kubernetes入门到进阶(2)

    被隔离的进程 一起来看看容器的本质 大家好 我们继续来一起学习k8s 在上一个章节里 我们初步了解了容器技术 在Linux虚拟机里安装了当前最流行的容器docker 还是用了docker ps docker run 等命令简单操作了容器 广
  • 【计算机图形学】PointNet文章的简单理解与运用,点云特征提取

    PointNet论文原文 PointNet Deep Learning on Point Sets for 3D Classification and Segmentation PointNet官方代码是使用tensorflow实现的 Po
  • 数据库学习日常案例20231218-oracle 19RAC hip远程注册服务到scan listener分析

    问题 用户一套Oracle19c RAC集群 出现一个奇怪的现象 通过SCAN IP访问的连接会话都集中在节点一实例 而且用户并没有做任何的节点服务访问去控制会话的连接节点 比如常见的通过集群的高可用服务去控制应用访问连接集中在同一节点 从
  • 渗透测试与安全测试主要区别是什么?

    在网络安全体系中 有很多专业术语 而且部分专业术语在名字上有很大的相似之处 因此很多小伙伴将它们混淆在一起 比如渗透测试和安全测试 这两个概念就经常被混淆在一起 那么什么是渗透测试和安全测试 有何区别 渗透测试是通过模拟恶意黑客的攻击方法
  • 转移mysql中的数据

    目录 1 mysqldump 2 将数据库中的数据转换为一个sql文件 3 执行sql文件 1 mysqldump 转移数据需要用到mysqldump 默认情况下mysqldump会自动被安装上 如果没有用不了 建议重新安装一下 参考 my
  • 4.docker镜像及相关命令

    目录 1 查看所有镜像 docker images 1 1 基本用法 1 2 docker images q 只显示所有镜像ID 1 3 docker images f 筛选条件 q 只显示符合条件的所有镜像ID 1 4 docker im
  • 在Springboot项目中使用Quartz执行定时任务

    所使用的jar包