I am defining scheduled jobs with cron style patterns in Spring, using the @Scheduled
annotation.
The cron pattern is stored in a config properties file
We can disable the bean creation of the class having that scheduled methods using @Conditional annotation. This is very similar to @ConditionalOnProperty. This is used to conditionally spin up a bean on to the spring context. If we set the value to false, then the bean will not be spun up and loaded to spring. Below is the code.
application.properties:
com.boot.enable.scheduling=enable
Condition:
public class ConditionalBeans implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return "enabled".equalsIgnoreCase(context.getEnvironment().getProperty("com.boot.enable.scheduling"));
}
}
My schedule class
@Service
@Conditional(ConditionalSchedules.class)
public class PrintPeriodicallyService {
@Scheduled(fixedRate = 3000)
public void runEvery3Seconds() {
System.out.println("Current time : " + new Date().getTime());
}
}
This approach has a lot of flexibility where the condition generation is totally under our control.
You can also create a Bean based on condition and that Bean can have a Scheduled method.
@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 = "";
}
}
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.
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.
@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;
}
}
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?
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.
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.
@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.
The most efficient way to disable @Scheduled in Spring. Just set crone expression like "-". It will disable the @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: