最近工作上接到了一个定时相关的需求,通过分析发现,需求比较简单,并不需要任务持久化,也不需要定时任务的复杂操作。

所以对比之下,选用了spring-context模块里包含的scheduling功能。

令人兴奋的是!基于spring-boot的自动配置,选用相关两个注解便把我从定时调度功能解脱出来,专心于业务功能的开发了。

像这样:

  • 首先在启动类或者配置类上,启用spring的定时调度,添加注解@EnableScheduling
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

/**
 * Created by lujiu on 2018/11/25.
 */
@EnableScheduling
@SpringBootApplication
public class TransApplication {
    public static void main(String[] args) {
        SpringApplication.run(TransApplication.class, args);
    }
}
  • 然后,在业务功能上使用@Scheduled,即可很方便的把所修饰的业务功能与一对应的触发器捆绑注册到spring的调度任务列表中(spring会根据调度任务类型的不同,分别添加到不同的列表中,以方便任务的触发执行)

源码属性部分

public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean {
    private TaskScheduler taskScheduler;
    private ScheduledExecutorService localExecutor;
    private List<TriggerTask> triggerTasks;
    private List<CronTask> cronTasks;
    private List<IntervalTask> fixedRateTasks;
    private List<IntervalTask> fixedDelayTasks;
    private final Map<Task, ScheduledTask> unresolvedTasks = new HashMap(16);
    private final Set<ScheduledTask> scheduledTasks = new LinkedHashSet(16);
    
    ... ...
}

可以发现@Scheduled的三种类型的定时调度分别被暂存,因为需求这里我只关心cron表达式相关的配置。其余两个固定频率和固定间隙,大家有兴趣可以在文末联系我,再做讨论。

如果,你的需求只是静态cron定时调度的话,恭喜老兄,你的工作已经完成了99%,只需要最后一个潇洒的简单注解:

//此处cron表达式仅做示例
@Scheduled(cron="0 0 0 * * ? ")
public void exec() {
    //业务功能
}

大功告成!

可惜,我的需求不是,我需要动态cron。还能怎么办,继续干呗!(笑哭脸)

翻翻spring手册,问问度娘。

说到这忍不住吐槽下技术分享环境,度娘一波下来,十篇博文,六篇内容都是几乎相同的。分享优秀的文章我也可以理解,但是分享之前能不能自己先分析下呢?根本就没道理的东西,有什么好传播的。

  • 从ScheduledTaskRegistrar中注意到了TriggerTask这个东西,主要是它这个名字比较吸引眼球,用过Quartz的应该都知道,与之同名组件是触发Quartz中Job用的。

其实,目前感觉spring的schedule和Quartz差不多,相当于轻量级Quartz。定时任务的持久化,麻烦一点用spring也可以完成。

言归正传!

  • 在ScheduledTaskRegistrar添加Trigger类型任务列表,需要构建一个TriggerTask,包含任务线程和触发器,任务线程内包含具体的业务功能,触发器内就可以灵活的设置cron表达式,可以用配置文件,数据库,REST服务调用传参等等。

具体代码如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * Created by lujiu on 2018/11/26.
 */
@Component
@EnableScheduling
public class DynamicCronSchedule implements SchedulingConfigurer{
    private static Logger logger = LoggerFactory.getLogger(DynamicCronSchedule.class);

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                //业务执行代码
            }
        };

        Trigger trigger = new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                //执行于每一次任务的触发
                String cron = "这里就可以随意设置你的数据源咯";

                logger.info("cron expression is [{}]",cron);
                logger.info("trigger list size is [{}]",taskRegistrar.getTriggerTaskList().size());

                CronTrigger cronTrigger = new CronTrigger(cron);
                Date nextExecTime = cronTrigger.nextExecutionTime(triggerContext);
                return nextExecTime;
            }
        };
        taskRegistrar.addTriggerTask(task, trigger);
    }
}