Browse Source

Revise and document TimeUnit support in @Scheduled

This commit also fixes a bug introduced in commit e99b43b91e, where
java.time.Duration strings were converted to milliseconds and then
converted again using the configured TimeUnit.

See gh-27309
pull/27330/head
Sam Brannen 3 years ago
parent
commit
bd72e4498b
  1. 54
      spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java
  2. 29
      spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java
  3. 454
      spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java
  4. 41
      src/docs/asciidoc/integration.adoc

54
spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java

@ -48,6 +48,7 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar; @@ -48,6 +48,7 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
* @author Dave Syer
* @author Chris Beams
* @author Victor Brown
* @author Sam Brannen
* @since 3.0
* @see EnableScheduling
* @see ScheduledAnnotationBeanPostProcessor
@ -103,63 +104,74 @@ public @interface Scheduled { @@ -103,63 +104,74 @@ public @interface Scheduled {
String zone() default "";
/**
* Execute the annotated method with a fixed period between the
* end of the last invocation and the start of the next.
* Using milliseconds by default with timeUnit().
* Execute the annotated method with a fixed period between the end of the
* last invocation and the start of the next.
* <p>The time unit is milliseconds by default but can be overridden via
* {@link #timeUnit}.
* @return the delay
*/
long fixedDelay() default -1;
/**
* Execute the annotated method with a fixed period between the
* end of the last invocation and the start of the next.
* Using milliseconds by default with fixedDelayTimeUnit().
* @return the delay as a String value, e.g. a placeholder
* Execute the annotated method with a fixed period between the end of the
* last invocation and the start of the next.
* <p>The time unit is milliseconds by default but can be overridden via
* {@link #timeUnit}.
* @return the delay as a String value &mdash; for example, a placeholder
* or a {@link java.time.Duration#parse java.time.Duration} compliant value
* @since 3.2.2
*/
String fixedDelayString() default "";
/**
* Execute the annotated method with a fixed period between
* invocations.
* Using milliseconds by default with timeUnit().
* Execute the annotated method with a fixed period between invocations.
* <p>The time unit is milliseconds by default but can be overridden via
* {@link #timeUnit}.
* @return the period
*/
long fixedRate() default -1;
/**
* Execute the annotated method with a fixed period between
* invocations.
* Using milliseconds by default with fixedRateTimeUnit().
* @return the period as a String value, e.g. a placeholder
* Execute the annotated method with a fixed period between invocations.
* <p>The time unit is milliseconds by default but can be overridden via
* {@link #timeUnit}.
* @return the period as a String value &mdash; for example, a placeholder
* or a {@link java.time.Duration#parse java.time.Duration} compliant value
* @since 3.2.2
*/
String fixedRateString() default "";
/**
* Number to delay before the first execution of a
* Number of units of time to delay before the first execution of a
* {@link #fixedRate} or {@link #fixedDelay} task.
* Using milliseconds by default with timeUnit().
* <p>The time unit is milliseconds by default but can be overridden via
* {@link #timeUnit}.
* @return the initial
* @since 3.2
*/
long initialDelay() default -1;
/**
* Number to delay before the first execution of a
* Number of units of time to delay before the first execution of a
* {@link #fixedRate} or {@link #fixedDelay} task.
* Using milliseconds by default with initialDelayTimeUnit().
* @return the initial delay in milliseconds as a String value, e.g. a placeholder
* <p>The time unit is milliseconds by default but can be overridden via
* {@link #timeUnit}.
* @return the initial delay as a String value &mdash; for example, a placeholder
* or a {@link java.time.Duration#parse java.time.Duration} compliant value
* @since 3.2.2
*/
String initialDelayString() default "";
/**
* Specify the {@link TimeUnit} to use for initialDelay, fixedRate and fixedDelay values.
* @return the {@link TimeUnit}, by default milliseconds will be used.
* The {@link TimeUnit} to use for {@link #fixedDelay}, {@link #fixedDelayString},
* {@link #fixedRate}, {@link #fixedRateString}, {@link #initialDelay}, and
* {@link #initialDelayString}.
* <p>Defaults to {@link TimeUnit#MICROSECONDS}.
* <p>This attribute is ignored for {@linkplain #cron() cron expressions}
* and for {@link java.time.Duration} values supplied via {@link #fixedDelayString},
* {@link #fixedRateString}, or {@link #initialDelayString}.
* @return the {@code TimeUnit} to use
* @since 5.3.10
*/
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;

29
spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java

@ -96,6 +96,7 @@ import org.springframework.util.StringValueResolver; @@ -96,6 +96,7 @@ import org.springframework.util.StringValueResolver;
* @author Chris Beams
* @author Elizabeth Chatman
* @author Victor Brown
* @author Sam Brannen
* @since 3.0
* @see Scheduled
* @see EnableScheduling
@ -385,7 +386,7 @@ public class ScheduledAnnotationBeanPostProcessor @@ -385,7 +386,7 @@ public class ScheduledAnnotationBeanPostProcessor
/**
* Process the given {@code @Scheduled} method declaration on the given bean.
* @param scheduled the @Scheduled annotation
* @param scheduled the {@code @Scheduled} annotation
* @param method the method that the annotation has been declared on
* @param bean the target bean instance
* @see #createRunnable(Object, Method)
@ -400,7 +401,7 @@ public class ScheduledAnnotationBeanPostProcessor @@ -400,7 +401,7 @@ public class ScheduledAnnotationBeanPostProcessor
Set<ScheduledTask> tasks = new LinkedHashSet<>(4);
// Determine initial delay
long initialDelay = TimeUnit.MILLISECONDS.convert(scheduled.initialDelay(), scheduled.timeUnit());
long initialDelay = convertToMillis(scheduled.initialDelay(), scheduled.timeUnit());
String initialDelayString = scheduled.initialDelayString();
if (StringUtils.hasText(initialDelayString)) {
Assert.isTrue(initialDelay < 0, "Specify 'initialDelay' or 'initialDelayString', not both");
@ -409,7 +410,7 @@ public class ScheduledAnnotationBeanPostProcessor @@ -409,7 +410,7 @@ public class ScheduledAnnotationBeanPostProcessor
}
if (StringUtils.hasLength(initialDelayString)) {
try {
initialDelay = TimeUnit.MILLISECONDS.convert(parseDelayAsLong(initialDelayString), scheduled.timeUnit());
initialDelay = convertToMillis(initialDelayString, scheduled.timeUnit());
}
catch (RuntimeException ex) {
throw new IllegalArgumentException(
@ -448,7 +449,7 @@ public class ScheduledAnnotationBeanPostProcessor @@ -448,7 +449,7 @@ public class ScheduledAnnotationBeanPostProcessor
}
// Check fixed delay
long fixedDelay = TimeUnit.MILLISECONDS.convert(scheduled.fixedDelay(), scheduled.timeUnit());
long fixedDelay = convertToMillis(scheduled.fixedDelay(), scheduled.timeUnit());
if (fixedDelay >= 0) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
@ -464,7 +465,7 @@ public class ScheduledAnnotationBeanPostProcessor @@ -464,7 +465,7 @@ public class ScheduledAnnotationBeanPostProcessor
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
try {
fixedDelay = TimeUnit.MILLISECONDS.convert(parseDelayAsLong(fixedDelayString), scheduled.timeUnit());
fixedDelay = convertToMillis(fixedDelayString, scheduled.timeUnit());
}
catch (RuntimeException ex) {
throw new IllegalArgumentException(
@ -475,7 +476,7 @@ public class ScheduledAnnotationBeanPostProcessor @@ -475,7 +476,7 @@ public class ScheduledAnnotationBeanPostProcessor
}
// Check fixed rate
long fixedRate = TimeUnit.MILLISECONDS.convert(scheduled.fixedRate(), scheduled.timeUnit());
long fixedRate = convertToMillis(scheduled.fixedRate(), scheduled.timeUnit());
if (fixedRate >= 0) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
@ -490,7 +491,7 @@ public class ScheduledAnnotationBeanPostProcessor @@ -490,7 +491,7 @@ public class ScheduledAnnotationBeanPostProcessor
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
try {
fixedRate = TimeUnit.MILLISECONDS.convert(parseDelayAsLong(fixedRateString), scheduled.timeUnit());
fixedRate = convertToMillis(fixedRateString, scheduled.timeUnit());
}
catch (RuntimeException ex) {
throw new IllegalArgumentException(
@ -530,11 +531,19 @@ public class ScheduledAnnotationBeanPostProcessor @@ -530,11 +531,19 @@ public class ScheduledAnnotationBeanPostProcessor
return new ScheduledMethodRunnable(target, invocableMethod);
}
private static long parseDelayAsLong(String value) throws RuntimeException {
if (value.length() > 1 && (isP(value.charAt(0)) || isP(value.charAt(1)))) {
private static long convertToMillis(long value, TimeUnit timeUnit) {
return TimeUnit.MILLISECONDS.convert(value, timeUnit);
}
private static long convertToMillis(String value, TimeUnit timeUnit) {
if (isDurationString(value)) {
return Duration.parse(value).toMillis();
}
return Long.parseLong(value);
return convertToMillis(Long.parseLong(value), timeUnit);
}
private static boolean isDurationString(String value) {
return (value.length() > 1 && (isP(value.charAt(0)) || isP(value.charAt(1))));
}
private static boolean isP(char ch) {

454
spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java

@ -33,6 +33,12 @@ import java.util.concurrent.TimeUnit; @@ -33,6 +33,12 @@ import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.converter.ArgumentConversionException;
import org.junit.jupiter.params.converter.ArgumentConverter;
import org.junit.jupiter.params.converter.ConvertWith;
import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.scope.ScopedProxyUtils;
@ -61,8 +67,11 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess @@ -61,8 +67,11 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.SoftAssertions.assertSoftly;
/**
* Tests for {@link ScheduledAnnotationBeanPostProcessor}.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @author Chris Beams
@ -70,77 +79,25 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -70,77 +79,25 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* @author Stevo Slavić
* @author Victor Brown
*/
public class ScheduledAnnotationBeanPostProcessorTests {
class ScheduledAnnotationBeanPostProcessorTests {
private final StaticApplicationContext context = new StaticApplicationContext();
@AfterEach
public void closeContextAfterTest() {
void closeContextAfterTest() {
context.close();
}
@Test
public void fixedDelayTask() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(FixedDelayTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
ScheduledTaskHolder postProcessor = context.getBean("postProcessor", ScheduledTaskHolder.class);
assertThat(postProcessor.getScheduledTasks().size()).isEqualTo(1);
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
@SuppressWarnings("unchecked")
List<IntervalTask> fixedDelayTasks = (List<IntervalTask>)
new DirectFieldAccessor(registrar).getPropertyValue("fixedDelayTasks");
assertThat(fixedDelayTasks.size()).isEqualTo(1);
IntervalTask task = fixedDelayTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedDelay");
assertThat(task.getInitialDelay()).isEqualTo(0L);
assertThat(task.getInterval()).isEqualTo(5000L);
}
@Test
public void fixedDelayWithSecondsTimeUnitTask() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(FixedDelayWithSecondsTimeUnitTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
ScheduledTaskHolder postProcessor = context.getBean("postProcessor", ScheduledTaskHolder.class);
assertThat(postProcessor.getScheduledTasks().size()).isEqualTo(1);
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
@SuppressWarnings("unchecked")
List<IntervalTask> fixedDelayTasks = (List<IntervalTask>)
new DirectFieldAccessor(registrar).getPropertyValue("fixedDelayTasks");
assertThat(fixedDelayTasks.size()).isEqualTo(1);
IntervalTask task = fixedDelayTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedDelay");
assertThat(task.getInitialDelay()).isEqualTo(0L);
assertThat(task.getInterval()).isEqualTo(5000L);
}
@Test
public void fixedDelayWithMinutesTimeUnitTask() {
@ParameterizedTest
@CsvSource({
"FixedDelay, 5000",
"FixedDelayInSeconds, 5000",
"FixedDelayInMinutes, 180000"
})
void fixedDelayTask(@NameToClass Class<?> beanClass, long expectedInterval) {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(FixedDelayWithMinutesTimeUnitTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(beanClass);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
@ -154,7 +111,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -154,7 +111,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
@SuppressWarnings("unchecked")
List<IntervalTask> fixedDelayTasks = (List<IntervalTask>)
new DirectFieldAccessor(registrar).getPropertyValue("fixedDelayTasks");
assertThat(fixedDelayTasks.size()).isEqualTo(1);
assertThat(fixedDelayTasks).hasSize(1);
IntervalTask task = fixedDelayTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
@ -162,125 +119,18 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -162,125 +119,18 @@ public class ScheduledAnnotationBeanPostProcessorTests {
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedDelay");
assertThat(task.getInitialDelay()).isEqualTo(0L);
assertThat(task.getInterval()).isEqualTo(180000L);
}
@Test
public void fixedRateTask() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(FixedRateTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
ScheduledTaskHolder postProcessor = context.getBean("postProcessor", ScheduledTaskHolder.class);
assertThat(postProcessor.getScheduledTasks().size()).isEqualTo(1);
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
@SuppressWarnings("unchecked")
List<IntervalTask> fixedRateTasks = (List<IntervalTask>)
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
assertThat(fixedRateTasks.size()).isEqualTo(1);
IntervalTask task = fixedRateTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertThat(task.getInitialDelay()).isEqualTo(0L);
assertThat(task.getInterval()).isEqualTo(3000L);
}
@Test
public void fixedRateWithSecondsTimeUnitTask() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(FixedRateWithSecondsTimeUnitTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
ScheduledTaskHolder postProcessor = context.getBean("postProcessor", ScheduledTaskHolder.class);
assertThat(postProcessor.getScheduledTasks().size()).isEqualTo(1);
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
@SuppressWarnings("unchecked")
List<IntervalTask> fixedRateTasks = (List<IntervalTask>)
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
assertThat(fixedRateTasks.size()).isEqualTo(1);
IntervalTask task = fixedRateTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertThat(task.getInitialDelay()).isEqualTo(0L);
assertThat(task.getInterval()).isEqualTo(5000L);
}
@Test
public void fixedRateWithMinutesTimeUnitTask() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(FixedRateWithMinutesTimeUnitTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
ScheduledTaskHolder postProcessor = context.getBean("postProcessor", ScheduledTaskHolder.class);
assertThat(postProcessor.getScheduledTasks().size()).isEqualTo(1);
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
@SuppressWarnings("unchecked")
List<IntervalTask> fixedRateTasks = (List<IntervalTask>)
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
assertThat(fixedRateTasks.size()).isEqualTo(1);
IntervalTask task = fixedRateTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertThat(task.getInitialDelay()).isEqualTo(0L);
assertThat(task.getInterval()).isEqualTo(180000L);
}
@Test
public void fixedRateTaskWithInitialDelay() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(FixedRateWithInitialDelayTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
ScheduledTaskHolder postProcessor = context.getBean("postProcessor", ScheduledTaskHolder.class);
assertThat(postProcessor.getScheduledTasks().size()).isEqualTo(1);
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
@SuppressWarnings("unchecked")
List<IntervalTask> fixedRateTasks = (List<IntervalTask>)
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
assertThat(fixedRateTasks.size()).isEqualTo(1);
IntervalTask task = fixedRateTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertThat(task.getInitialDelay()).isEqualTo(1000L);
assertThat(task.getInterval()).isEqualTo(3000L);
assertThat(task.getInterval()).isEqualTo(expectedInterval);
}
@Test
public void fixedRateTaskWithSecondsTimeUnitWithInitialDelay() {
@ParameterizedTest
@CsvSource({
"FixedRate, 3000",
"FixedRateInSeconds, 5000",
"FixedRateInMinutes, 180000"
})
void fixedRateTask(@NameToClass Class<?> beanClass, long expectedInterval) {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(FixedRateWithSecondsTimeUnitInitialDelayTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(beanClass);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
@ -301,14 +151,21 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -301,14 +151,21 @@ public class ScheduledAnnotationBeanPostProcessorTests {
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertThat(task.getInitialDelay()).isEqualTo(5000L);
assertThat(task.getInterval()).isEqualTo(3000L);
}
@Test
public void fixedRateTaskWithMinutesTimeUnitWithInitialDelay() {
assertSoftly(softly -> {
softly.assertThat(task.getInitialDelay()).as("initial delay").isEqualTo(0);
softly.assertThat(task.getInterval()).as("interval").isEqualTo(expectedInterval);
});
}
@ParameterizedTest
@CsvSource({
"FixedRateWithInitialDelay, 1000, 3000",
"FixedRateWithInitialDelayInSeconds, 5000, 3000",
"FixedRateWithInitialDelayInMinutes, 60000, 180000"
})
void fixedRateTaskWithInitialDelay(@NameToClass Class<?> beanClass, long expectedInitialDelay, long expectedInterval) {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(FixedRateWithMinutesTimeUnitInitialDelayTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(beanClass);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
@ -329,40 +186,42 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -329,40 +186,42 @@ public class ScheduledAnnotationBeanPostProcessorTests {
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertThat(task.getInitialDelay()).isEqualTo(60000L);
assertThat(task.getInterval()).isEqualTo(180000L);
assertSoftly(softly -> {
softly.assertThat(task.getInitialDelay()).as("initial delay").isEqualTo(expectedInitialDelay);
softly.assertThat(task.getInterval()).as("interval").isEqualTo(expectedInterval);
});
}
@Test
public void severalFixedRatesWithRepeatedScheduledAnnotation() {
void severalFixedRatesWithRepeatedScheduledAnnotation() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(SeveralFixedRatesWithRepeatedScheduledAnnotationTestBean.class);
severalFixedRates(context, processorDefinition, targetDefinition);
}
@Test
public void severalFixedRatesWithSchedulesContainerAnnotation() {
void severalFixedRatesWithSchedulesContainerAnnotation() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(SeveralFixedRatesWithSchedulesContainerAnnotationTestBean.class);
severalFixedRates(context, processorDefinition, targetDefinition);
}
@Test
public void severalFixedRatesOnBaseClass() {
void severalFixedRatesOnBaseClass() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(FixedRatesSubBean.class);
severalFixedRates(context, processorDefinition, targetDefinition);
}
@Test
public void severalFixedRatesOnDefaultMethod() {
void severalFixedRatesOnDefaultMethod() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(FixedRatesDefaultBean.class);
severalFixedRates(context, processorDefinition, targetDefinition);
}
@Test
public void severalFixedRatesAgainstNestedCglibProxy() {
void severalFixedRatesAgainstNestedCglibProxy() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(SeveralFixedRatesWithRepeatedScheduledAnnotationTestBean.class);
targetDefinition.setFactoryMethodName("nestedProxy");
@ -405,7 +264,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -405,7 +264,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void cronTask() {
void cronTask() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(CronTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
@ -432,7 +291,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -432,7 +291,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void cronTaskWithZone() {
void cronTaskWithZone() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(CronWithTimezoneTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
@ -478,7 +337,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -478,7 +337,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void cronTaskWithInvalidZone() {
void cronTaskWithInvalidZone() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(CronWithInvalidTimezoneTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
@ -488,7 +347,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -488,7 +347,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void cronTaskWithMethodValidation() {
void cronTaskWithMethodValidation() {
BeanDefinition validationDefinition = new RootBeanDefinition(MethodValidationPostProcessor.class);
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(CronTestBean.class);
@ -500,7 +359,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -500,7 +359,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void cronTaskWithScopedProxy() {
void cronTaskWithScopedProxy() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
new AnnotatedBeanDefinitionReader(context).register(ProxiedCronTestBean.class, ProxiedCronTestBeanDependent.class);
@ -525,7 +384,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -525,7 +384,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void metaAnnotationWithFixedRate() {
void metaAnnotationWithFixedRate() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(MetaAnnotationFixedRateTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
@ -552,7 +411,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -552,7 +411,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void composedAnnotationWithInitialDelayAndFixedRate() {
void composedAnnotationWithInitialDelayAndFixedRate() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(ComposedAnnotationFixedRateTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
@ -580,7 +439,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -580,7 +439,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void metaAnnotationWithCronExpression() {
void metaAnnotationWithCronExpression() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(MetaAnnotationCronTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
@ -607,7 +466,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -607,7 +466,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void propertyPlaceholderWithCron() {
void propertyPlaceholderWithCron() {
String businessHoursCronExpression = "0 0 9-17 * * MON-FRI";
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition placeholderDefinition = new RootBeanDefinition(PropertySourcesPlaceholderConfigurer.class);
@ -640,7 +499,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -640,7 +499,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void propertyPlaceholderWithInactiveCron() {
void propertyPlaceholderWithInactiveCron() {
String businessHoursCronExpression = "-";
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition placeholderDefinition = new RootBeanDefinition(PropertySourcesPlaceholderConfigurer.class);
@ -657,24 +516,23 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -657,24 +516,23 @@ public class ScheduledAnnotationBeanPostProcessorTests {
assertThat(postProcessor.getScheduledTasks().isEmpty()).isTrue();
}
@Test
public void propertyPlaceholderWithFixedDelayInMillis() {
propertyPlaceholderWithFixedDelay(false);
}
@Test
public void propertyPlaceholderWithFixedDelayInDuration() {
propertyPlaceholderWithFixedDelay(true);
}
@ParameterizedTest
@CsvSource({
"PropertyPlaceholderWithFixedDelay, 5000, 1000, 5000, 1000",
"PropertyPlaceholderWithFixedDelay, PT5S, PT1S, 5000, 1000",
"PropertyPlaceholderWithFixedDelayInSeconds, 5000, 1000, 5000000, 1000000",
"PropertyPlaceholderWithFixedDelayInSeconds, PT5S, PT1S, 5000, 1000"
})
void propertyPlaceholderWithFixedDelay(@NameToClass Class<?> beanClass, String fixedDelay, String initialDelay,
long expectedInterval, long expectedInitialDelay) {
private void propertyPlaceholderWithFixedDelay(boolean durationFormat) {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition placeholderDefinition = new RootBeanDefinition(PropertySourcesPlaceholderConfigurer.class);
Properties properties = new Properties();
properties.setProperty("fixedDelay", (durationFormat ? "PT5S" : "5000"));
properties.setProperty("initialDelay", (durationFormat ? "PT1S" : "1000"));
properties.setProperty("fixedDelay", fixedDelay);
properties.setProperty("initialDelay", initialDelay);
placeholderDefinition.getPropertyValues().addPropertyValue("properties", properties);
BeanDefinition targetDefinition = new RootBeanDefinition(PropertyPlaceholderWithFixedDelayTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(beanClass);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("placeholder", placeholderDefinition);
context.registerBeanDefinition("target", targetDefinition);
@ -696,28 +554,29 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -696,28 +554,29 @@ public class ScheduledAnnotationBeanPostProcessorTests {
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedDelay");
assertThat(task.getInitialDelay()).isEqualTo(1000L);
assertThat(task.getInterval()).isEqualTo(5000L);
}
@Test
public void propertyPlaceholderWithFixedRateInMillis() {
propertyPlaceholderWithFixedRate(false);
}
@Test
public void propertyPlaceholderWithFixedRateInDuration() {
propertyPlaceholderWithFixedRate(true);
}
assertSoftly(softly -> {
softly.assertThat(task.getInitialDelay()).as("initial delay").isEqualTo(expectedInitialDelay);
softly.assertThat(task.getInterval()).as("interval").isEqualTo(expectedInterval);
});
}
@ParameterizedTest
@CsvSource({
"PropertyPlaceholderWithFixedRate, 3000, 1000, 3000, 1000",
"PropertyPlaceholderWithFixedRate, PT3S, PT1S, 3000, 1000",
"PropertyPlaceholderWithFixedRateInSeconds, 3000, 1000, 3000000, 1000000",
"PropertyPlaceholderWithFixedRateInSeconds, PT3S, PT1S, 3000, 1000"
})
void propertyPlaceholderWithFixedRate(@NameToClass Class<?> beanClass, String fixedRate, String initialDelay,
long expectedInterval, long expectedInitialDelay) {
private void propertyPlaceholderWithFixedRate(boolean durationFormat) {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition placeholderDefinition = new RootBeanDefinition(PropertySourcesPlaceholderConfigurer.class);
Properties properties = new Properties();
properties.setProperty("fixedRate", (durationFormat ? "PT3S" : "3000"));
properties.setProperty("initialDelay", (durationFormat ? "PT1S" : "1000"));
properties.setProperty("fixedRate", fixedRate);
properties.setProperty("initialDelay", initialDelay);
placeholderDefinition.getPropertyValues().addPropertyValue("properties", properties);
BeanDefinition targetDefinition = new RootBeanDefinition(PropertyPlaceholderWithFixedRateTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(beanClass);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("placeholder", placeholderDefinition);
context.registerBeanDefinition("target", targetDefinition);
@ -739,12 +598,14 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -739,12 +598,14 @@ public class ScheduledAnnotationBeanPostProcessorTests {
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertThat(task.getInitialDelay()).isEqualTo(1000L);
assertThat(task.getInterval()).isEqualTo(3000L);
assertSoftly(softly -> {
softly.assertThat(task.getInitialDelay()).as("initial delay").isEqualTo(expectedInitialDelay);
softly.assertThat(task.getInterval()).as("interval").isEqualTo(expectedInterval);
});
}
@Test
public void expressionWithCron() {
void expressionWithCron() {
String businessHoursCronExpression = "0 0 9-17 * * MON-FRI";
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(ExpressionWithCronTestBean.class);
@ -775,7 +636,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -775,7 +636,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void propertyPlaceholderForMetaAnnotation() {
void propertyPlaceholderForMetaAnnotation() {
String businessHoursCronExpression = "0 0 9-17 * * MON-FRI";
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition placeholderDefinition = new RootBeanDefinition(PropertySourcesPlaceholderConfigurer.class);
@ -808,7 +669,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -808,7 +669,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void nonVoidReturnType() {
void nonVoidReturnType() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(NonVoidReturnTypeTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
@ -835,7 +696,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -835,7 +696,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void emptyAnnotation() {
void emptyAnnotation() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(EmptyAnnotationTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
@ -845,7 +706,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -845,7 +706,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void invalidCron() throws Throwable {
void invalidCron() throws Throwable {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(InvalidCronTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
@ -855,7 +716,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -855,7 +716,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
@Test
public void nonEmptyParamList() {
void nonEmptyParamList() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(NonEmptyParamListTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
@ -865,70 +726,68 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -865,70 +726,68 @@ public class ScheduledAnnotationBeanPostProcessorTests {
}
static class FixedDelayTestBean {
static class FixedDelay {
@Scheduled(fixedDelay = 5000)
public void fixedDelay() {
void fixedDelay() {
}
}
static class FixedDelayWithSecondsTimeUnitTestBean {
static class FixedDelayInSeconds {
@Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS)
public void fixedDelay() {
void fixedDelay() {
}
}
static class FixedDelayWithMinutesTimeUnitTestBean {
static class FixedDelayInMinutes {
@Scheduled(fixedDelay = 3, timeUnit = TimeUnit.MINUTES)
public void fixedDelay() {
void fixedDelay() {
}
}
static class FixedRateTestBean {
static class FixedRate {
@Scheduled(fixedRate = 3000)
public void fixedRate() {
void fixedRate() {
}
}
static class FixedRateWithSecondsTimeUnitTestBean {
static class FixedRateInSeconds {
@Scheduled(fixedRate = 5, timeUnit = TimeUnit.SECONDS)
public void fixedRate() {
void fixedRate() {
}
}
static class FixedRateWithMinutesTimeUnitTestBean {
static class FixedRateInMinutes {
@Scheduled(fixedRate = 3, timeUnit = TimeUnit.MINUTES)
public void fixedRate() {
void fixedRate() {
}
}
static class FixedRateWithInitialDelayTestBean {
static class FixedRateWithInitialDelay {
@Scheduled(fixedRate = 3000, initialDelay = 1000)
public void fixedRate() {
void fixedRate() {
}
}
static class FixedRateWithSecondsTimeUnitInitialDelayTestBean {
static class FixedRateWithInitialDelayInSeconds {
@Scheduled(fixedRate = 3, initialDelay = 5, timeUnit = TimeUnit.SECONDS)
public void fixedRate() {
void fixedRate() {
}
}
static class FixedRateWithMinutesTimeUnitInitialDelayTestBean {
static class FixedRateWithInitialDelayInMinutes {
@Scheduled(fixedRate = 3, initialDelay = 1, timeUnit = TimeUnit.MINUTES)
public void fixedRate() {
void fixedRate() {
}
}
@ -936,7 +795,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -936,7 +795,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class SeveralFixedRatesWithSchedulesContainerAnnotationTestBean {
@Schedules({@Scheduled(fixedRate = 4000), @Scheduled(fixedRate = 4000, initialDelay = 2000)})
public void fixedRate() {
void fixedRate() {
}
}
@ -945,7 +804,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -945,7 +804,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
@Scheduled(fixedRate = 4000)
@Scheduled(fixedRate = 4000, initialDelay = 2000)
public void fixedRate() {
void fixedRate() {
}
static SeveralFixedRatesWithRepeatedScheduledAnnotationTestBean nestedProxy() {
@ -962,7 +821,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -962,7 +821,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
@Scheduled(fixedRate = 4000)
@Scheduled(fixedRate = 4000, initialDelay = 2000)
public void fixedRate() {
void fixedRate() {
}
}
@ -1006,7 +865,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -1006,7 +865,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class CronWithInvalidTimezoneTestBean {
@Scheduled(cron = "0 0 0-4,6-23 * * ?", zone = "FOO")
public void cron() throws IOException {
void cron() throws IOException {
throw new IOException("no no no");
}
}
@ -1017,7 +876,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -1017,7 +876,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class ProxiedCronTestBean {
@Scheduled(cron = "*/7 * * * * ?")
public void cron() throws IOException {
void cron() throws IOException {
throw new IOException("no no no");
}
}
@ -1025,7 +884,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -1025,7 +884,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class ProxiedCronTestBeanDependent {
public ProxiedCronTestBeanDependent(ProxiedCronTestBean testBean) {
ProxiedCronTestBeanDependent(ProxiedCronTestBean testBean) {
}
}
@ -1033,7 +892,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -1033,7 +892,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class NonVoidReturnTypeTestBean {
@Scheduled(cron = "0 0 9-17 * * MON-FRI")
public String cron() {
String cron() {
return "oops";
}
}
@ -1042,7 +901,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -1042,7 +901,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class EmptyAnnotationTestBean {
@Scheduled
public void invalid() {
void invalid() {
}
}
@ -1050,7 +909,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -1050,7 +909,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class InvalidCronTestBean {
@Scheduled(cron = "abc")
public void invalid() {
void invalid() {
}
}
@ -1058,7 +917,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -1058,7 +917,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class NonEmptyParamListTestBean {
@Scheduled(fixedRate = 3000)
public void invalid(String oops) {
void invalid(String oops) {
}
}
@ -1090,7 +949,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -1090,7 +949,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class MetaAnnotationFixedRateTestBean {
@EveryFiveSeconds
public void checkForUpdates() {
void checkForUpdates() {
}
}
@ -1098,7 +957,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -1098,7 +957,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class ComposedAnnotationFixedRateTestBean {
@WaitASec(fixedRate = 5000)
public void checkForUpdates() {
void checkForUpdates() {
}
}
@ -1106,7 +965,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -1106,7 +965,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class MetaAnnotationCronTestBean {
@Hourly
public void generateReport() {
void generateReport() {
}
}
@ -1114,23 +973,37 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -1114,23 +973,37 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class PropertyPlaceholderWithCronTestBean {
@Scheduled(cron = "${schedules.businessHours}")
public void x() {
void x() {
}
}
static class PropertyPlaceholderWithFixedDelayTestBean {
static class PropertyPlaceholderWithFixedDelay {
@Scheduled(fixedDelayString = "${fixedDelay}", initialDelayString = "${initialDelay}")
public void fixedDelay() {
void fixedDelay() {
}
}
static class PropertyPlaceholderWithFixedDelayInSeconds {
@Scheduled(fixedDelayString = "${fixedDelay}", initialDelayString = "${initialDelay}", timeUnit = TimeUnit.SECONDS)
void fixedDelay() {
}
}
static class PropertyPlaceholderWithFixedRateTestBean {
static class PropertyPlaceholderWithFixedRate {
@Scheduled(fixedRateString = "${fixedRate}", initialDelayString = "${initialDelay}")
public void fixedRate() {
void fixedRate() {
}
}
static class PropertyPlaceholderWithFixedRateInSeconds {
@Scheduled(fixedRateString = "${fixedRate}", initialDelayString = "${initialDelay}", timeUnit = TimeUnit.SECONDS)
void fixedRate() {
}
}
@ -1138,7 +1011,7 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -1138,7 +1011,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class ExpressionWithCronTestBean {
@Scheduled(cron = "#{schedules.businessHours}")
public void x() {
void x() {
}
}
@ -1153,7 +1026,24 @@ public class ScheduledAnnotationBeanPostProcessorTests { @@ -1153,7 +1026,24 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class PropertyPlaceholderMetaAnnotationTestBean {
@BusinessHours
public void y() {
void y() {
}
}
@Retention(RetentionPolicy.RUNTIME)
@ConvertWith(NameToClass.Converter.class)
private @interface NameToClass {
class Converter implements ArgumentConverter {
@Override
public Class<?> convert(Object beanClassName, ParameterContext context) throws ArgumentConversionException {
try {
String name = getClass().getEnclosingClass().getEnclosingClass().getName() + "$" + beanClassName;
return getClass().getClassLoader().loadClass(name);
}
catch (Exception ex) {
throw new ArgumentConversionException("Failed to convert class name to Class", ex);
}
}
}
}

41
src/docs/asciidoc/integration.adoc

@ -4923,37 +4923,54 @@ switching to `aspectj` mode in combination with compile-time or load-time weavin @@ -4923,37 +4923,54 @@ switching to `aspectj` mode in combination with compile-time or load-time weavin
==== The `@Scheduled` annotation
You can add the `@Scheduled` annotation to a method, along with trigger metadata. For
example, the following method is invoked every five seconds with a fixed delay,
meaning that the period is measured from the completion time of each preceding
invocation:
example, the following method is invoked every five seconds (5000 milliseconds) with a
fixed delay, meaning that the period is measured from the completion time of each
preceding invocation.
[source,java,indent=0,subs="verbatim,quotes"]
----
@Scheduled(fixedDelay=5000)
@Scheduled(fixedDelay = 5000)
public void doSomething() {
// something that should run periodically
}
----
If you need a fixed-rate execution, you can change the property name specified within
the annotation. The following method is invoked every five seconds (measured between the
successive start times of each invocation):
[NOTE]
====
By default, milliseconds will be used as the time unit for fixed delay, fixed rate, and
initial delay values. If you would like to use a different time unit such as seconds or
minutes, you can configure this via the `timeUnit` attribute in `@Scheduled`.
For example, the previous example can also be written as follows.
[source,java,indent=0,subs="verbatim,quotes"]
----
@Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS)
public void doSomething() {
// something that should run periodically
}
----
====
If you need a fixed-rate execution, you can use the `fixedRate` attribute within the
annotation. The following method is invoked every five seconds (measured between the
successive start times of each invocation).
[source,java,indent=0,subs="verbatim,quotes"]
----
@Scheduled(fixedRate=5000)
@Scheduled(fixedRate = 5, timeUnit = TimeUnit.SECONDS)
public void doSomething() {
// something that should run periodically
}
----
For fixed-delay and fixed-rate tasks, you can specify an initial delay by indicating the
number of milliseconds to wait before the first execution of the method, as the following
`fixedRate` example shows:
amount of time to wait before the first execution of the method, as the following
`fixedRate` example shows.
[source,java,indent=0,subs="verbatim,quotes"]
----
@Scheduled(initialDelay=1000, fixedRate=5000)
@Scheduled(initialDelay = 1000, fixedRate = 5000)
public void doSomething() {
// something that should run periodically
}
@ -4975,7 +4992,7 @@ The following example runs only on weekdays: @@ -4975,7 +4992,7 @@ The following example runs only on weekdays:
TIP: You can also use the `zone` attribute to specify the time zone in which the cron
expression is resolved.
Notice that the methods to be scheduled must have void returns and must not expect any
Notice that the methods to be scheduled must have void returns and must not accept any
arguments. If the method needs to interact with other objects from the application
context, those would typically have been provided through dependency injection.

Loading…
Cancel
Save