news 2026/5/6 14:04:35

Spring Boot项目里,用若依的Quartz定时任务发个邮件/清理日志,保姆级配置

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot项目里,用若依的Quartz定时任务发个邮件/清理日志,保姆级配置

Spring Boot整合若依Quartz实现定时任务全流程实战

在后台管理系统开发中,定时任务几乎是每个项目都绕不开的刚需功能。无论是凌晨的日志清理、定期的数据备份,还是业务数据的定时同步,都需要一个稳定可靠的定时任务系统来支撑。Spring Boot生态中,Quartz作为老牌的任务调度框架,与若依(RuoYi)这一流行后台管理系统结合,能快速搭建出企业级的任务调度平台。

1. 环境准备与基础配置

1.1 项目初始化

首先确保你已经有一个基于Spring Boot和若依框架搭建的基础项目。若依框架已经内置了对Quartz的支持,我们只需要在pom.xml中添加必要的依赖:

<!-- Quartz核心依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> <!-- 若依框架核心 --> <dependency> <groupId>com.ruoyi</groupId> <artifactId>ruoyi-common</artifactId> <version>${ruoyi.version}</version> </dependency>

1.2 数据库表结构

若依框架已经为我们准备好了定时任务相关的表结构,主要涉及sys_job表:

CREATE TABLE `sys_job` ( `job_id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务ID', `job_name` varchar(64) NOT NULL DEFAULT '' COMMENT '任务名称', `job_group` varchar(64) NOT NULL DEFAULT 'DEFAULT' COMMENT '任务组名', `invoke_target` varchar(500) NOT NULL COMMENT '调用目标字符串', `cron_expression` varchar(255) DEFAULT '' COMMENT 'cron执行表达式', `misfire_policy` varchar(20) DEFAULT '3' COMMENT '计划执行错误策略', `concurrent` char(1) DEFAULT '1' COMMENT '是否并发执行', `status` char(1) DEFAULT '0' COMMENT '状态', `create_by` varchar(64) DEFAULT '' COMMENT '创建者', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) DEFAULT '' COMMENT '更新者', `update_time` datetime DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) DEFAULT '' COMMENT '备注信息', PRIMARY KEY (`job_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定时任务调度表';

2. 创建定时任务业务逻辑

2.1 定义日志清理Job

我们以实现"每日凌晨清理7天前的日志文件"为例,创建一个具体的Job实现类:

package com.ruoyi.project.job; import org.quartz.JobExecutionContext; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.file.FileUtils; import com.ruoyi.project.monitor.job.util.AbstractQuartzJob; import java.io.File; import java.util.Date; public class LogCleanJob extends AbstractQuartzJob { @Override protected void doExecute(JobExecutionContext context) { String logPath = "/var/log/app/"; // 日志目录路径 int keepDays = 7; // 保留天数 File logDir = new File(logPath); if (logDir.exists() && logDir.isDirectory()) { File[] logFiles = logDir.listFiles(); if (logFiles != null) { for (File file : logFiles) { if (file.isFile() && DateUtils.daysBetween( new Date(file.lastModified()), new Date()) > keepDays) { file.delete(); logger.info("删除过期日志文件: {}", file.getName()); } } } } } }

2.2 邮件发送Job实现

再来看一个发送日报邮件的Job实现:

package com.ruoyi.project.job; import org.quartz.JobExecutionContext; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.mail.MailUtils; import com.ruoyi.project.monitor.job.util.AbstractQuartzJob; public class DailyReportJob extends AbstractQuartzJob { @Override protected void doExecute(JobExecutionContext context) { // 获取日报数据 String reportContent = generateDailyReport(); // 发送邮件 MailUtils.sendEmail( "admin@example.com", "每日系统报告 - " + DateUtils.getDate(), reportContent, false ); logger.info("日报邮件发送成功"); } private String generateDailyReport() { // 实际项目中这里会从数据库获取各种统计数据 StringBuilder sb = new StringBuilder(); sb.append("=== 系统日报 ===\n"); sb.append("日期: ").append(DateUtils.getDate()).append("\n"); sb.append("用户活跃数: 1254\n"); sb.append("新增订单: 342\n"); sb.append("系统异常: 2\n"); return sb.toString(); } }

3. 任务配置与管理

3.1 通过若依后台管理定时任务

若依框架提供了完善的前台界面来管理定时任务:

  1. 登录系统后,进入"系统监控" → "定时任务"
  2. 点击"新增"按钮,填写任务信息:
    • 任务名称:清理过期日志
    • 任务组名:SYSTEM
    • 调用目标字符串:com.ruoyi.project.job.LogCleanJob
    • Cron表达式:0 0 2 * * ? (每天凌晨2点执行)
    • 并发执行:禁止
    • 错误策略:立即执行

关键参数说明:

参数名称说明示例值
调用目标字符串完整类名com.ruoyi.project.job.LogCleanJob
Cron表达式任务执行时间规则0 0 2 * * ?
并发执行是否允许多实例并行禁止
错误策略错过执行时的处理方式立即执行

3.2 手动编码配置任务

除了通过界面配置,我们也可以通过代码方式注册任务:

@Configuration public class QuartzConfig { @Autowired private Scheduler scheduler; @PostConstruct public void init() throws SchedulerException { // 日志清理任务 JobDetail logCleanJob = JobBuilder.newJob(LogCleanJob.class) .withIdentity("logCleanJob", "SYSTEM") .storeDurably() .build(); Trigger logCleanTrigger = TriggerBuilder.newTrigger() .forJob(logCleanJob) .withIdentity("logCleanTrigger", "SYSTEM") .withSchedule(CronScheduleBuilder.cronSchedule("0 0 2 * * ?")) .build(); // 日报邮件任务 JobDetail dailyReportJob = JobBuilder.newJob(DailyReportJob.class) .withIdentity("dailyReportJob", "REPORT") .storeDurably() .build(); Trigger dailyReportTrigger = TriggerBuilder.newTrigger() .forJob(dailyReportJob) .withIdentity("dailyReportTrigger", "REPORT") .withSchedule(CronScheduleBuilder.cronSchedule("0 30 9 * * ?")) .build(); scheduler.scheduleJob(logCleanJob, logCleanTrigger); scheduler.scheduleJob(dailyReportJob, dailyReportTrigger); } }

4. 高级功能与最佳实践

4.1 动态修改任务执行时间

在实际项目中,经常需要在不重启应用的情况下修改任务的执行时间:

public void rescheduleJob(Long jobId, String newCron) throws SchedulerException { SysJob job = jobMapper.selectJobById(jobId); if (job == null) { throw new RuntimeException("任务不存在"); } job.setCronExpression(newCron); jobMapper.updateJob(job); TriggerKey triggerKey = ScheduleUtils.getTriggerKey(jobId, job.getJobGroup()); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); trigger = trigger.getTriggerBuilder() .withIdentity(triggerKey) .withSchedule(scheduleBuilder) .build(); scheduler.rescheduleJob(triggerKey, trigger); }

4.2 任务执行日志记录

为了更好的监控任务执行情况,建议记录每次执行的详细日志:

public class LoggingJobListener implements JobListener { @Override public String getName() { return "GlobalJobListener"; } @Override public void jobToBeExecuted(JobExecutionContext context) { logger.info("任务开始执行: {}", context.getJobDetail().getKey()); } @Override public void jobExecutionVetoed(JobExecutionContext context) { logger.warn("任务被否决: {}", context.getJobDetail().getKey()); } @Override public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { if (jobException != null) { logger.error("任务执行失败: {}", context.getJobDetail().getKey(), jobException); } else { logger.info("任务执行完成: {}", context.getJobDetail().getKey()); } } }

注册监听器:

scheduler.getListenerManager().addJobListener(new LoggingJobListener());

4.3 集群环境下的注意事项

在分布式环境中运行定时任务时,需要特别注意:

  • 确保数据库持久化:所有任务配置必须持久化到数据库
  • 避免任务重复执行:通过若依框架的@DisallowConcurrentExecution注解防止并发
  • 合理设置misfire策略:根据业务需求选择错过执行的处理方式

常见misfire策略对比:

策略值说明适用场景
0立即执行错过的任务必须确保执行的任务
1执行一次(默认)大多数常规任务
2放弃执行非关键性任务

5. 常见问题排查

5.1 任务不执行的检查步骤

  1. 检查任务状态:确认任务在sys_job表中的status字段为0(正常)
  2. 验证Cron表达式:使用若依自带的Cron表达式验证功能
  3. 查看应用日志:检查是否有任务初始化或执行时的异常
  4. 确认Scheduler状态:通过JMX或日志确认Quartz Scheduler是否正常运行

5.2 性能优化建议

  • 避免长时间运行的任务:将大任务拆分为小任务
  • 合理设置线程池大小:在application.yml中配置:
    spring: quartz: properties: org.quartz.threadPool.threadCount: 5
  • 使用@DisallowConcurrentExecution:防止同一个任务并发执行
  • 考虑使用异步任务:对于非关键路径任务

在最近的一个电商后台项目中,我们使用若依的Quartz模块实现了订单超时取消、库存预警通知等12个定时任务。初期遇到了任务偶尔不执行的问题,后来发现是因为没有正确配置misfire策略。调整为"立即执行"后,系统稳定性显著提升。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/6 13:57:35

3分钟让通达信自动识别缠论中枢和笔段:告别复杂手工分析

3分钟让通达信自动识别缠论中枢和笔段&#xff1a;告别复杂手工分析 【免费下载链接】ChanlunX 缠中说禅炒股缠论可视化插件 项目地址: https://gitcode.com/gh_mirrors/ch/ChanlunX 还在为缠论分析耗费数小时而苦恼吗&#xff1f;面对密密麻麻的K线图&#xff0c;手动识…

作者头像 李华
网站建设 2026/5/6 13:52:03

利用快马ai快速生成vmware虚拟机配置原型,告别手动编写脚本

今天想和大家分享一个提升虚拟化开发效率的小技巧——如何用InsCode(快马)平台快速生成VMware虚拟机配置原型。作为经常需要搭建测试环境的开发者&#xff0c;手动编写VMX配置文件和PowerCLI脚本实在太耗时了&#xff0c;直到发现这个智能生成方法&#xff0c;效率直接翻倍。 需…

作者头像 李华
网站建设 2026/5/6 13:50:11

新手开发者首次使用Taotoken从注册到成功调用API的全流程体验

新手开发者首次使用Taotoken从注册到成功调用API的全流程体验 1. 注册与初始配置 注册Taotoken平台的过程非常直观。访问官网后&#xff0c;点击注册按钮进入邮箱验证流程&#xff0c;整个步骤耗时不到两分钟。系统自动分配了初始API Key&#xff0c;并在控制台显著位置展示了…

作者头像 李华