Spring内置任务调度如何实现添加、取消与重置详解

前言

大家应该都有所体会,使用Spring的任务调度给我们的开发带来了极大的便利,不过当我们的任务调度配置完成后,很难再对其进行更改,除非停止服务器,修改配置,然后再重启,显然这样是不利于线上操作的,为了实现动态的任务调度修改,我在网上也查阅了一些资料,大部分都是基于quartz实现的,使用Spring内置的任务调度则少之又少,而且效果不理想,需要在下次任务执行后,新的配置才能生效,做不到立即生效。本着探索研究的原则,查看了一下Spring的源码,下面为大家提供一种Spring内置任务调度实现添加、取消、重置的方法。话不多说了,来一起看看详细的介绍 吧。

实现方法如下

首先,我们需要启用Spring的任务调度

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:task="http://www.springframework.org/schema/task"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-3.2.xsd
  http://www.springframework.org/schema/task
  http://www.springframework.org/schema/task/spring-task-3.2.xsd">
 <task:annotation-driven executor="jobExecutor" scheduler="jobScheduler" />
 <task:executor id="jobExecutor" pool-size="5"/>
 <task:scheduler id="jobScheduler" pool-size="10" />
</beans>

这一部分配置在网上是很常见的,接下来我们需要联合使用@EnableScheduling与org.springframework.scheduling.annotation.SchedulingConfigurer便携我们自己的调度配置,在SchedulingConfigurer接口中,需要实现一个void configureTasks(ScheduledTaskRegistrar taskRegistrar);方法,传统做法是在该方法中添加需要执行的调度信息。网上的基本撒谎那个也都是使用该方法实现的,使用addTriggerTask添加任务,并结合cron表达式动态修改调度时间,这里我们并不这样做。

查看一下ScheduledTaskRegistrar源码,我们发现该对象初始化完成后会执行scheduleTasks()方法,在该方法中添加任务调度信息,最终所有的任务信息都存放在名为scheduledFutures的集合中。

protected void scheduleTasks() {
  long now = System.currentTimeMillis();

  if (this.taskScheduler == null) {
   this.localExecutor = Executors.newSingleThreadScheduledExecutor();
   this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
  }
  if (this.triggerTasks != null) {
   for (TriggerTask task : this.triggerTasks) {
    this.scheduledFutures.add(this.taskScheduler.schedule(
      task.getRunnable(), task.getTrigger()));
   }
  }
  if (this.cronTasks != null) {
   for (CronTask task : this.cronTasks) {
    this.scheduledFutures.add(this.taskScheduler.schedule(
      task.getRunnable(), task.getTrigger()));
   }
  }
  if (this.fixedRateTasks != null) {
   for (IntervalTask task : this.fixedRateTasks) {
    if (task.getInitialDelay() > 0) {
     Date startTime = new Date(now + task.getInitialDelay());
     this.scheduledFutures.add(this.taskScheduler.scheduleAtFixedRate(
       task.getRunnable(), startTime, task.getInterval()));
    }
    else {
     this.scheduledFutures.add(this.taskScheduler.scheduleAtFixedRate(
       task.getRunnable(), task.getInterval()));
    }
   }
  }
  if (this.fixedDelayTasks != null) {
   for (IntervalTask task : this.fixedDelayTasks) {
    if (task.getInitialDelay() > 0) {
     Date startTime = new Date(now + task.getInitialDelay());
     this.scheduledFutures.add(this.taskScheduler.scheduleWithFixedDelay(
       task.getRunnable(), startTime, task.getInterval()));
    }
    else {
     this.scheduledFutures.add(this.taskScheduler.scheduleWithFixedDelay(
       task.getRunnable(), task.getInterval()));
    }
   }
  }
 }

所以我的思路就是动态修改该集合,实现任务调度的添加、取消、重置。实现代码如下:

package com.jianggujin.web.util.job;

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

import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.TriggerTask;

import com.jianggujin.web.util.BeanUtils;

/**
 * 默认任务调度配置
 *
 * @author jianggujin
 *
 */
@EnableScheduling
public class DefaultSchedulingConfigurer implements SchedulingConfigurer
{
 private final String FIELD_SCHEDULED_FUTURES = "scheduledFutures";
 private ScheduledTaskRegistrar taskRegistrar;
 private Set<ScheduledFuture<?>> scheduledFutures = null;
 private Map<String, ScheduledFuture<?>> taskFutures = new ConcurrentHashMap<String, ScheduledFuture<?>>();

 @Override
 public void configureTasks(ScheduledTaskRegistrar taskRegistrar)
 {
  this.taskRegistrar = taskRegistrar;
 }

 @SuppressWarnings("unchecked")
 private Set<ScheduledFuture<?>> getScheduledFutures()
 {
  if (scheduledFutures == null)
  {
   try
   {
   scheduledFutures = (Set<ScheduledFuture<?>>) BeanUtils.getProperty(taskRegistrar, FIELD_SCHEDULED_FUTURES);
   }
   catch (NoSuchFieldException e)
   {
   throw new SchedulingException("not found scheduledFutures field.");
   }
  }
  return scheduledFutures;
 }

 /**
 * 添加任务
 *
 * @param taskId
 * @param triggerTask
 */
 public void addTriggerTask(String taskId, TriggerTask triggerTask)
 {
  if (taskFutures.containsKey(taskId))
  {
   throw new SchedulingException("the taskId[" + taskId + "] was added.");
  }
  TaskScheduler scheduler = taskRegistrar.getScheduler();
  ScheduledFuture<?> future = scheduler.schedule(triggerTask.getRunnable(), triggerTask.getTrigger());
  getScheduledFutures().add(future);
  taskFutures.put(taskId, future);
 }

 /**
 * 取消任务
 *
 * @param taskId
 */
 public void cancelTriggerTask(String taskId)
 {
  ScheduledFuture<?> future = taskFutures.get(taskId);
  if (future != null)
  {
   future.cancel(true);
  }
  taskFutures.remove(taskId);
  getScheduledFutures().remove(future);
 }

 /**
 * 重置任务
 *
 * @param taskId
 * @param triggerTask
 */
 public void resetTriggerTask(String taskId, TriggerTask triggerTask)
 {
  cancelTriggerTask(taskId);
  addTriggerTask(taskId, triggerTask);
 }

 /**
 * 任务编号
 *
 * @return
 */
 public Set<String> taskIds()
 {
  return taskFutures.keySet();
 }

 /**
 * 是否存在任务
 *
 * @param taskId
 * @return
 */
 public boolean hasTask(String taskId)
 {
  return this.taskFutures.containsKey(taskId);
 }

 /**
 * 任务调度是否已经初始化完成
 *
 * @return
 */
 public boolean inited()
 {
  return this.taskRegistrar != null && this.taskRegistrar.getScheduler() != null;
 }
}

其中用到的BeanUtils源码如下:

package com.jianggujin.web.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BeanUtils
{

 public static Field findField(Class<?> clazz, String name)
 {
  try
  {
   return clazz.getField(name);
  }
  catch (NoSuchFieldException ex)
  {
   return findDeclaredField(clazz, name);
  }
 }

 public static Field findDeclaredField(Class<?> clazz, String name)
 {
  try
  {
   return clazz.getDeclaredField(name);
  }
  catch (NoSuchFieldException ex)
  {
   if (clazz.getSuperclass() != null)
   {
   return findDeclaredField(clazz.getSuperclass(), name);
   }
   return null;
  }
 }

 public static Method findMethod(Class<?> clazz, String methodName, Class<?>... paramTypes)
 {
  try
  {
   return clazz.getMethod(methodName, paramTypes);
  }
  catch (NoSuchMethodException ex)
  {
   return findDeclaredMethod(clazz, methodName, paramTypes);
  }
 }

 public static Method findDeclaredMethod(Class<?> clazz, String methodName, Class<?>... paramTypes)
 {
  try
  {
   return clazz.getDeclaredMethod(methodName, paramTypes);
  }
  catch (NoSuchMethodException ex)
  {
   if (clazz.getSuperclass() != null)
   {
   return findDeclaredMethod(clazz.getSuperclass(), methodName, paramTypes);
   }
   return null;
  }
 }

 public static Object getProperty(Object obj, String name) throws NoSuchFieldException
 {
  Object value = null;
  Field field = findField(obj.getClass(), name);
  if (field == null)
  {
   throw new NoSuchFieldException("no such field [" + name + "]");
  }
  boolean accessible = field.isAccessible();
  field.setAccessible(true);
  try
  {
   value = field.get(obj);
  }
  catch (Exception e)
  {
   throw new RuntimeException(e);
  }
  field.setAccessible(accessible);
  return value;
 }

 public static void setProperty(Object obj, String name, Object value) throws NoSuchFieldException
 {
  Field field = findField(obj.getClass(), name);
  if (field == null)
  {
   throw new NoSuchFieldException("no such field [" + name + "]");
  }
  boolean accessible = field.isAccessible();
  field.setAccessible(true);
  try
  {
   field.set(obj, value);
  }
  catch (Exception e)
  {
   throw new RuntimeException(e);
  }
  field.setAccessible(accessible);
 }

 public static Map<String, Object> obj2Map(Object obj, Map<String, Object> map)
 {
  if (map == null)
  {
   map = new HashMap<String, Object>();
  }
  if (obj != null)
  {
   try
   {
   Class<?> clazz = obj.getClass();
   do
   {
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields)
    {
     int mod = field.getModifiers();
     if (Modifier.isStatic(mod))
     {
      continue;
     }
     boolean accessible = field.isAccessible();
     field.setAccessible(true);
     map.put(field.getName(), field.get(obj));
     field.setAccessible(accessible);
    }
    clazz = clazz.getSuperclass();
   } while (clazz != null);
   }
   catch (Exception e)
   {
   throw new RuntimeException(e);
   }
  }
  return map;
 }

 /**
 * 获得父类集合,包含当前class
 *
 * @param clazz
 * @return
 */
 public static List<Class<?>> getSuperclassList(Class<?> clazz)
 {
  List<Class<?>> clazzes = new ArrayList<Class<?>>(3);
  clazzes.add(clazz);
  clazz = clazz.getSuperclass();
  while (clazz != null)
  {
   clazzes.add(clazz);
   clazz = clazz.getSuperclass();
  }
  return Collections.unmodifiableList(clazzes);
 }
}

因为加载的延迟,在使用这种方法自定义配置任务调度是,首先需要调用inited()方法判断是否初始化完成,否则可能出现错误。

接下来我们来测试一下:

package com.jianggujin.zft.job;

import java.util.Calendar;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.config.TriggerTask;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import com.jianggujin.web.util.job.DefaultSchedulingConfigurer;

public class TestJob implements InitializingBean
{
 @Autowired
 private DefaultSchedulingConfigurer defaultSchedulingConfigurer;

 public void afterPropertiesSet() throws Exception
 {
  new Thread() {
   public void run()
   {

   try
   {
    // 等待任务调度初始化完成
    while (!defaultSchedulingConfigurer.inited())
    {
     Thread.sleep(100);
    }
   }
   catch (InterruptedException e)
   {
    e.printStackTrace();
   }
   System.out.println("任务调度初始化完成,添加任务");
   defaultSchedulingConfigurer.addTriggerTask("task", new TriggerTask(new Runnable() {

    @Override
    public void run()
    {
     System.out.println("run job..." + Calendar.getInstance().get(Calendar.SECOND));

    }
   }, new CronTrigger("0/5 * * * * ? ")));
   };
  }.start();
  new Thread() {
   public void run()
   {

   try
   {
    Thread.sleep(30000);
   }
   catch (Exception e)
   {
   }
   System.out.println("重置任务............");
   defaultSchedulingConfigurer.resetTriggerTask("task", new TriggerTask(new Runnable() {

    @Override
    public void run()
    {
     System.out.println("run job..." + Calendar.getInstance().get(Calendar.SECOND));

    }
   }, new CronTrigger("0/10 * * * * ? ")));
   };
  }.start();
 }
}

在该类中,我们首先使用一个线程,等待我们自己的任务调度初始化完成后向其中添加一个每五秒钟打印一句话的任务,然后再用另一个线程过30秒后修改该任务,修改的本质其实是现将原来的任务取消,然后再添加一个新的任务。

在配置文件中初始化上面的类

<bean id="defaultSchedulingConfigurer" class="com.jianggujin.web.util.job.DefaultSchedulingConfigurer"/>
<bean id="testJob" class="com.jianggujin.zft.job.TestJob"/>

运行程序,观察控制台输出

这样我们就实现了动态的重置任务了。以上为个人探索出来的方法,如有更好的解决方案,欢迎指正。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Spring整合Quartz实现定时任务调度的方法

    最近项目中需要实现定时执行任务,比如定时计算会员的积分.调用第三方接口等,由于项目采用spring框架,所以这里结合spring框架来介绍. 编写作业类 即普通的pojo,如下: package com.pcmall.task; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TaskA { private static Logger logger = LoggerFactory.getLogger(Ta

  • Spring整合TimerTask实现定时任务调度

    一. 前言 最近在公司的项目中用到了定时任务, 本篇博文将会对TimerTask定时任务进行总结, 其实TimerTask在实际项目中用的不多, 因为它不能在指定时间运行, 只能让程序按照某一个频度运行. 二. TimerTask JDK中Timer是一个定时器类, 它可以为指定的定时任务进行配置. JDK中TimerTask是一个定时任务类, 该类实现了Runnable接口, 是一个抽象类, 我们可以继承这个类, 实现定时任务. /** * 继承TimerTask实现定时任务 */ publi

  • Spring 中使用Quartz实现任务调度

    前言:Spring中使用Quartz 有两种方式,一种是继承特定的基类:org.springframework.scheduling.quartz.QuartzJobBean,另一种则不需要,(推荐使用第二种).下面分别介绍. 1.作业类继承 org.springframework.scheduling.quartz.QuartzJobBean 第一步:定义作业类 java代码 import java.text.SimpleDateFormat; import java.util.Date; i

  • Java中Spring使用Quartz任务调度定时器

    Quartz 任务调度是什么 Quartz 是 OpenSymphony 开源组织在 Job scheduling 领域又一个开源项目,它可以与 J2EE 与 J2SE 应用程序相结合也可以单独使用.Quartz 是一个完全由 Java 编写的开源作业调度框架.不要让作业调度这个术语吓着你.尽管Quartz框架整合了许多额外功能,但就其简易形式看,你会发现它易用得简直让人受不了! 其实,他还是没有解释明白,我简单说一下:Quartz 作业调度就是可以实现定时任务.它可以实现类似 Windows

  • Spring内置任务调度如何实现添加、取消与重置详解

    前言 大家应该都有所体会,使用Spring的任务调度给我们的开发带来了极大的便利,不过当我们的任务调度配置完成后,很难再对其进行更改,除非停止服务器,修改配置,然后再重启,显然这样是不利于线上操作的,为了实现动态的任务调度修改,我在网上也查阅了一些资料,大部分都是基于quartz实现的,使用Spring内置的任务调度则少之又少,而且效果不理想,需要在下次任务执行后,新的配置才能生效,做不到立即生效.本着探索研究的原则,查看了一下Spring的源码,下面为大家提供一种Spring内置任务调度实现添

  • 对python内置map和six.moves.map的区别详解

    python内置map返回的是列表,而six.moves.map返回的是iter. >>> map(lambda a: a*2, [1, 2, 3]) [2, 4, 6] >>> m = six.moves.map(lambda a: a*2, [1, 2, 3]) >>> type(m) <type 'itertools.imap'> >>> next(m) 2 >>> next(m) 4 >&g

  • 玩客云内置EMMC存储刷入Armbian系统(图文详解)

    目录 设备准备:玩客云 玩客云配置: 系统:Armbian 准备工具 刷机软件及系统准备 刷机 步骤一:连接设备 步骤二:导入镜像 步骤三:烧入安卓底包 步骤四:U盘写入 Armbian 步骤五:U盘写入系统 重启并连接设备 把系统写入EMMC 第三步1:拆机 最近因 Nas 负荷太大,搞了一个玩客云作为微型主机分担了部分压力.要让玩客云成为一台微型主机,需要给它安装一个Armbian系统. 设备准备:玩客云 玩客云是一款前些年很火的矿机,曾经在官网售卖¥599,现在已经沦落到¥45包邮的田地了

  • 利用Distinct()内置方法对List集合的去重问题详解

    前言 说到对集合去重处理,第一时间想到的肯定是Linq的Distinct扩展方式,对于一般的值类型集合去重,很好处理,直接list.Distinct()即可.但是如果想要对一个引用类型的集合去重(属性值都相同就认为重复),就会发现,直接Distinct()是不行的 先来看看泛型链表 List<T> 的定义: public class List<T> : IList<T>, ICollection<T>, IList, ICollection, IReadOn

  • Spring Cloud中使用jib进行docker部署的步骤详解

    Jib介绍 Jib 是 Google 开发的可以直接构建 Java 应用的 Docker 和 OCI 镜像的类库,以 Maven 和 Gradle 插件形式提供. 通过 Jib,Java 开发者可以使用他们熟悉的 Java 工具来构建容器.Jib 是一个快速而简单的容器镜像构建工具,它负责处理将应用程序打包到容器镜像中所需的所有步骤.它不需要你编写 Dockerfile 或安装 Docker,而且可以直接集成到 Maven 和 Gradle中 -- 只需要将插件添加到构建中,就可以立即将 Jav

  • Spring 整合Shiro 并扩展使用EL表达式的实例详解

    Shiro是一个轻量级的权限控制框架,应用非常广泛.本文的重点是介绍Spring整合Shiro,并通过扩展使用Spring的EL表达式,使@RequiresRoles等支持动态的参数.对Shiro的介绍则不在本文的讨论范围之内,读者如果有对shiro不是很了解的,可以通过其官方网站了解相应的信息.infoq上也有一篇文章对shiro介绍比较全面的,也是官方推荐的,其地址是https://www.infoq.com/articles/apache-shiro. Shiro整合Spring 首先需要

  • Spring为IOC容器注入Bean的五种方式详解

    这篇文章主要介绍了Spring为IOC容器注入Bean的五种方式详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一 @Import导入组件,id默认是组件的全类名 //类中组件统一设置.满足当前条件,这个类中配置的所有bean注册才能生效: @Conditional({WindowsCondition.class}) @Configuration @Import({Color.class,Red.class,MyImportSelector

  • Spring AOP里的静态代理和动态代理用法详解

    什么是代理? 为某一个对象创建一个代理对象,程序不直接用原本的对象,而是由创建的代理对象来控制原对象,通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间 什么是静态代理? 由程序创建或特定工具自动生成源代码,在程序运行前,代理类的.class文件就已经存在 通过将目标类与代理类实现同一个接口,让代理类持有真实类对象,然后在代理类方法中调用真实类方法,在调用真实类方法的前后添加我们所需要的功能扩展代码来达到增强的目的. 优点

  • spring security在分布式项目下的配置方法(案例详解)

    分布式项目和传统项目的区别就是,分布式项目有多个服务,每一个服务仅仅只实现一套系统中一个或几个功能,所有的服务组合在一起才能实现系统的完整功能.这会产生一个问题,多个服务之间session不能共享,你在其中一个服务中登录了,登录信息保存在这个服务的session中,别的服务不知道啊,所以你访问别的服务还得在重新登录一次,对用户十分不友好.为了解决这个问题,于是就产生了单点登录: **jwt单点登录:**就是用户在登录服务登录成功后,登录服务会产生向前端响应一个token(令牌),以后用户再访问系

  • Java Spring框架创建项目与Bean的存储与读取详解

    目录 1.Spring项目的创建 1.1创建Maven项目 1.2添加spring依赖 1.3创建启动类 1.4配置国内源 2.储存或读取Bean对象 2.1添加spring配置文件 2.2创建Bean对象 2.3读取Bean对象 本文思维导图: 1.Spring项目的创建 1.1创建Maven项目 第一步,创建Maven项目,Spring也是基于Maven的. 1.2添加spring依赖 第二步,在Maven项目中添加Spring的支持(spring-context, spring-beans

随机推荐