如何在Spring中有条件地启用或禁用计划的作业?


69

我正在使用@Scheduled批注在Spring中使用cron样式模式定义计划的作业。

Cron模式存储在配置属性文件中。实际上,有两个属性文件:一个默认配置,以及一个与环境相关的配置文件配置(例如dev,test,prod客户1,prod客户2等),并覆盖某些默认值。

我在Spring上下文中配置了一个属性占位符bean,这使我可以使用${}样式占位符从属性文件中导入值。

工作豆看起来像这样:

@Component
public class ImagesPurgeJob implements Job {

    private Logger logger = Logger.getLogger(this.getClass());

    @Override
    @Transactional(readOnly=true)
    @Scheduled(cron = "${jobs.mediafiles.imagesPurgeJob.schedule}")
    public void execute() {
        //Do something
            //can use DAO or other autowired beans here
    }
}

我的上下文XML的相关部分:

<!-- Enable configuration of scheduled tasks via annotations -->
    <task:annotation-driven/>

<!-- Load configuration files and allow '${}' style placeholders -->
    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:config/default-config.properties</value>
                <value>classpath:config/environment-config.properties</value>
            </list>
        </property>
        <property name="ignoreUnresolvablePlaceholders" value="true"/>
        <property name="ignoreResourceNotFound" value="false"/>
    </bean>

我真的很喜欢 使用最少的XML十分简单干净。

但是,我还有一个要求:在某些情况下,其中一些工作可能会完全被禁用。

因此,在使用Spring管理它们之前,我手动创建了它们,并且在配置文件中有一个布尔参数和cron参数,以指定是否必须启用该作业:

jobs.mediafiles.imagesPurgeJob.enable=true or false
jobs.mediafiles.imagesPurgeJob.schedule=0 0 0/12 * * ?

如何在Spring中使用此参数有条件地创建或完全忽略bean(取决于此config参数)?

一个显而易见的解决方法是定义一个永远不会求值的cron模式,因此永远不会执行该作业。但是仍然会创建bean,并且配置会有些晦涩,因此我觉得必须有一个更好的解决方案。


您是否找到了答案?
2014年

1
@Chaos:不,仍然为此使用额外的布尔属性。
皮埃尔·亨利

“永远不会求值的cron模式”不起作用,spring调度程序代码需要有效的“下一个运行日期”,否则会引发异常。(可能并非所有版本都正确)
DavidBalažic17年

Answers:


48
@Component
public class ImagesPurgeJob implements Job {

    private Logger logger = Logger.getLogger(this.getClass());

    @Value("${jobs.mediafiles.imagesPurgeJob.enable}")
    private boolean imagesPurgeJobEnable;

    @Override
    @Transactional(readOnly=true)
    @Scheduled(cron = "${jobs.mediafiles.imagesPurgeJob.schedule}")
    public void execute() {

         //Do something
        //can use DAO or other autowired beans here
        if(imagesPurgeJobEnable){

            Do your conditional job here...

        }
    }
}

14
谢谢,这是一个选择,但是我宁愿完全实例化bean而不是创建bean并让方法定期执行,即使它什么也不做。
皮埃尔·亨利

2
我最终像这样实现了它,因为它虽然简单但并不完美。而且我了解了@Value注释,这很好。
皮埃尔·亨利

13
而不是“在这里做您的工作”,我宁愿检查是否(!启用){return;},我认为这样更干净。
杰夫·王

31

您可以按条件将调度方法分组为服务数量,然后按如下所示初始化它们:

@Service
@ConditionalOnProperty("yourConditionPropery")
public class SchedulingService {

@Scheduled
public void task1() {...}

@Scheduled
public void task2() {...}

}

可以@ConditionalOnProperty采用单一方法吗?还是为不同的@Scheduled方法指定不同的属性?
izogfif

@izogfif据我所知,它只能放在创建的方法上@Bean@ConditionalOnProperty继承自Conditional链接
kamwo

30

Spring Boot提供了@ConditionalOnProperty,如果您使用的是Spring Boot,这将是完美的。这个注释是@Conditional的特化,是Spring 4.0.0引入的。

假设您只使用“常规” spring而不是Spring Boot,则可以创建自己的Condition实现以与@Conditional一起使用,该实现将模仿Spring Boot的@ConditionalOnProperty。


2
理想情况下,这应该可以工作,但对于我的Spring Boot 1.3.2-RELEASE而言,它不起作用。
dARKpRINCE,2016年

也无法使用SpringBoot 1.4.0中的@ConditionalOnProperty做到这一点
ETL

4
根据@Conditional文档,注释仅在方法级别适用于以注释的方法@Bean。Bean定义。
jordanpg

19

在Spring中禁用@Scheduled的最有效方法。只需将crone表达式设置为“-”。它将禁用@Scheduled。

@Scheduled(cron = "-")
public void autoEvictAllCache() {
    LOGGER.info("Refresing the Cache Start :: " + new Date());
    activeMQUtility.sendToTopicCacheEviction("ALL");
    LOGGER.info("Refresing the Cache Complete :: " + new Date());
}

有关更多信息:

在此处输入图片说明


14

如果您希望从属性切换@EnableScheduling,则可以在Spring Boot中通过将@EnableScheduling批注移至配置类并按如下方式使用@ConditionalOnProperty来实现:

@Configuration
@EnableScheduling
@ConditionalOnProperty(prefix = "com.example.scheduling", name="enabled", havingValue="true", matchIfMissing = true)
public class SchedulingConfiguration {

}

这将禁用应用程序的计划。在您希望能够一次运行或计划运行该应用程序(取决于其启动方式)的情况下,这可能很有用。

来自wilkinsona的评论在这里:https : //github.com/spring-projects/spring-boot/issues/12682


4

您的问题指出要限制Bean的实际创建。如果您至少使用Spring 3.1,则可以使用@Profile通过此参数轻松完成此操作。

请参阅此处的文档:http : //static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/context/annotation/Profile.html


虽然配置文件在简单的开发/产品环境集中就可以了,但是我认为它们并没有真正适应多种生产环境(例如,具有不同功能打开/关闭的多个客户),并且无法与基于属性文件和活动配置文件的Maven生成时选择。现在,所有与环境相关的参数都集中在一个属性文件中。所有的Spring配置都是一样的。如果我们使用弹簧轮廓,则不会是这种情况。
皮埃尔·亨利

您可以在自定义初始化程序中有条件地激活spring配置文件(例如,通过扩展ApplicationContextInitializer <ConfigurableWebApplicationContext>并将其添加到web.xml)。然后,在初始化程序中,您可以获取环境并根据属性设置激活配置文件,例如。字符串标志= environment.getProperty(“ my.custom.property”); if(“ true” .equals(flag)){environment.activateProfile(“ MY_CUSTOM_PROFILE”)}。我们经常在不同的客户环境中将其用于不同的功能,并启用/禁用某些测试功能(例如,启用零延迟服务)。
杂耍

2
@Component
public class CurrencySyncServiceImpl implements CurrencySyncService {

    private static Boolean isEnableSync;
    /**
     * Currency Sync FixedDelay in minutes
     */
    private static Integer fixedDelay;

    @Transactional
    @Override
    @Scheduled(fixedDelayString = "#{${currency.sync.fixedDelay}*60*1000}")
    public void sync() {
        if(CurrencySyncServiceImpl.isEnableSync) {
            //Do something
            //you can use DAO or other autowired beans here.
        }
    }

    @Value("${currency.sync.fixedDelay}")
    public void setFixedDelay(Integer fixedDelay) {
        CurrencySyncServiceImpl.fixedDelay = fixedDelay;
    }

    @Value("${currency.sync.isEnable}")
    public void setIsEnableSync(Boolean isEnableSync) {
        CurrencySyncServiceImpl.isEnableSync = isEnableSync;
    }
}

1
您不是在禁用调度程序,而是在登录中添加有条件的内容
Harshal Patil '18

1

请在另一个问题中查看我的回答。我认为这是解决此问题的最佳方法。 如何停止使用@Scheduled批注启动的计划任务?

定义一个自定义注释,如下所示。

@Documented
@Retention (RUNTIME)
@Target(ElementType.TYPE)
public @interface ScheduledSwitch {
    // do nothing
}

定义一个实现org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor的类。

public class ScheduledAnnotationBeanPostProcessorCustom 
    extends ScheduledAnnotationBeanPostProcessor {

    @Value(value = "${prevent.scheduled.tasks:false}")
    private boolean preventScheduledTasks;

    private Map<Object, String> beans = new HashMap<>();

    private final ReentrantLock lock = new ReentrantLock(true);

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        ScheduledSwitch switch = AopProxyUtils.ultimateTargetClass(bean)
            .getAnnotation(ScheduledSwitch.class);
        if (null != switch) {
            beans.put(bean, beanName);
            if (preventScheduledTasks) {
                return bean;
            }
        }
        return super.postProcessAfterInitialization(bean, beanName);
    }

    public void stop() {
        lock.lock();
        try {
            for (Map.Entry<Object, String> entry : beans.entrySet()) {
                postProcessBeforeDestruction(entry.getKey(), entry.getValue());
            }
        } finally {
            lock.unlock();
        }
    }

    public void start() {
        lock.lock();
        try {
            for (Map.Entry<Object, String> entry : beans.entrySet()) {
                if (!requiresDestruction(entry.getKey())) {
                    super.postProcessAfterInitialization(
                        entry.getKey(), entry.getValue());
                }
            }
        } finally {
            lock.unlock();
        }
    }

}

在配置中,用自定义bean替换ScheduledAnnotationBeanPostProcessor bean。

@Configuration
public class ScheduledConfig {

    @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationBeanPostProcessor() {
        return new ScheduledAnnotationBeanPostProcessorCustom();
    }

}

将@ScheduledSwitch批注添加到要阻止或停止@Scheduled任务的bean。



0

您还可以根据条件创建Bean,并且Bean可以具有Scheduled方法。

@Component
@Configuration
@EnableScheduling
public class CustomCronComponent {
    @Bean
    @ConditionalOnProperty(value = "my.cron.enabled", matchIfMissing = true, havingValue = "true")
    public MyCronTask runMyCronTask() {
        return new MyCronTask();
    }
}

@Component
public class MyCronTask {
    @Scheduled(cron = "${my.cron.expression}")
    public void run() {
        String a = "";
    }
}

0

我们可以使用@Conditional批注禁用具有预定方法的类的bean创建。这与@ConditionalOnProperty非常相似。这用于有条件地将bean旋转到spring上下文。如果将值设置为false,则不会将bean旋转并加载到spring。下面是代码。

application.properties:

com.boot.enable.scheduling=enable

健康)状况:

public class ConditionalBeans implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return "enabled".equalsIgnoreCase(context.getEnvironment().getProperty("com.boot.enable.scheduling"));
    }
}

我的日程课

@Service
@Conditional(ConditionalSchedules.class)
public class PrintPeriodicallyService {

    @Scheduled(fixedRate = 3000)
    public void runEvery3Seconds() {
        System.out.println("Current time : " + new Date().getTime());
    }
}

在条件生成完全由我们控制的情况下,这种方法具有很大的灵活性。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.