@Scheduled注解停止定时任务、@Scheduled设置定时任务不启用、springboot 配置Schedule不启用、SpringBoot动态配置开启关闭定时任务

在项目中我们可能会遇到这样一个场景,把一个@Schedule注解的定时任务关闭。以下整理一下如何设置@Schedule注解的定时任务不启用。以下内容基于 org.springframework:spring-context:5.3.12.jar进行讲解


以下为测试代码:

package com.demo.schedule;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@EnableScheduling
@Component
@Slf4j
public class ScheduleTest {

    //每5秒执行一次
    @Scheduled(cron = "${schedule.test1Cron}")
    public void test_1() {
        log.info("测试任务1: " + System.currentTimeMillis());
    }

    //每5秒执行一次
    @Scheduled(cron = "${schedule.test2Cron}")
    public void test_2() {
        log.info("测试任务2: " + System.currentTimeMillis());
    }
}

application.properties配置文件:

#定时任务1
schedule.test1Cron= 0/5 * * * * *
#定时任务2
schedule.test2Cron= 0/5 * * * * *

或者application.yml配置文件:

schedule:
  #定时任务1
  test1Cron: "0/5 * * * * *"
  #定时任务2
  test2Cron: "0/5 * * * * *"

注意: properties中不要用双引号否则会报错(因为加双引号读取时会原文输出即把双引号也带上);yml中可加双引号。

执行结果:

2024-09-24 10:03:55.003  INFO 6788 --- [scheduling-1] com.demo.schedule.ScheduleTest: 测试任务1: 1727143435002
2024-09-24 10:03:55.003  INFO 6788 --- [scheduling-1] com.demo.schedule.ScheduleTest: 测试任务2: 1727143435003
2024-09-24 10:04:00.007  INFO 6788 --- [scheduling-1] com.demo.schedule.ScheduleTest: 测试任务1: 1727143440007
2024-09-24 10:04:00.009  INFO 6788 --- [scheduling-1] com.demo.schedule.ScheduleTest: 测试任务2: 1727143440009
2024-09-24 10:04:05.013  INFO 6788 --- [scheduling-1] com.demo.schedule.ScheduleTest: 测试任务2: 1727143445013
2024-09-24 10:04:05.013  INFO 6788 --- [scheduling-1] com.demo.schedule.ScheduleTest: 测试任务1: 1727143445013
2024-09-24 10:04:10.012  INFO 6788 --- [scheduling-1] com.demo.schedule.ScheduleTest: 测试任务1: 1727143450012
2024-09-24 10:04:10.013  INFO 6788 --- [scheduling-1] com.demo.schedule.ScheduleTest: 测试任务2: 1727143450013

一、关闭定时任务

1.1、方法一:注释@EnableScheduling注解

@EnableScheduling注解的意义是开启定时任务,若@EnableScheduling注解被注释,则整个项目中所有@Scheduled注解的定时任务都将关闭。

优点:

  • 可以一次性关闭项目中所有的定时任务

缺点:

  • 实际项目中可能只是想关闭其中一个定时任务不执行。直接注释@EnableScheduling注解会导致整个项目所有@Scheduled注解的定时任务都将关闭。
  • 需要手动注释代码,生产上更多的是想配置化的,方便修改。
package com.demo.schedule;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

//注释@EnableScheduling注解,关闭整个项目所有的定时任务
//@EnableScheduling  
@Component
@Slf4j
public class ScheduleTest {

    //每5秒执行一次
    @Scheduled(cron = "${schedule.test1Cron}")
    public void test_1() {
        log.info("测试任务1: " + System.currentTimeMillis());
    }

    //每5秒执行一次
    @Scheduled(cron = "0/5 * * * * *")
    public void test_2() {
        log.info("测试任务2: " + System.currentTimeMillis());
    }
}

1.1.1、原理

eg1.正常情况下,只要注释掉@EnableScheduling注解即可,原理就是:

  • 注释掉@EnableScheduling就不会@Import(SchedulingConfiguration.class)
  • 就不会注入ScheduledAnnotationBeanPostProcessor这个后置处理器
  • 这个后置处理器就是用来注册执行@Scheduled定时任务的

eg2.注释掉@EnableScheduling无效,还会执行@Scheduled定时任务?

  • 检查是否项目除启动类还有其它配置类用了@EnableScheduling注解
  • 检查有没有引入spring-session-data-redis依赖,RedisHttpSessionConfiguration内部bean使用了@EnableScheduling
  • 据说spring-boot-starter-actuator依赖也会有,但是我没找到

1.2、方法二:不加载ScheduledAnnotationBeanPostProcessor类

此方法与方法一效果一致,关闭所有@Scheduled的定时任务,只是可以通过配置的方式关闭。

其实 @Scheduled 注解,是被一个叫做 ScheduledAnnotationBeanPostProcessor 的类所拦截的,所以我们可以根据配置,决定是否创建这个 bean,如果没有这个 bean,@Scheduled 就不会被拦截,那么定时任务肯定不会执行了,有了这个思路,实现起来就很简单了。需要注意的是:这种方式,启动类上面的 @EnableScheduling 需要去掉。

  1. 注释所有@EnableScheduling注解
  2. application.properties配置文件中添加以下配置
# 关闭所有scheduled定时任务
scheduled.enable = false
#定时任务1
schedule.test1Cron= 0/5 * * * * *
#定时任务2
schedule.test2Cron= 0/5 * * * * *
  1. 创建一个 ScheduledCondtion 类,内容如下:

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class ScheduledCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        System.out.println("配置属性:"+Boolean.valueOf(context.getEnvironment().getProperty("scheduled.enable")));
        //读取配置中的属性
        return Boolean.valueOf(context.getEnvironment().getProperty("scheduled.enable"));
    }
}

这个类的功能很简单,就是去读取配置,然后返回一个 boolean 值。

  1. 创建一个配置类 ScheduledConfig ,内容如下:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor;

@Configuration
public class ScheduledConfig {
    @Conditional(ScheduledCondition.class)
    @Bean
    public ScheduledAnnotationBeanPostProcessor processor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }
}

这个配置就是以 ScheduledCondtion 为条件,决定是否创建 bean。然后,启动项目,定时任务就会执行,如果我们将配置修改为 false,则不会执行。

1.3、方法三:注释@Scheduled注解

@Scheduled注解的意义是指定当前方法为定时任务,若@Scheduled注解被注释,则当前这个方法的定时任务将关闭。

优点:

  • 通过注释@Scheduled注解可以实现单个定时任务的关闭,

缺点:

  • 需要手动注释代码,对于生产环境来说需要提交代码并重新打包,并不友好。
package com.demo.schedule;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@EnableScheduling  
@Component
@Slf4j
public class ScheduleTest {

    //每5秒执行一次
    //注释@Scheduled注解,关闭当前这个定时任务
    //@Scheduled(cron = "${schedule.test1Cron}")
    public void test_1() {
        log.info("测试任务1: " + System.currentTimeMillis());
    }

    //每5秒执行一次
    @Scheduled(cron = "${schedule.test2Cron}")
    public void test_2() {
        log.info("测试任务2: " + System.currentTimeMillis());
    }
}

1.4、方法四:设置@Scheduled注解cron时间不开启(推荐)

@Scheduled注解源码中说明当cron表达式配置为- 时,不执行该定时任务。所以我们可以通过在application.properties文件中设置定时任务时间为-

cron表达式设置为-时在项目启动时就不会把该定时任务添加到任务池中。原理见下方原理分析。

优点:

  • 可配置化,可以在application.properties配置文件中配置时间,不用修改代码。
  • 支持开启/关闭指定的任务

@Scheduled注解源码如下:

/**
 * 标记要调度的方法的注释。 必须准确指定 cron、fixedDelay 或 fixedRate 属性之一。带注释的方法必须没有参数。    
 * 它通常有一个 void 返回类型; 如果不是,则在通过调度程序调用时将忽略返回值。@Scheduled 注解的处理是通过注册
 * 一个 ScheduledAnnotationBeanPostProcessor 来执行的。 这可以手动完成,或者更方便的是,通过 
 * <task:annotation-driven/> 元素或 @EnableScheduling 注释。
 * 此注释可用作元注释以创建具有属性覆盖的自定义组合注释。
 * @since 3.0
 * @see EnableScheduling
 * @see ScheduledAnnotationBeanPostProcessor
 * @see Schedules
 */
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {

	/**
	 * 指示禁用触发器的特殊 cron 表达式值:“-”。
	 * 这主要用于 ${...} 占位符,允许外部禁用相应的计划方法。
	 * 
	 * @since 5.1
	 * @see ScheduledTaskRegistrar#CRON_DISABLED
	 */
	String CRON_DISABLED = ScheduledTaskRegistrar.CRON_DISABLED;

	/**
	 * 一个类似 cron 的表达式,扩展了通常的 UN*X 定义以包括秒、分、小时、月中的某一天、月份和星期几的触发器。
	 * 例如,“0 * * * * MON-FRI”表示工作日每分钟一次(在分钟的顶部 - 第 0 秒)。
	 * 
	 * 从左到右读取的字段解释如下:
	 * <ul>
	 * <li>second</li>
	 * <li>minute</li>
	 * <li>hour</li>
	 * <li>day of month</li>
	 * <li>month</li>
	 * <li>day of week</li>
	 * </ul>
	 * 特殊值“-”表示禁用的 cron 触发器,主要用于由 ${...} 占位符解析的外部指定值。
	 * @return 返回一个可以解析为 cron 计划的表达式
	 * @see org.springframework.scheduling.support.CronSequenceGenerator
	 */
	String cron() default "";

	/**
	 * 将解析 cron 表达式的时区。 默认:""(即使用服务器的本地时区)。
	 * 
	 * @return java.util.TimeZone.getTimeZone(String) 接受的区域 ID,或指示服务器默认时区的空字符串
	 * @since 4.0
	 * @see org.springframework.scheduling.support.CronTrigger#CronTrigger(String, java.util.TimeZone)
	 * @see java.util.TimeZone
	 */
	String zone() default "";

	/**
	 * 在上次调用结束和下一次调用开始之间的固定时间段,单位:毫秒,默认:-1(不延迟)
	 * 
	 * @return 延迟时长,单位:毫秒
	 */
	long fixedDelay() default -1;

	/**
	 * 上次调用的结束和下一次调用的开始之间固定时间间隔字符串,单位:毫秒。 
	 * 
	 * @return 延迟值字符串,单位:毫秒,例如占位符或
	 *         {@link java.time.Duration#parse java.time.Duration} 兼容值
	 * @since 3.2.2
	 */
	String fixedDelayString() default "";

	/**
	 * 在调用之间的固定时间段,单位:毫秒。
	 * 
	 * @return 以毫秒为单位的周期
	 */
	long fixedRate() default -1;

	/**
	 * 在调用之间的固定时间段字符串,单位:毫秒。
	 * 
	 * @return 延迟值字符串,单位:毫秒,例如占位符或 
	 *         {@link java.time.Duration#parse java.time.Duration} 兼容值
	 * @since 3.2.2
	 */
	String fixedRateString() default "";

	/**
	 * 在第一次执行 fixedRate 或 fixedDelay 任务之前延迟的毫秒数。
	 * 
	 * @return 初始延迟值,单位:毫秒
	 * @since 3.2
	 */
	long initialDelay() default -1;

	/**
	 * 在第一次执行 fixedRate 或 fixedDelay 任务之前延迟的毫秒数字符串。
	 * @return 初始延迟值字符串,单位:毫秒,例如占位符或符合 java.time.Duration 的值
	 * @since 3.2.2
	 */
	String initialDelayString() default "";

}
package com.demo.schedule;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@EnableScheduling  
@Component
@Slf4j
public class ScheduleTest {

    //每5秒执行一次
    //在配置文件中将cron时间配置为- 即可关闭当前这个定时任务
    @Scheduled(cron = "${schedule.test1Cron}")
    public void test_1() {
        log.info("测试任务1: " + System.currentTimeMillis());
    }

    //每5秒执行一次
    @Scheduled(cron = "${schedule.test2Cron}")
    public void test_2() {
        log.info("测试任务2: " + System.currentTimeMillis());
    }
}

application.propertites配置文件:

#定时任务1
schedule.test1Cron= -
#定时任务2
schedule.test2Cron= -

1.4.1、原理

@Scheduled注解中有个常量参数CRON_DISABLED值为- ,当配置cron的值为-时,则不加入任务池中,即实现了关闭定时任务。

源码分析见下图:

位置:org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#processScheduled
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五月天的尾巴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值