我使用@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并且配置有点模糊,所以我觉得必须有更好的解决方案。
答案 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());
}
有关更多信息:
答案 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轻松地使用此参数执行此操作。
答案 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。