文档章节

spring boot 学习笔记(4) 动态创建定时任务

小镇刁民
 小镇刁民
发布于 2017/03/21 19:00
字数 992
阅读 1440
收藏 0

最近项目里面需要做动态定时任务的功能,需要查询数据库里面的某个表,然后动态的创建、修改、删除定时任务。作为一个伸手党,本来想着上网一查就能找到现成的方案,结果找了半天也没找到合适的,没办法自己动手研究一下吧。 还好在官方文档中翻到了一个SchedulingConfigurer类,查看源码发现configureTasks方法中,可以通过ScheduledTaskRegistrar.addCronTask实例来添加任务,嗯,看起来有戏,动手试一下。实现思路如下:

  • 通过@Scheduled注解创建一个定时任务,30秒执行一次,用于加载数据库中的定时任务列表
  • 继承SchedulingConfigurer类,通过ScheduledTaskRegistrar.addCronTask实例来添加任务
  • 创建DynamicTaskRunable 用来执行对应的任务操作
package com.spbd.task.job;

import java.util.List;

import javax.annotation.PreDestroy;

import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.stereotype.Component;

import com.spbd.task.model.TimingTask;
@Component
public class DynamicTaskConfigurer implements SchedulingConfigurer {
	
	private volatile ScheduledTaskRegistrar registrar;
	
	@Override
	public void configureTasks(ScheduledTaskRegistrar registrar) {
		this.registrar = registrar;
	}
	
	public void refreshTasks(List<TimingTask> tasks){
		for (TimingTask tt : tasks) {
			//判断是否已经存在该任务
			if(hasTask(tt.getTaskId())){
				DynamicTaskRunable t = new DynamicTaskRunable(tt.getTaskId());
				String expression = tt.getExpression();
				CronTask task = new CronTask(t, expression);
				registrar.addCronTask(task);
			}
		}
	}
	
	private boolean hasTask(Integer taskId){
		List<CronTask> taskList = registrar.getCronTaskList();
		for (CronTask cronTask : taskList) {
			if(cronTask.getRunnable() instanceof DynamicTaskRunable){
				DynamicTaskRunable r = (DynamicTaskRunable) cronTask.getRunnable();
				if(r.getTaskId().equals(taskId)){
					return true;
				}
			}
		}
		return false;
	}
	
	@PreDestroy
	public void destroy() {
		this.registrar.destroy();
	}
}

package com.spbd.task.job;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.spbd.task.model.TimingTask;

@Component
public class DynamicTask {
	
	private final static Logger LOGGER =Logger.getLogger(DynamicTaskRunable.class);
	@Autowired
	private DynamicTaskConfigurer dynamicTaskConfigurer;

	@Scheduled(cron="0/30 * * * * ?")
	public void loadTasks(){
		LOGGER.info("DynamicTask 正在执行......");
		//从数据库查询
		List<TimingTask> tasks = new ArrayList<TimingTask>();
		dynamicTaskConfigurer.refreshTasks(tasks);
	}
}

package com.spbd.task.job;

import org.apache.log4j.Logger;

public class DynamicTaskRunable implements Runnable{
	
	private final static Logger LOGGER =Logger.getLogger(DynamicTaskRunable.class);
	
	private Integer taskId;
	
	public DynamicTaskRunable(Integer taskId) {
		this.taskId = taskId;
	}
	@Override
	public void run() {
		LOGGER.info("DynamicTaskRunable is running, taskId:"+taskId);
	}

	public Integer getTaskId() {
		return taskId;
	}
}

当我几下把代码写出来之后,迫不及待的运行起来试了试,然而并没有什么用。。。

又看了一下源码才发现ScheduledTaskRegistrar.addCronTask只是把任务放到列表里面,只有在调用scheduleTasks方法的时候才会真正的创建定时任务,而这个方法只有在实例化之后才会去调用。于是脑袋上的小灯泡闪了一下,在refreshTasks方法中调用ScheduledTaskRegistrar.afterPropertiesSet()。于是我又迫不及待的试了试,诶,居然可以了。

毕竟我还是太年轻,正当我沾沾自喜的时候,发现定时任务的数量越来越多了,我勒个去,这不是我想要的。看样子一定是我打开的方式不对。继续看源码吧!

仔细查看源码发现ScheduledTaskRegistrar.scheduleTasks()方法每次都会遍历任务列表并且调用TaskScheduler.schedule()创建定时任务,所以才会创建很多的定时任务。

既然是通过TaskScheduler.schedule()创建定时任务的,那就在它上面想办法吧。改造之后的DynamicTaskConfigurer类如下:

package com.spbd.task.job;

import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

import javax.annotation.PreDestroy;

import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.spbd.task.model.TimingTask;
@Component
public class DynamicTaskConfigurer implements SchedulingConfigurer {
	
	private volatile ScheduledTaskRegistrar registrar;
	
	private final ConcurrentHashMap<Integer, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<Integer, ScheduledFuture<?>>();
	private final ConcurrentHashMap<Integer, CronTask> cronTasks = new ConcurrentHashMap<Integer, CronTask>();
	
	@Override
	public void configureTasks(ScheduledTaskRegistrar registrar) {
		this.registrar = registrar;
	}
	
	public void refreshTasks(List<TimingTask> tasks){
		//取消已经删除的策略任务
		Set<Integer> sids = scheduledFutures.keySet();
		for (Integer sid : sids) {
			if(!exists(tasks, sid)){				
				scheduledFutures.get(sid).cancel(false);
			}
		}
		for (TimingTask tt : tasks) {
			DynamicTaskRunable t = new DynamicTaskRunable(tt.getTaskId());
			String expression = tt.getExpression();
			if(StringUtils.isEmpty(expression)){
				continue;
			}
			if(scheduledFutures.containsKey(tt.getTaskId()) && cronTasks.get(tt.getTaskId()).getExpression().equals(expression)){
				continue;
			}
			//如果策略执行时间发生了变化,则取消当前策略的任务
			if(scheduledFutures.containsKey(tt.getTaskId())){
				scheduledFutures.get(tt.getTaskId()).cancel(false);
				scheduledFutures.remove(tt.getTaskId());
				cronTasks.remove(tt.getTaskId());
			}
			CronTask task = new CronTask(t, expression);
			ScheduledFuture<?> future = registrar.getScheduler().schedule(task.getRunnable(), task.getTrigger());
			cronTasks.put(tt.getTaskId(), task);
			scheduledFutures.put(tt.getTaskId(), future);
		}
	}
	
	private boolean exists(List<TimingTask> tasks,Integer tid){
		for(TimingTask task:tasks){
			if(task.getTaskId().equals(tid)){
				return true;
			}
		}
		return false;
	}
	
	@PreDestroy
	public void destroy() {
		this.registrar.destroy();
	}
}

再跑起来看了一下,暂时没有问题,后面再充分测试看看。。。

https://github.com/sergewu/spbd/tree/master/spbd-task

THE END

© 著作权归作者所有

共有 人打赏支持
小镇刁民
粉丝 3
博文 12
码字总数 7394
作品 0
成都
后端工程师
私信 提问
Spring Boot学习笔记

多模块开发 [SpringBoot学习]-IDEA创建Gradle多Module结构的SpringBoot项目 RabbitMQ RabbitMQ 安装 linux安装RabbitMQ详细教程 Ubuntu 16.04 RabbitMq 安装与运行(安装篇) ubantu安装...

OSC_fly
2018/07/26
0
0
Spring Boot:在Spring Boot中使用定时任务

本文主要介绍如何在Spring Boot中使用定时任务,假设你已经建好了一个基础的Spring Boot项目。首先,我们在项目中建立一个定时任务。 1.创建定时任务 package hello;import java.text.Simpl...

Element0506
2015/11/10
0
0
Spring Boot点滴笔记

Spring Boot导入xml配置文件 使用Spring Boot后再也不想回到一堆xml配置的spring项目中了。但Spring boot有时候也避免不了需要xml配置文件。 可以在启动类中加入@ImportResource Spring Boot...

hutaishi
2017/11/01
0
0
Spring Boot实践--定时任务两种(Schedule与Quartz整合)

Spring Boot实践--定时任务两种(Schedule与Quartz整合) 最近在项目中使用到定时任务,之前一直都是使用Quartz 来实现,最近看Spring 基础发现其实Spring 提供 Spring Schedule 可以帮助我们实...

spinachgit
2018/02/11
0
0
spring-boot快速入门学习笔记-集成JPA mybatis rabbitmq mongodb redis

image.png 交流或更多内容请关注我的公众号:nezha_blog 我的技术博客:https://nezha.github.io 微信公众号 1. Spring Boot学习笔记--全注解方式 Spring Boot教程与Spring Cloud教程 2. pri...

哪吒小子
2017/12/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

条形码设计软件BarTender实用教程——透明度样本标签

BarTender是一款优秀的标签、条形码、卡片以及RFID标签设计和打印软件。所有版本的BarTender都具有独立的设计和打印功能,对于更高级的用户,自动化版和企业自动化版本提供了更强大的集成功能...

ymy_666666
36分钟前
0
0
Sql--order by、desc降序、top

Sql--order by、desc降序、top ---------通过order by 语句进行排序: --1.降序order by 列名desc --2.升序order by 列名 或order by 列名asc --3.order by语句必须一定要放在整个sql语句的最...

linjin200
37分钟前
4
0
websocket wss所需的nginx配置以及解决golang作为服务端的跨域问题

下面的配置是微信小程序所需的wss的配置,这里为了方便,,也为了避免使用端口号,和原项目的配置写在一起了,因此使用了固定后缀/wss,以作区分,配置的重点就是location /wss 段,其之后的部分是原...

漫步海边小路
38分钟前
3
0
数字转换汉语中人民币的大写

最终结果输出:你输入的金额为:【123】 大写金额: [壹佰贰拾叁] 下面是实现代码: /** 6 * 数字转换为汉语中人民币的大写<br> 7 * 8 * @author Arvin 9 * @contact 94...

政旭Arvin
48分钟前
2
0
智能合约编程/Dapp漏洞 -- 默认可见性修饰符

Solidity函数有visibility指定符,标明函数如何被允许访问。Visibility决定一个函数是否能被用户,被其他派生合约,从外部调用,仅从内部调用等等。有4个visibility指定符.函数默认的visibil...

怎当她临去时秋波那一转
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部