1. 概论
Spring Boot 中的 @Scheduled 注解为定时任务提供了一种很简单的实现,只需要在注解中加上一些属性,例如 fixedRate、fixedDelay、cron(最常用)等等,并且在启动类上面加上 @EnableScheduling 注解,就可以启动一个定时任务了。
但是在某些情况下,并没有这么简单,例如项目部署上线之后,我们可能会修改定时任务的执行时间,并且停止、重启定时任务等,因为定时任务是直接写死在程序中的,修改起来不是非常的方便。所以,简单记录一下自己的一些解决方案,仅供参考。
2. 在配置文件中设置参数
以 cron 表达式为例,一般的做法是将 @Scheduled 的属性写在程序中的,例如这样: @Component public class TestTask { private static SimpleDateFormat dateFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Scheduled(cron = "0/5 * * * * ?") public void test(){ System.out.println(dateFmt.format(new Date()) + " : 执行定时任务"); } }
如果需要修改的话,我们可以将 cron 表达式配在 application.yml 中:
#application.yml中的配置 scheduled: cron: 0/5 * * * * ? 然后在 @Scheduled 中获取这个配置: @Scheduled(cron = "${scheduled.cron}") public void test(){ System.out.println(dateFmt.format(new Date()) + " : 执行定时任务"); }
等到了线上的时候,再通过修改配置文件中的内容来进行控制。具体怎么动态的修改配置文件中的内容,后面我会专门写一篇文章来说明。
3. 如何关闭定时任务
一种方式是根据实际的需求,设置一个很久之后的时间再执行,例如明年的某个时间点,你可能会想何不设置一个已经过去的时间(例如 2012 年),但是很遗憾,@Scheduled 并不支持设置年份。
另外 Spring Boot 2.1 以上的版本还提供了一种停止定时任务的方案,就是在 cron 中配置 "-" 即可,你也可以在配置文件中设置这个符号:
#application.yml中的配置 scheduled: cron: "-"
注意这里必须加上一个双引号,因为在 application.yml 中, - 是一个特殊的字符。
4. 为定时任务设置开关
如果嫌上面这种方式比较死板,可以尝试另一种,给定时任务加上开关的方案,在配置文件中配置一个 boolean 属性,如果是 true 的话,就开启定时任务,否则不开启。
#application.yml中的配置 scheduled: cron: 0/5 * * * * ? enable: scheduled: true
然后我们可以使前面说到的 @Conditional 注解来实现这个功能,如果你还不了解,可以看我这篇文章:浅谈 Spring Boot 中的 @Conditional 注解
其实 @Scheduled 注解,是被一个叫做 ScheduledAnnotationBeanPostProcessor 的类所拦截的,所以我们可以根据配置,决定是否创建这个 bean,如果没有这个 bean,@Scheduled 就不会被拦截,那么定时任务肯定不会执行了,有了这个思路,实现起来就很简单了。需要注意的是:这种方式,启动类上面的 @EnableScheduling 需要去掉。
然后创建一个 ScheduledCondtion 类,内容如下:
public class ScheduledCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //读取配置中的属性 return Boolean.valueOf(context.getEnvironment().getProperty("enable.scheduled")); } }
这个类的功能很简单,就是去读取配置,然后返回一个 boolean 值。
然后创建一个配置类 ScheduledConfig ,内容如下:
@Configuration public class ScheduledConfig { @Conditional(ScheduledCondition.class) @Bean public ScheduledAnnotationBeanPostProcessor processor() { return new ScheduledAnnotationBeanPostProcessor(); } }
这个配置,就是以 ScheduledCondtion 为条件,决定是否创建 bean。然后,启动项目,定时任务就会执行,如果我们将配置修改为 false,则不会执行。
这样的话,我们就能够很容易的启动或者关闭定时任务了,并且也可以实时修改 cron 表达式的值。