本文适用语言:java
序章:定时任务实现方式
当下,java编码过程中,实现定时任务的方式主要以以下两种为主
网络上关于这两种框架的实践和配置相关的教程很多,这里不再赘述。
本文主要就二者的框架原理实现做一个入门引导,为了解深层实现细节做一定的铺垫。
本文源码版本:
- spring-context-3.2.18.RELEASE.jar
- quartz-1.8.6.jar
一、Scheduled
1.1 使用方法
| @EnableScheduling |
| @Component(value="myClass") |
| public class MyClass { |
| |
| @Scheduled(cron= "0 0 0 * * ?") |
| public void myTask() { |
| |
| ... |
| } |
| } |
1.2 源码分析
1.2.1 定时任务执行入口在哪?
| org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar |
| |
| public void onApplicationEvent(ContextRefreshedEvent event) { |
| if (event.getApplicationContext() != this.applicationContext) { |
| return; |
| } |
| |
| scheduleTasks(); |
| } |
1.2.2 调用链路
| 1. 所有已注册task |
| org.springframework.scheduling.config.ScheduledTaskRegistrar |
| protected void scheduleTasks() { |
| ... |
| if (this.triggerTasks != null) { |
| for (TriggerTask task : this.triggerTasks) { |
| |
| this.scheduledFutures.add(this.taskScheduler.schedule( |
| task.getRunnable(), task.getTrigger())); |
| } |
| } |
| ... |
| } |
| |
| 2. 单个task |
| org.springframework.scheduling.TaskScheduler |
| ScheduledFuture schedule(Runnable task, Trigger trigger); |
| |
| 3. 线程池执行task |
| org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler |
| public ScheduledFuture schedule(Runnable task, Trigger trigger) { |
| ScheduledExecutorService executor = getScheduledExecutor(); |
| try { |
| ErrorHandler errorHandler = |
| (this.errorHandler != null ? this.errorHandler : TaskUtils.getDefaultErrorHandler(true)); |
| |
| return new ReschedulingRunnable(task, trigger, executor, errorHandler).schedule(); |
| } |
| catch (RejectedExecutionException ex) { |
| throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); |
| } |
| } |
| |
| 4. 这块是具体的线程实现细节,已经与schedul无关 |
| private ScheduledFuture schedule(final ScheduledFutureTask task) { |
| if (task == null) { |
| throw new NullPointerException("task"); |
| } else { |
| if (this.inEventLoop()) { |
| this.delayedTaskQueue.add(task); |
| } else { |
| |
| this.execute(new Runnable() { |
| public void run() { |
| SingleThreadEventExecutor.this.delayedTaskQueue.add(task); |
| } |
| }); |
| } |
| |
| return task; |
| } |
| } |
1.2.3 @Scheduled注解的生效原理
| org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor |
| |
| |
| public Object postProcessAfterInitialization(final Object bean, String beanName) { |
| Class> targetClass = AopUtils.getTargetClass(bean); |
| if (!this.nonAnnotatedClasses.containsKey(targetClass)) { |
| final Set annotatedMethods = new LinkedHashSet(1); |
| ReflectionUtils.doWithMethods(targetClass, new MethodCallback() { |
| public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { |
| Scheduled scheduled = AnnotationUtils.getAnnotation(method, Scheduled.class); |
| if (scheduled != null) { |
| |
| |
| |
| processScheduled(scheduled, method, bean); |
| annotatedMethods.add(method); |
| } |
| } |
| }); |
| if (annotatedMethods.isEmpty()) { |
| this.nonAnnotatedClasses.put(targetClass, Boolean.TRUE); |
| } |
| } |
| return bean; |
| } |
二、QUARTZ
2.1 使用方法
| |
| SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory(); |
| |
| Scheduler sched = schedFact.getScheduler(); |
| |
| sched.start(); |
| |
| |
| JobDetail job = newJob(HelloJob.class) |
| .withIdentity("myJob", "group1") |
| .build(); |
| |
| |
| Trigger trigger = newTrigger() |
| .withIdentity("myTrigger", "group1") |
| .startNow() |
| .withSchedule(simpleSchedule() |
| .withIntervalInSeconds(40) |
| .repeatForever()) |
| .build(); |
| |
| |
| sched.scheduleJob(job, trigger); |
2.2 源码分析
2.2.1 启动入口
| |
| 1. web.xml配置 |
| |
| quartz:config-file |
| /some/path/my_quartz.properties |
| |
| |
| quartz:shutdown-on-unload |
| true |
| |
| |
| quartz:start-on-load |
| true |
| |
| |
| |
| |
| org.quartz.ee.servlet.QuartzInitializerListener |
| |
| |
| |
| 2. org.quartz.ee.servlet.QuartzInitializerListener |
| |
| public void contextInitialized(ServletContextEvent sce) { |
| ... |
| |
| if (configFile != null) { |
| factory = new StdSchedulerFactory(configFile); |
| } else { |
| factory = new StdSchedulerFactory(); |
| } |
| |
| |
| scheduler = factory.getScheduler(); |
| |
| |
| scheduler.start(); |
| log.info("Scheduler has been started..."); |
| ... |
| } |
2.2.2 核心方法详解
| 1. StdSchedulerFactory.getScheduler() |
| public Scheduler getScheduler() throws SchedulerException { |
| if (cfg == null) { |
| |
| initialize(); |
| } |
| ... |
| |
| sched = instantiate(); |
| return sched; |
| } |
| |
| 2. StdSchedulerFactory.getScheduler().instantiate() |
| 具体实现代码很多,以下做伪代码描述 |
| private Scheduler instantiate() throws SchedulerException { |
| |
| |
| if (cfg == null) { |
| initialize(); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| qs = new QuartzScheduler(rsrcs, schedCtxt, idleWaitTime, dbFailureRetry); |
| Scheduler scheduler = instantiate(rsrcs, qs); |
| qs.initialize(); |
| |
| |
| return scheduler; |
| } |