在我们平时开发的项目中,定时任务基本属于必不可少的功能,那大家都是怎么做的呢?但我知道的大多都是静态定时任务实现。

基于注解来创建定时任务非常简单,只需几行代码便可完成。实现如下:

  1. @Configuration
  2. @EnableScheduling
  3. public class SimpleScheduleTask {
  4.  
  5. //10秒钟执行一次
  6. @Scheduled(cron = "0/10 * * * * ?")
  7. private void tasks() {
  8. System.out.println("【定时任务】 每10秒执行一次!");
  9. }
  10. }

Cron表达式参数分别表示(从左到右):
秒(0~59) 如0/5表示每5秒
分(0~59)
时(0~23)
日(0~31) 月的某一天
月(0~11)
周几( 可填1-7 或 SUN/MON/TUE/WED/THU/FRI/SAT)

就上面几行代码,就能搞定一个定时任务。显然,使用Scheduled 确实特别的方便,但有很大的缺点和局限,就是当我们调整了执行计划的时间时,需要重启服务才能生效,这就有些不方便。为了达到实时生效的效果,可以通过数据库来动态实现定时任务。

 

基于数据库的动态定时任务实现

将定时任务配置在数据库,启动项目的时候,用mybatis读取数据库,实例化对象,并设定定时任务。如果需要新增,减少,修改定时任务,仅需要修改数据库资料,并重启项目即可,无需改代码。

  1. @Lazy(value = false)
  2. @Component
  3. public class ScheduleTask implements SchedulingConfigurer {
  4.  
  5. protected static Logger logger = LoggerFactory.getLogger(ScheduleTask.class);
  6. private SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  7.  
  8. @Autowired
  9. private ScheduleTaskMapper scheduleTaskMapper;
  10.  
  11. @Override
  12. public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
  13. List<ScheduleTask> tasks = getAllScheduleTasks();
  14. logger.info("【定时任务启动】 启动任务数:"+tasks.size()+"; time="+sdf.format(new Date()));
  15.  
  16. //校验数据
  17. checkDataList(tasks);
  18. //通过校验的数据执行定时任务
  19. int count = 0;
  20. if(tasks.size()>0) {
  21. for (int i = 0; i < tasks.size(); i++) {
  22. try {
  23. taskRegistrar.addTriggerTask(getRunnable(tasks.get(i)), getTrigger(tasks.get(i)));
  24. count++;
  25. } catch (Exception e) {
  26. logger.error("task start error:" + tasks.get(i).getClassName() + ";" + tasks.get(i).getMethodName() + ";" + e.getMessage());
  27. }
  28. }
  29. }
  30. logger.info("started task number="+count+"; time="+sdf.format(new Date()));
  31. };
  32.  
  33. /**
  34. * 获取要执行的所有任务
  35. * @return
  36. */
  37. private List<ScheduleTask> getAllScheduleTasks() {
  38. ScheduleTaskExample example=new ScheduleTaskExample();
  39. example.createCriteria().andIsDeleteEqualTo((byte) 0);
  40. return scheduleTaskMapper.selectByExample(example);
  41. }
  42.  
  43. /**
  44. * 获取Runnable
  45. *
  46. * @param task
  47. * @return
  48. */
  49. private Runnable getRunnable(ScheduleTask task){
  50. return new Runnable() {
  51. @Override
  52. public void run() {
  53. try {
  54. Object obj = SpringUtil.getBean(task.getClassName());
  55. Method method = obj.getClass().getMethod(task.getMethodName(),null);
  56. method.invoke(obj);
  57. } catch (InvocationTargetException e) {
  58. logger.error("refect exception:"+task.getClassName()+";"+task.getMethodName()+";"+ e.getMessage());
  59. } catch (Exception e) {
  60. logger.error(e.getMessage());
  61. }
  62. }
  63. };
  64. }
  65.  
  66. /**
  67. * 获取Trigger
  68. *
  69. * @param task
  70. * @return
  71. */
  72. private Trigger getTrigger(ScheduleTask task){
  73. return new Trigger() {
  74. @Override
  75. public Date nextExecutionTime(TriggerContext triggerContext) {
  76. //将Cron 0/1 * * * * ?
  77. CronTrigger trigger = new CronTrigger(task.getCron());
  78. Date nextExec = trigger.nextExecutionTime(triggerContext);
  79. return nextExec;
  80. }
  81. };
  82. }
  83.  
  84. /**
  85. * 校验数据
  86. *
  87. * @param list
  88. * @return
  89. */
  90. private List<ScheduleTask> checkDataList(List<ScheduleTask> list) {
  91. String msg="";
  92. for(int i=0;i<list.size();i++){
  93. if(!checkOneData(list.get(i)).equalsIgnoreCase("ok")){
  94. msg+=list.get(i).getTaskName()+";";
  95. list.remove(list.get(i));
  96. i--;
  97. };
  98. }
  99. if(!StringUtils.IsEmpty(msg)){
  100. msg="未启动的任务:"+msg;
  101. logger.error(msg);
  102. }
  103. return list;
  104. }
  105.  
  106. /**
  107. * 按每一条校验数据
  108. *
  109. * @param task
  110. * @return
  111. */
  112. private String checkOneData(ScheduleTask task){
  113. String result="ok";
  114. Class cal= null;
  115. try {
  116. cal = Class.forName(task.getClassName());
  117. Object obj =SpringUtil.getBean(cal);
  118. Method method = obj.getClass().getMethod(task.getMethodName(),null);
  119. String cron=task.getCron();
  120. if(StringUtils.isBlank(cron)){
  121. result="no found the cron:"+task.getTaskName();
  122. logger.error(result);
  123. }
  124. } catch (ClassNotFoundException e) {
  125. result="not found the class:"+task.getClassName()+ e.getMessage();
  126. logger.error(result);
  127. } catch (NoSuchMethodException e) {
  128. result="not found the method:"+task.getClassName()+";"+task.getMethodName()+";"+ e.getMessage();
  129. logger.error(result);
  130. } catch (Exception e) {
  131. logger.error(e.getMessage());
  132. }
  133. return result;
  134. }
  135. }

  

数据库配置

 

运行的结果

 

这样我们可以通过直接修改数据库,执行周期就会改变,并且不需要我们重启应用,十分方便。

推荐阅读:

Java中大量if...else语句的消除替代方案

Java8中遍历Map的常用四种方式

推荐一些MySQL优化技巧,效率提升不止十倍!

扫码关注公众号,发送关键词获取相关资料:
  1. 发“Springboot”领取电商项目实战源码;

  2. 发“SpringCloud”领取学习实战资料;

 

SpringBoot基于数据库的定时任务实现的更多相关文章

  1. SpringBoot基于数据库的定时任务统一管理

    定时任务1 import lombok.extern.slf4j.Slf4j; /** * @author Created by niugang on 2019/12/24/15:29 */ @Slf ...

  2. SpringBoot基于数据库实现简单的分布式锁

    本文介绍SpringBoot基于数据库实现简单的分布式锁. 1.简介 分布式锁的方式有很多种,通常方案有: 基于mysql数据库 基于redis 基于ZooKeeper 网上的实现方式有很多,本文主要 ...

  3. springboot 基于@Scheduled注解 实现定时任务

    前言 使用SpringBoot创建定时任务非常简单,目前主要有以下三种创建方式: 一.基于注解(@Scheduled) 二.基于接口(SchedulingConfigurer) 前者相信大家都很熟悉, ...

  4. SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例

    1.前言 本文主要介绍使用SpringBoot与shiro实现基于数据库的细粒度动态权限管理系统实例. 使用技术:SpringBoot.mybatis.shiro.thymeleaf.pagehelp ...

  5. 四、springBoot 优雅的创建定时任务

    前言 好几天没写了,工作有点忙,最近工作刚好做一个定时任务统计的,所以就将springboot 如何创建定时任务整理了一下. 总的来说,springboot创建定时任务是非常简单的,不用像spring ...

  6. SpringBoot2.0整合mybatis、shiro、redis实现基于数据库权限管理系统

    转自https://blog.csdn.net/poorcoder_/article/details/71374002 本文主要介绍使用SpringBoot与shiro实现基于数据库的细粒度动态权限管 ...

  7. SpringBoot基于websocket的网页聊天

    一.入门简介正常聊天程序需要使用消息组件ActiveMQ或者Kafka等,这里是一个Websocket入门程序. 有人有疑问这个技术有什么作用,为什么要有它?其实我们虽然有http协议,但是它有一个缺 ...

  8. 基于数据库、redis和zookeeper实现的分布式锁

    基于数据库 基于数据库(MySQL)的方案,一般分为3类:基于表记录.乐观锁和悲观锁 基于表记录 用表主键或表字段加唯一性索引便可实现,如下: CREATE TABLE `database_lock` ...

  9. 为什么要用hibernate 与基于数据库表结构的项目开发

    最近开始学习hibernate,其实并不知道要学习什么,有什么用.后来问了一下同事,他就说快捷方便简单,很多事情不用自己做他会帮你做好,但是我觉得不应该是这样的,于是我就去搜了一下,就搜到了一篇帖子, ...

随机推荐

  1. 小白学 Python 爬虫(24):2019 豆瓣电影排行

    人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...

  2. 进程间通讯IPC的几种方式总结

    Linux进程间的通讯 Unix发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间通信方面的侧重点有所不同.前者对Unix早期的进程间通信 ...

  3. 第四十一篇 入门机器学习——Numpy的基本操作——聚合操作

    No.1. 对向量元素求和使用np.sum,也可以使用类似big_array.sum()的方式 No.2. 对向量元素求最小值使用np.min,求最大值使用np.max,也可以使用类似big_arra ...

  4. linux下grep分析apache日志的命令集合

    https://my.oschina.net/hmc0316/blog/112004 实例:月份英文简写英文全称一月Jan.January二月Feb.February三月Mar.March四月Apr. ...

  5. 基于SILVACO ATLAS的a-IGZO薄膜晶体管二维器件仿真(01)

    最近因为肺炎的缘故,宅在家里不能出门,就翻了下一些资料,刚好研究方向是这个,就简单研究了下.参考资料主要如下: 1.<半导体工艺和器件仿真软件Silvaco TCAD实用教程> 唐龙谷 2 ...

  6. Jarvis OJ - 爬楼梯 -Writeup

    Jarvis OJ - 爬楼梯 -Writeup 本来是想逆一下算法的,后来在学长的指导下发现可以直接修改关键函数,这个题做完有种四两拨千斤的感觉,记录在这里 转载请标明出处:http://www.c ...

  7. (转载)Docker的boot2docker.iso镜像使用

    原文路径:https://blog.csdn.net/jiangjingxuan/article/details/54908272#commentsedit 在Docker首次启动时需要下载的一个bo ...

  8. 265. 粉刷房子 II

    Q: A: 首先这题可以和粉刷房子这题一样解法,对于i号房子,遍历k种颜色,对于每一种,都去找i-1号房子除该颜色之外的最小花费.但上一题是3种颜色,总复杂度O(N),这题k种颜色,复杂度O(NK^2 ...

  9. java的动态绑定和多态

    public class Shape { public void area() { System.out.println("各种形状的面积..."); } public stati ...

  10. 【资源分享】Gmod-Expression2 - 自定义像素画生成

    *作者:BUI* 可自定义制作属于你的像素画(默认为Sans) 第77行的COLOR可编辑你想要的颜色(RGB值) 1,2,3,4分别代表第77行所定义的颜色(0代表不显示) 视频地址:传送链接 @n ...