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

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

@Configuration
@EnableScheduling
public class SimpleScheduleTask { //10秒钟执行一次
@Scheduled(cron = "0/10 * * * * ?")
private void tasks() {
System.out.println("【定时任务】 每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读取数据库,实例化对象,并设定定时任务。如果需要新增,减少,修改定时任务,仅需要修改数据库资料,并重启项目即可,无需改代码。

@Lazy(value = false)
@Component
public class ScheduleTask implements SchedulingConfigurer { protected static Logger logger = LoggerFactory.getLogger(ScheduleTask.class);
private SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Autowired
private ScheduleTaskMapper scheduleTaskMapper; @Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
List<ScheduleTask> tasks = getAllScheduleTasks();
logger.info("【定时任务启动】 启动任务数:"+tasks.size()+"; time="+sdf.format(new Date())); //校验数据
checkDataList(tasks);
//通过校验的数据执行定时任务
int count = 0;
if(tasks.size()>0) {
for (int i = 0; i < tasks.size(); i++) {
try {
taskRegistrar.addTriggerTask(getRunnable(tasks.get(i)), getTrigger(tasks.get(i)));
count++;
} catch (Exception e) {
logger.error("task start error:" + tasks.get(i).getClassName() + ";" + tasks.get(i).getMethodName() + ";" + e.getMessage());
}
}
}
logger.info("started task number="+count+"; time="+sdf.format(new Date()));
}; /**
* 获取要执行的所有任务
* @return
*/
private List<ScheduleTask> getAllScheduleTasks() {
ScheduleTaskExample example=new ScheduleTaskExample();
example.createCriteria().andIsDeleteEqualTo((byte) 0);
return scheduleTaskMapper.selectByExample(example);
} /**
* 获取Runnable
*
* @param task
* @return
*/
private Runnable getRunnable(ScheduleTask task){
return new Runnable() {
@Override
public void run() {
try {
Object obj = SpringUtil.getBean(task.getClassName());
Method method = obj.getClass().getMethod(task.getMethodName(),null);
method.invoke(obj);
} catch (InvocationTargetException e) {
logger.error("refect exception:"+task.getClassName()+";"+task.getMethodName()+";"+ e.getMessage());
} catch (Exception e) {
logger.error(e.getMessage());
}
}
};
} /**
* 获取Trigger
*
* @param task
* @return
*/
private Trigger getTrigger(ScheduleTask task){
return new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
//将Cron 0/1 * * * * ?
CronTrigger trigger = new CronTrigger(task.getCron());
Date nextExec = trigger.nextExecutionTime(triggerContext);
return nextExec;
}
};
} /**
* 校验数据
*
* @param list
* @return
*/
private List<ScheduleTask> checkDataList(List<ScheduleTask> list) {
String msg="";
for(int i=0;i<list.size();i++){
if(!checkOneData(list.get(i)).equalsIgnoreCase("ok")){
msg+=list.get(i).getTaskName()+";";
list.remove(list.get(i));
i--;
};
}
if(!StringUtils.IsEmpty(msg)){
msg="未启动的任务:"+msg;
logger.error(msg);
}
return list;
} /**
* 按每一条校验数据
*
* @param task
* @return
*/
private String checkOneData(ScheduleTask task){
String result="ok";
Class cal= null;
try {
cal = Class.forName(task.getClassName());
Object obj =SpringUtil.getBean(cal);
Method method = obj.getClass().getMethod(task.getMethodName(),null);
String cron=task.getCron();
if(StringUtils.isBlank(cron)){
result="no found the cron:"+task.getTaskName();
logger.error(result);
}
} catch (ClassNotFoundException e) {
result="not found the class:"+task.getClassName()+ e.getMessage();
logger.error(result);
} catch (NoSuchMethodException e) {
result="not found the method:"+task.getClassName()+";"+task.getMethodName()+";"+ e.getMessage();
logger.error(result);
} catch (Exception e) {
logger.error(e.getMessage());
}
return result;
}
}

  

数据库配置

 

运行的结果

 

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

推荐阅读:

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. MVC5+EF6 入门完整教程5 :UI的一些改造

    https://www.cnblogs.com/miro/p/4095165.html 上篇文章介绍了EF实现CRUD及一些基本的Html Helpers. 这次我们将会对之前的内容进行一些修改和重构 ...

  2. TOYS(计算几何-入门)

    题目 ‘^’代表叉乘 ‘•’代表点乘 点积:a•b=ax*bx+ay*by 叉积:a^b=ax*by-bx*ay

  3. 数据库too many connections 解决方法

    问题:网站后台突然报错了,显示“too many connections........”这是咋回事? 解决: 先罗列几个有用的操作: ① mysql -u root -p  回车输入密码进入mysq ...

  4. Goahead WebSever 总结

    编译成功后用http://127.0.0.1可以访问网站,若端口号不是默认的80,者访问时加“:端口”,如: http://127.0.0.1:8888 1.websHomePageHandler函数 ...

  5. Python的深拷贝、浅拷贝

    浅拷贝 定义:浅拷贝只是对另外一个变量的内存地址的拷贝,这两个变量指向同一个内存地址的变量值. 浅拷贝的特点: 公用一个值: 这两个变量的内存地址一样: 对其中一个变量的值改变,另外一个变量的值也会改 ...

  6. UVA 1267 Network(DFS)

    题目链接:https://vjudge.net/problem/UVA-1267 首先我们要把这样一棵无根树转换成有根树,那么树根我们可以直接使用$VOD$. 还有一个性质:如果深度为$d$的一个节点 ...

  7. XML学习笔记1

    一.XML与HTML的差异 XML 不是 HTML 的替代:XML 和 HTML 为不同的目的而设计: XML 被设计用来传输和存储数据,其焦点是数据的内容: HTML 被设计用来显示数据,其焦点是数 ...

  8. 基于语音识别、音文同步、图像OCR的字幕解决方案HtwMedia介绍

    背景介绍 俗话说,“好记性不如乱笔头”,这充分说明了文字归档的重要性.如今随着微信.抖音等移动端app的使用越来越广,人们生产音.视频内容也越来越便捷.而相比语音和视频而言,文字具有易存档.易检索.易 ...

  9. 30分钟编写一个抓取 Unsplash 图片的 Python爬虫

       我一直想用 Python and Selenium 创建一个网页爬虫,但从来没有实现它. 几天前, 我决定尝试一下,这听起来可能是挺复杂的, 然而编写代码从 Unsplash 抓取一些美丽的图片 ...

  10. pyppeteer硬钢掉淘宝登入的滑块验证

    完整代码我也不好公布,我可以给你们思路,以及部分代码动动脑子看看文档应该也能搞定 一.初始化Chromium浏览器相关属性 browser = await pyppeteer.launch({'hea ...