我需要以固定的时间间隔运行批处理作业,并且能够在运行时更改此批处理作业的时间。为此,我遇到@Scheduled
了 Spring 框架下提供的注解。但我不确定如何在运行时更改 fixedDelay 的值。我做了一些谷歌搜索,但没有发现任何有用的东西。
6 回答
在spring boot中,可以直接使用一个应用属性!
例如:
@Scheduled(fixedDelayString = "${my.property.fixed.delay.seconds}000")
private void process() {
// your impl here
}
请注意,如果未定义该属性,您还可以使用默认值,例如默认值为“60”(秒):
@Scheduled(fixedDelayString = "${my.property.fixed.delay.seconds:60}000")
我发现的其他事情:
- 该方法必须是无效的
- 该方法必须没有参数
- 该方法可能是
private
我发现能够private
方便地使用可见性并以这种方式使用它:
@Service
public class MyService {
public void process() {
// do something
}
@Scheduled(fixedDelayString = "${my.poll.fixed.delay.seconds}000")
private void autoProcess() {
process();
}
}
作为private
,计划的方法可以是您的服务本地的,而不是您的服务 API 的一部分。
此外,这种方法允许process()
方法返回一个值,而@Scheduled
方法可能不会。例如,您的process()
方法可能如下所示:
public ProcessResult process() {
// do something and collect information about what was done
return processResult;
}
提供一些关于处理过程中发生的事情的信息。
您可以使用 aTrigger
来动态设置下一次执行时间。
有关详细信息,请参阅我对以编程方式使用 Spring 调度工作的回答。
创建接口,类似这样:
public abstract class DynamicSchedule{
/**
* Delays scheduler
* @param milliseconds - the time to delay scheduler.
*/
abstract void delay(Long milliseconds);
/**
* Decreases delay period
* @param milliseconds - the time to decrease delay period.
*/
abstract void decreaseDelayInterval(Long milliseconds);
/**
* Increases delay period
* @param milliseconds - the time to increase dela period
*/
abstract void increaseDelayInterval(Long milliseconds);
}
接下来,让我们在 spring-context 项目中实现位于 org.springframework.scheduling 的 Trigger 接口。
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import java.util.Date;
import java.util.concurrent.ScheduledFuture;
public class CustomDynamicSchedule extends DynamicSchedule implements Trigger {
private TaskScheduler taskScheduler;
private ScheduledFuture<?> schedulerFuture;
/**
* milliseconds
*/
private long delayInterval;
public CustomDynamicSchedule(TaskScheduler taskScheduler) {
this.taskScheduler = taskScheduler;
}
@Override
public void increaseDelayInterval(Long delay) {
if (schedulerFuture != null) {
schedulerFuture.cancel(true);
}
this.delayInterval += delay;
schedulerFuture = taskScheduler.schedule(() -> { }, this);
}
@Override
public void decreaseDelayInterval(Long delay) {
if (schedulerFuture != null) {
schedulerFuture.cancel(true);
}
this.delayInterval -= delay;
schedulerFuture = taskScheduler.schedule(() -> { }, this);
}
@Override
public void delay(Long delay) {
if (schedulerFuture != null) {
schedulerFuture.cancel(true);
}
this.delayInterval = delay;
schedulerFuture = taskScheduler.schedule(() -> { }, this);
}
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
Date lastTime = triggerContext.lastActualExecutionTime();
return (lastTime == null) ? new Date() : new Date(lastTime.getTime() + delayInterval);
}
}
现在配置:
@Configuration
public class DynamicSchedulerConfig {
@Bean
public CustomDynamicSchedule getDynamicScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.initialize();
return new CustomDynamicSchedule(threadPoolTaskScheduler);
}
}
和用法:
@EnableScheduling
@Component
public class TestSchedulerComponent {
@Autowired
private CustomDynamicSchedule dynamicSchedule;
@Scheduled(fixedDelay = 5000)
public void testMethod() {
dynamicSchedule.delay(1000l);
dynamicSchedule.increaseDelayInterval(9000l);
dynamicSchedule.decreaseDelayInterval(5000l);
}
}
您也可以为此使用 Spring 表达式语言 (SpEL)。
@Scheduled(fixedDelayString = "#{@applicationPropertyService.getApplicationProperty()}")
public void getSchedule(){
System.out.println("in scheduled job");
}
@Service
public class ApplicationPropertyService {
public String getApplicationProperty(){
//get your value here
return "5000";
}
}
AFAIK Spring API 不会让您访问更改触发器所需的内部结构。但是您可以改为手动配置 bean:
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="jobDetail" />
<property name="startDelay" value="10000" />
<property name="repeatInterval" value="50000" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="simpleTrigger" />
</list>
</property>
</bean>
然后如 SchedulerFactoryBean 中所述:
要在运行时动态注册作业,请使用对此 SchedulerFactoryBean 的 bean 引用来直接访问 Quartz Scheduler (
org.quartz.Scheduler
)。这使您可以创建新的作业和触发器,还可以控制和监视整个调度程序。
我处理过同样的问题。我们需要在运行时更改 cron 表达式并重新安排服务。所以应该有:
- 无需重新编译
- 没有重新部署
- 不重启
的应用程序。我检查了所有流行的解决方案,但只有 2 个满足所有要求。
- 按照建议实施SchedulingConfigurer _
- 使用此处描述的自定义解决方案
SchedulingConfigurer 方法的缺点是它是基于拉取的,即每次执行服务的业务逻辑时都会拉取调度配置。这通常不是一件坏事,但是如果配置很少更改并且执行间隔很短,那么将会有很多不必要的请求。
自定义解决方案的缺点是它需要更多的编码,但它是基于推送的并对配置更改做出反应,因此不会执行不必要的请求/调用。