Springboot 通过自带的@Scheduled 实现定时任务 home 编辑时间 2019/05/06 ![](/api/file/getImage?fileId=5d527c3e16199b2a52000afd) ## 需求 项目中需要用到一些简单的定时任务,定时执行一些事先写好的java方法 但具体何时执行等,需要做到数据库,实现动态配置 最终选择采用Springboot自带的方案 `@Schedule` 或 `SchedulingConfigurer` 来实现 ## 参考 参考以下文章 https://www.cnblogs.com/mmzs/p/10161936.html https://www.cnblogs.com/zt007/p/8954096.html ## 数据库 ![](/api/file/getImage?fileId=5d527c3e16199b2a52000aff) ## 代码 **主要依赖** `Springboot2` `MybatisPLus` <br> 定时任务方法类 `Job.java` ```java /** * 定时任务 * zmh * 2019-5-7 13:54:08 */ public class Job { public void test5(String str){ System.out.println("定时任务 5秒执行一次 参数:" + str); } public void test12(){ System.out.println("定时任务 12点执行一次 无参数"); } } ``` <br> 配置定时任务 `ScheduleConfig.java` ```java import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.xxx.xxx.entity.QuarztJob; import com.xxx.xxx.service.IQuarztJobService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.TriggerContext; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.config.ScheduledTaskRegistrar; import org.springframework.scheduling.support.CronTrigger; import org.springframework.stereotype.Component; import java.util.Date; import java.util.List; import java.util.Map; /** * zzzmh * 2019-5-6 16:29:56 * 动态定时任务 */ @Configuration @EnableScheduling public class ScheduleConfig implements SchedulingConfigurer { @Autowired private IQuarztJobService quarztJobService; /** * 注册定时任务 * * 程序启动先获取所有任务id,并逐一查询出最新状态和执行时间 * 如果状态status = 1说明是暂停中,跳过执行,但依旧注册 * 每次执行前查看数据库最新的任务状态 任务目标和参数 * 每次执行后查看数据库注册最新的下次执行时间 */ @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { QueryWrapper<QuarztJob> wrapper = new QueryWrapper<>(); // 查询所有任务id (这一步先不管 status) List<Map<String, Object>> list = quarztJobService.listMaps(wrapper.select("id")); // 根据数据库配置实时触发 for (Map<String, Object> map : list) { int id = (int) map.get("id"); taskRegistrar.addTriggerTask( // 添加任务内容(Runnable) () -> { // 从数据库获取执行任务 每次要确保拿最新的 QuarztJob job = quarztJobService.getById(id); // 如果 status 是1 代表暂停 跳过任务 if (job.getStatus().intValue() == 0) { try { // 这里通过反射调用方法 // 包名根据实际Bean所在位置调整 String pack = "com.xxx.xxx.quarztJob."; Class<?> obj = Class.forName(pack + job.getBeanName()); // 考虑程序复杂度,简化为根据数据库参数字段是否为空 要么无参数 要么单String类型参数 String params = job.getParams(); if (StringUtils.isBlank(params)) { Method method = obj.getMethod(job.getMethodName()); // 每次new一个新对象 method.invoke(obj.newInstance()); } else { Method method = obj.getMethod(job.getMethodName(), String.class); // 每次new一个新对象 method.invoke(obj.newInstance(), params); } } catch (Exception e) { e.printStackTrace(); } } }, // 设置执行周期(Trigger) triggerContext -> { // 从数据库获取执行周期 每次要确保拿最新的 QuarztJob job = quarztJobService.getById(id); String cron = job.getCronExpression(); // 返回执行周期(Date) return new CronTrigger(cron).nextExecutionTime(triggerContext); } ); } } } ``` ## 运行结果 ![](/api/file/getImage?fileId=5d527c3e16199b2a52000afe) 需要注意:由于用了反射方法,定时任务数据库和实体类之间的配置必须一一对应,否则会一触发就报错! 另外直接在Job中使用@Autowired 存在无法注入Service问题 目前我用的解决方案是从注册定时任务的地方传参 ## END 欢迎关注 个人博客: https://zzzmh.cn 学习笔记: https://leanote.zzzmh.cn 极简壁纸: https://bz.zzzmh.cn 极简插件: https://chrome.zzzmh.cn 送人玫瑰,手留余香 赞赏 Wechat Pay Alipay 关于Nginx日志输出格式问题 【转载】Nginx配置文件nginx.conf中文详解