如何在Spring中有条件地启用或禁用预定作业?

时间:2013-08-23 15:36:28

标签: java spring cron scheduled-tasks

我使用@Scheduled注释在Spring中定义了具有cron样式模式的预定作业。

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

我在spring上下文中配置了一个属性占位符bean,允许我使用${}样式占位符从我的属性文件中导入值。

作业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,具体取决于此配置参数?

一个明显的解决方法是定义永远不会评估的cron模式,因此永远不会执行作业。但是仍然会创建bean并且配置有点模糊,所以我觉得必须有更好的解决方案。

10 个答案:

答案 0 :(得分:37)

@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...

        }
    }
}

答案 1 :(得分:26)

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

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

答案 2 :(得分:24)

您可以按条件将计划方法分组为多个服务,并按如下方式启动它们:

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

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

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

}

答案 3 :(得分:7)

在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());
}

有关更多信息:

enter image description here

答案 4 :(得分:6)

如果您希望从属性中切换@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

答案 5 :(得分:4)

你的问题陈述了条件实际创建bean。如果您至少使用Spring 3.1,则可以使用@Profile轻松地使用此参数执行此操作。

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

答案 6 :(得分: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;
    }
}

答案 7 :(得分:0)

我知道我的答案是黑客攻击,但是提供一个永远不会执行的有效cron表达式可能会解决问题(在特定于环境的配置中),Quartz: Cron expression that will never execute

答案 8 :(得分: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 = "";
    }
}

答案 9 :(得分:0)

请在另一个问题中查看我的回答。我认为这是解决此问题的最佳方法。 How to stop a scheduled task that was started using @Scheduled annotation?

定义如下的自定义注释。

@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。