基于Spring任务调度器实现可动态启停的任务调度器
来源:互联网 发布:快压解压缩软件 编辑:程序博客网 时间:2024/04/30 12:54
一. Spring任务调度介绍
介绍下Spring的任务调度,启动一个间隔1秒的定时任务,首先开启Spring定时任务:
import org.springframework.context.annotation.Configuration;import org.springframework.scheduling.annotation.EnableScheduling;@Configuration@EnableSchedulingpublic class TimerConfig {}
在@Configuration注解下添加@EnableScheduling就可以启动任务调度
然后配置定时任务:
import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component;@Componentpublic class TimerTask { @Scheduled(fixedDelay = 2000) public void doTask() { System.out.println("task executing"); }}
这里设置2秒执行间隔
最后启动Spring容器:
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Main { public static void main(String[] args) throws InterruptedException { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.scan("com.tubemogul.springsecuredemo.springDemo.basicTimer"); annotationConfigApplicationContext.refresh(); }}
在控制台中可以看到定时任务被执行了:
但是现在有个新需求,要求动态启停任务,也就是在程序中启动/停止定时任务,当然比较简单的做法是在定时任务中加个bool变量,动态改变这个变量来控制是否执行这个任务,但如果定时任务很多,代码就很丑了,而且一堆空任务会无谓的消耗计算机资源。
Spring调度器本身没有实现这个功能,下面我们就在Spring调度器的基础上,实现动态启停。
二. Spring 任务调度原理分析
在实现自己定制的任务调度器以前,先分析一下Spring任务调度器的原理。
我们从第一个配置看起@EnableScheduling,这是这个注解的源码:
关键在@Import注解,这个注解导入了SchedulingConfiguration类:
这也是一个配置类,在这个类中导入了ScheduledAnnotationBeanPostProcessor进入Spring容器,从名字上就可以看出这个类是一个BeanPostprocessor,了解Spring容器原理可知,后处理器的postProcessBeforeInitialization(Object bean, String beanName)方法和postProcessAfterInitialization(Object bean, String beanName)方法在Bean创建后被调用(这两个方法调用时间有细微差别,前者在自定义初始化代码生效前调用,后者在自定义初始化代码生效后调用)
有用的逻辑在postProcessBeforeInitialization(Object bean, String beanName)方法里面:
可以看出来实际就是扫描类的里面的方法是否包含了@Scheduled注解,然后调用processScheduled(scheduled, method, bean)注册调度任务,跟踪看看processScheduled(Scheduled scheduled, Method method, Object bean)方法,里面有一些对jdk代理和cglib代理的特殊处理,建立runnable对象,关键片段在这里:
现在去看看registrar对象是何方神圣
进入ScheduledTaskRegistrar类看看
可以看到实际所有的调度任务都被保存在这里。看看第一个变量private TaskScheduler taskScheduler; 阅读后面的源码可以知道,这个是真实的任务调度接口,这个接口有3个实现类:
从名字上看,我们先选择带线程池那个ThreadPoolTaskScheduler来看看:
终于找到了ScheduledExecutorService接口,原来Spring任务调度是基于ScheduledExecutorService实现的,这个是java.lang包里面提供的多线程调度工具类。
三. 实现可动态启停的任务调度器
前面已经分析了Spring调度器的原理,简单说就是定义一个BeanPostProcessor,扫描Bean方法中是否有任务调度的注解,如果有,则解析调度策略并则注册任务到TaskScheduler中进行调度。
OK,那么要实现可动态启停的思路就是自己实现一个BeanPostProcessor,自定义一个注解来标记需要调度的任务,然后向TaskScheduler中注册,然后提供一个启停任务的manager类,话不多说,直接上代码。
自定义一个任务标记注解MyFixDelaySchedule:
import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface MyFixDelaySchedule { long fixedDelay() default 60 * 60 * 1000;}
这里只实现了fixedDelay,其他的cron表达式,fixedRate等调度方式可以参考源码自己添加
修改一下TimerTask,使用自己的任务调度注解:
import org.springframework.stereotype.Component;@Componentpublic class TimerTask { @MyFixDelaySchedule(fixedDelay = 2000) public void doTask() { System.out.println("task executing"); }}
然后是MyScheduledAnnotationBeanPostProcessor,一个BeanPostProcessor
import java.lang.reflect.Method;import java.util.Collections;import java.util.Map;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;import org.springframework.aop.support.AopUtils;import org.springframework.beans.BeansException;import org.springframework.beans.factory.DisposableBean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.config.BeanPostProcessor;import org.springframework.core.MethodIntrospector;import org.springframework.core.Ordered;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.scheduling.config.IntervalTask;import org.springframework.scheduling.support.ScheduledMethodRunnable;public class MyScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered, DisposableBean { @Autowired private MySchedulingManager mySchedulingManager; private final Set<Class<?>> nonAnnotatedClasses = Collections .newSetFromMap(new ConcurrentHashMap<Class<?>, Boolean>(64)); @Override public int getOrder() { return LOWEST_PRECEDENCE; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Class<?> targetClass = AopUtils.getTargetClass(bean); if (!this.nonAnnotatedClasses.contains(targetClass)) { Map<Method, Set<MyFixDelaySchedule>> annotatedMethods = MethodIntrospector.selectMethods(targetClass, new MethodIntrospector.MetadataLookup<Set<MyFixDelaySchedule>>() { @Override public Set<MyFixDelaySchedule> inspect(Method method) { Set<MyFixDelaySchedule> scheduledMethods = AnnotationUtils.getRepeatableAnnotations(method, MyFixDelaySchedule.class, MyFixDelaySchedule.class); return (!scheduledMethods.isEmpty() ? scheduledMethods : null); } }); if (annotatedMethods.isEmpty()) { this.nonAnnotatedClasses.add(targetClass); } else { // Non-empty set of methods for (Map.Entry<Method, Set<MyFixDelaySchedule>> entry : annotatedMethods.entrySet()) { Method method = entry.getKey(); for (MyFixDelaySchedule scheduled : entry.getValue()) { Runnable runnable = new ScheduledMethodRunnable(bean, method); IntervalTask task = new IntervalTask(runnable, scheduled.fixedDelay()); mySchedulingManager.addFixedDelayTask(task); } } } } return bean; } @Override public void destroy() throws Exception { mySchedulingManager.stopScheduleJobs(); }}
写完BeanPostProcess后需要一个配置类把这个BeanPostProcess导入Spring容器:
import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Role;@Configuration@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public class MySchedulingConfiguration { @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public MyScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() { return new MyScheduledAnnotationBeanPostProcessor(); }}
最后是任务控制类:
import java.util.ArrayList;import java.util.LinkedHashSet;import java.util.List;import java.util.Set;import java.util.concurrent.ScheduledFuture;import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;import org.springframework.scheduling.config.IntervalTask;import org.springframework.stereotype.Component;@Componentpublic class MySchedulingManager { private ThreadPoolTaskScheduler taskScheduler; private List<IntervalTask> fixedDelayTasks = new ArrayList<IntervalTask>(); private final Set<ScheduledFuture<?>> scheduledFutures = new LinkedHashSet<ScheduledFuture<?>>(); public MySchedulingManager() { taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(4); taskScheduler.initialize(); taskScheduler.getScheduledThreadPoolExecutor().setRemoveOnCancelPolicy(true); } public void addFixedDelayTask(IntervalTask task) { this.fixedDelayTasks.add(task); } public synchronized void startScheduleJobs() { for (IntervalTask task : this.fixedDelayTasks) { this.scheduledFutures.add(this.taskScheduler.scheduleWithFixedDelay( task.getRunnable(), task.getInterval())); } } public synchronized void stopScheduleJobs() { for (ScheduledFuture<?> future : this.scheduledFutures) { future.cancel(true); } scheduledFutures.clear(); }}
写Main类,先启动任务,运行10秒后停止任务,再等10秒再启动任务,代码如下:
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Main { public static void main(String[] args) throws InterruptedException { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.scan("com.tubemogul.springsecuredemo.springDemo.basicTimer"); annotationConfigApplicationContext.refresh(); MySchedulingManager mySchedulingManager = annotationConfigApplicationContext.getBean(MySchedulingManager.class); System.out.println("start job!"); mySchedulingManager.startScheduleJobs(); Thread.sleep(10000); System.out.println("stop job!"); mySchedulingManager.stopScheduleJobs(); Thread.sleep(10000); System.out.println("re-start job!"); mySchedulingManager.startScheduleJobs(); }}
运行结果如下:
这样我们就实现了可动态启停的任务调度器。
说明一点,Spring的任务调度器需要@EnableScheduling来生效是因为这个注解才能把SchedulingConfiguration导入Spring容器,而我自己实现这个MySchedulingConfiguration由于是放在com.tubemogul.springsecuredemo.springDemo.basicTimer包下面的,在执行annotationConfigApplicationContext.scan(“com.tubemogul.springsecuredemo.springDemo.basicTimer”);自动就会把MySchedulingConfiguration自动就被扫描进入Spring容器,所有就不需要类似@EnableScheduling的配置了
- 基于Spring任务调度器实现可动态启停的任务调度器
- spring任务调度器
- spring+quartz实现动态任务调度
- spring任务调度器quartz
- Spring @Scheduled任务调度器
- Spring Task 任务调度器
- Spring任务调度器Task的使用
- spring的调度任务
- spring的任务调度
- Spring的任务调度
- SPRING 的任务调度
- Spring任务调度-基于Quartz
- Spring+quartz 动态任务调度
- Spring + Quartz动态任务调度
- Spring任务调度的几种实现
- Spring 注释实现任务调度
- quartz+spring 实现任务调度
- Spring Schedule 任务调度实现
- MySQL(7):CRUD语句(3)——子查询和多表查询
- LeetCode 92. Reverse Linked List II
- 查看apk签名,查看key签名,adb常用命令
- Linux vi替换字符串
- 字串乱序 PHP&JS
- 基于Spring任务调度器实现可动态启停的任务调度器
- 交叉编译工具链环境的搭建__1
- 插入排序
- 函数的求导问题
- HDU 1512 Monkey King(左偏堆)
- 博为峰Java技术文章 ——JavaEE Hibernate保存数据
- 中文分割
- 开博啦
- Unity 5 中的全局光照技术详解