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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18406713/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 01:20:39  来源:igfitidea点击:

How to conditionally enable or disable scheduled jobs in Spring?

javaspringcronscheduled-tasks

提问by Pierre Henry

I am defining scheduled jobs with cron style patterns in Spring, using the @Scheduledannotation.

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

The cron pattern is stored in a config properties file. Actually there are two properties files: one default config, and one profile config that is environment dependent (e.g. dev, test, prod customer 1, prod customer 2 etc.) and overrides some of the default values.

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

I configured a property placeholder bean in my spring context which allows me to use ${}style placeholders to import values from my properties files.

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

The job beans looks like this:

作业 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
    }
}

Relevant parts of my context XML :

我的上下文 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>

I really like this. It's quite simple and clean with minimal XML.

我真的很喜欢这个。它非常简单和干净,使用最少的 XML。

However I have one more requirement: some of these jobs can be totally disabled in some cases.

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

So, before I used Spring to manage them I created them manually and there is a boolean parameter along with the cron parameter in the config files, to specify if the job has to be enabled or not:

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

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

How can I use this parameter in Spring to conditionally create or just plainly ignore the bean, depending on this config parameter?

如何根据此配置参数在 Spring 中使用此参数有条件地创建或直接忽略 bean?

One obvious workaround would be to define a cron pattern that would never evaluate, so the job is never executed. But the bean would still be created and the config would be a bit obscure, so I feel there must be a better solution.

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

采纳答案by Prabhakaran Ramaswamy

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

        }
    }
}

回答by aweigold

Your question states to condition the actual creation of the bean. You can do this easily with this parameter by using @Profile if you are using at least Spring 3.1.

您的问题说明要调节 bean 的实际创建。如果您至少使用 Spring 3.1,则可以通过使用 @Profile 使用此参数轻松完成此操作。

See the documentation here: http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/context/annotation/Profile.html

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

回答by tmullin

Spring Boot provides @ConditionalOnProperty, which would be perfect if you were using Spring Boot. This annotation is a specialization of @Conditional, introduced with Spring 4.0.0.

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

Assuming you're just using "regular" spring and not Spring Boot, you could create your own Condition implementation for use with @Conditional that would mimic Spring Boot's @ConditionalOnProperty.

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

回答by Oleg

You can group schedule methods by conditions into number of services and init them like this:

您可以按条件将调度方法分组为服务数量,并像这样初始化它们:

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

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

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

}

回答by Vinit Solanki

@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;
    }
}

回答by Suraj Muraleedharan

I know my answer is a hack, but giving a valid cron expression that never executes may fix the issue (in the environment specific configuration), Quartz: Cron expression that will never execute

我知道我的答案是一个 hack,但是给出一个永远不会执行的有效 cron 表达式可能会解决这个问题(在特定于环境的配置中),Quartz:永远不会执行的 Cron 表达式

回答by user3474985

If you are looking to toggle @EnableScheduling from a property you can do this in Spring Boot by moving the @EnableScheduling annotation to a configuration class and use @ConditionalOnProperty as follows:

如果您希望从属性中切换 @EnableScheduling,您可以通过将 @EnableScheduling 注释移动到配置类并使用 @ConditionalOnProperty 在 Spring Boot 中执行此操作,如下所示:

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

}

This will disable scheduling for the application. This may be useful in a situation where you want to be able to run the application once or scheduled depending on how it's being started.

这将禁用应用程序的调度。在您希望能够运行应用程序一次或根据其启动方式进行调度的情况下,这可能很有用。

From wilkinsona's comment on here: https://github.com/spring-projects/spring-boot/issues/12682

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

回答by labm0nkey

You can also create a Bean based on condition and that Bean can have a Scheduled method.

您还可以根据条件创建一个 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();
    }
}

and

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

回答by Vpn_talent

The most efficient way to disable @Scheduled in Spring. Just set crone expression like "-". It will disable the @Scheduled.

在 Spring 中禁用 @Scheduled 的最有效方法。只需设置像“-”这样的老太婆表达。它将禁用@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());
}

For more info:

欲了解更多信息:

enter image description here

在此处输入图片说明

回答by Bin

Please see my answer in another question. I think this is the best way to solve it. How to stop a scheduled task that was started using @Scheduled annotation?

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

Define a custom annotation like below.

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

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

Define a class implements org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.

定义一个类实现 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();
        }
    }

}

Replace ScheduledAnnotationBeanPostProcessor bean by the custom bean in configuration.

用配置中的自定义 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();
    }

}

Add @ScheduledSwitch annotation to the beans that you want to prevent or stop @Scheduled tasks.

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