From c33ceda40a9929f60ef8c89a8ca2f5785823ae48 Mon Sep 17 00:00:00 2001 From: Axzial Date: Sat, 21 Aug 2021 18:00:26 +0200 Subject: [PATCH 1/6] Using TimeUnit with @Scheduled annotation --- .../scheduling/annotation/Scheduled.java | 48 +++++++++++++---- .../ScheduledAnnotationBeanPostProcessor.java | 51 ++++++------------- 2 files changed, 52 insertions(+), 47 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java index 415c72381792..48913f06acc9 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java @@ -22,7 +22,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; +import org.joda.time.DateTimeUtils; import org.springframework.scheduling.config.ScheduledTaskRegistrar; /** @@ -101,52 +103,76 @@ String zone() default ""; /** - * Execute the annotated method with a fixed period in milliseconds between the + * Execute the annotated method with a fixed period between the * end of the last invocation and the start of the next. - * @return the delay in milliseconds + * Using milliseconds by default with fixedDelayTimeUnit(). + * @return the delay */ long fixedDelay() default -1; /** - * Execute the annotated method with a fixed period in milliseconds between the + * Execute the annotated method with a fixed period between the * end of the last invocation and the start of the next. - * @return the delay in milliseconds as a String value, e.g. a placeholder + * Using milliseconds by default with fixedDelayTimeUnit(). + * @return the delay as a String value, e.g. 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 in milliseconds between + * Specify the {@link TimeUnit} to use for the fixedDelay and the fixedDelayString values. + * @return the {@link TimeUnit}, by default milliseconds will be used. + */ + TimeUnit fixedDelayTimeUnit() default TimeUnit.MILLISECONDS; + + /** + * Execute the annotated method with a fixed period between * invocations. - * @return the period in milliseconds + * Using milliseconds by default with fixedRateTimeUnit(). + * @return the period */ long fixedRate() default -1; /** - * Execute the annotated method with a fixed period in milliseconds between + * Execute the annotated method with a fixed period between * invocations. - * @return the period in milliseconds as a String value, e.g. a placeholder + * Using milliseconds by default with fixedRateTimeUnit(). + * @return the period as a String value, e.g. a placeholder * or a {@link java.time.Duration#parse java.time.Duration} compliant value * @since 3.2.2 */ String fixedRateString() default ""; /** - * Number of milliseconds to delay before the first execution of a + * Specify the {@link TimeUnit} to use for the fixedRate and the fixedRateString values. + * @return the {@link TimeUnit}, by default milliseconds will be used. + */ + TimeUnit fixedRateTimeUnit() default TimeUnit.MILLISECONDS; + + /** + * Number to delay before the first execution of a * {@link #fixedRate} or {@link #fixedDelay} task. - * @return the initial delay in milliseconds + * Using milliseconds by default with initialDelayTimeUnit(). + * @return the initial * @since 3.2 */ long initialDelay() default -1; /** - * Number of milliseconds to delay before the first execution of a + * Number 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 * 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 the initialDelay and the initialDelayString values. + * @return the {@link TimeUnit}, by default milliseconds will be used. + */ + TimeUnit initialDelayTimeUnit() default TimeUnit.MILLISECONDS; + } diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java index 14b73d6d7f7c..385fefbeab74 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java @@ -16,35 +16,12 @@ package org.springframework.scheduling.annotation; -import java.lang.reflect.Method; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledExecutorService; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.aop.framework.AopInfrastructureBean; import org.springframework.aop.framework.AopProxyUtils; import org.springframework.aop.support.AopUtils; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.ListableBeanFactory; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.NoUniqueBeanDefinitionException; -import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.beans.factory.*; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; @@ -64,18 +41,19 @@ import org.springframework.lang.Nullable; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.Trigger; -import org.springframework.scheduling.config.CronTask; -import org.springframework.scheduling.config.FixedDelayTask; -import org.springframework.scheduling.config.FixedRateTask; -import org.springframework.scheduling.config.ScheduledTask; -import org.springframework.scheduling.config.ScheduledTaskHolder; -import org.springframework.scheduling.config.ScheduledTaskRegistrar; +import org.springframework.scheduling.config.*; import org.springframework.scheduling.support.CronTrigger; import org.springframework.scheduling.support.ScheduledMethodRunnable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.util.StringValueResolver; +import java.lang.reflect.Method; +import java.time.Duration; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledExecutorService; + /** * Bean post-processor that registers methods annotated with @{@link Scheduled} * to be invoked by a {@link org.springframework.scheduling.TaskScheduler} according @@ -398,7 +376,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Set tasks = new LinkedHashSet<>(4); // Determine initial delay - long initialDelay = scheduled.initialDelay(); + long initialDelay = scheduled.initialDelayTimeUnit().toMillis(1) * scheduled.initialDelay(); String initialDelayString = scheduled.initialDelayString(); if (StringUtils.hasText(initialDelayString)) { Assert.isTrue(initialDelay < 0, "Specify 'initialDelay' or 'initialDelayString', not both"); @@ -407,7 +385,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) } if (StringUtils.hasLength(initialDelayString)) { try { - initialDelay = parseDelayAsLong(initialDelayString); + initialDelay = scheduled.initialDelayTimeUnit().toMillis(1) * parseDelayAsLong(initialDelayString); } catch (RuntimeException ex) { throw new IllegalArgumentException( @@ -446,12 +424,13 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) } // Check fixed delay - long fixedDelay = scheduled.fixedDelay(); + long fixedDelay = scheduled.fixedDelayTimeUnit().toMillis(1) * scheduled.fixedDelay(); if (fixedDelay >= 0) { Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay))); } + String fixedDelayString = scheduled.fixedDelayString(); if (StringUtils.hasText(fixedDelayString)) { if (this.embeddedValueResolver != null) { @@ -461,7 +440,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; try { - fixedDelay = parseDelayAsLong(fixedDelayString); + fixedDelay = scheduled.fixedDelayTimeUnit().toMillis(1) * parseDelayAsLong(fixedDelayString); } catch (RuntimeException ex) { throw new IllegalArgumentException( @@ -472,7 +451,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) } // Check fixed rate - long fixedRate = scheduled.fixedRate(); + long fixedRate = scheduled.fixedRateTimeUnit().toMillis(1) * scheduled.fixedRate(); if (fixedRate >= 0) { Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; @@ -487,7 +466,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; try { - fixedRate = parseDelayAsLong(fixedRateString); + fixedRate = scheduled.fixedRateTimeUnit().toMillis(1) * parseDelayAsLong(fixedRateString); } catch (RuntimeException ex) { throw new IllegalArgumentException( From 54e41f3e548d0030d75d74e3244199e319da64ea Mon Sep 17 00:00:00 2001 From: Axzial Date: Sat, 21 Aug 2021 18:00:26 +0200 Subject: [PATCH 2/6] Using TimeUnit with @Scheduled annotation --- .../scheduling/annotation/Scheduled.java | 56 +++++++++++++------ .../ScheduledAnnotationBeanPostProcessor.java | 51 +++++------------ 2 files changed, 53 insertions(+), 54 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java index 415c72381792..e18e68e52631 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java @@ -16,15 +16,11 @@ package org.springframework.scheduling.annotation; -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Repeatable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - import org.springframework.scheduling.config.ScheduledTaskRegistrar; +import java.lang.annotation.*; +import java.util.concurrent.TimeUnit; + /** * Annotation that marks a method to be scheduled. Exactly one of the * {@link #cron}, {@link #fixedDelay}, or {@link #fixedRate} attributes must be @@ -101,52 +97,76 @@ String zone() default ""; /** - * Execute the annotated method with a fixed period in milliseconds between the + * Execute the annotated method with a fixed period between the * end of the last invocation and the start of the next. - * @return the delay in milliseconds + * Using milliseconds by default with fixedDelayTimeUnit(). + * @return the delay */ long fixedDelay() default -1; /** - * Execute the annotated method with a fixed period in milliseconds between the + * Execute the annotated method with a fixed period between the * end of the last invocation and the start of the next. - * @return the delay in milliseconds as a String value, e.g. a placeholder + * Using milliseconds by default with fixedDelayTimeUnit(). + * @return the delay as a String value, e.g. 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 in milliseconds between + * Specify the {@link TimeUnit} to use for the fixedDelay and the fixedDelayString values. + * @return the {@link TimeUnit}, by default milliseconds will be used. + */ + TimeUnit fixedDelayTimeUnit() default TimeUnit.MILLISECONDS; + + /** + * Execute the annotated method with a fixed period between * invocations. - * @return the period in milliseconds + * Using milliseconds by default with fixedRateTimeUnit(). + * @return the period */ long fixedRate() default -1; /** - * Execute the annotated method with a fixed period in milliseconds between + * Execute the annotated method with a fixed period between * invocations. - * @return the period in milliseconds as a String value, e.g. a placeholder + * Using milliseconds by default with fixedRateTimeUnit(). + * @return the period as a String value, e.g. a placeholder * or a {@link java.time.Duration#parse java.time.Duration} compliant value * @since 3.2.2 */ String fixedRateString() default ""; /** - * Number of milliseconds to delay before the first execution of a + * Specify the {@link TimeUnit} to use for the fixedRate and the fixedRateString values. + * @return the {@link TimeUnit}, by default milliseconds will be used. + */ + TimeUnit fixedRateTimeUnit() default TimeUnit.MILLISECONDS; + + /** + * Number to delay before the first execution of a * {@link #fixedRate} or {@link #fixedDelay} task. - * @return the initial delay in milliseconds + * Using milliseconds by default with initialDelayTimeUnit(). + * @return the initial * @since 3.2 */ long initialDelay() default -1; /** - * Number of milliseconds to delay before the first execution of a + * Number 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 * 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 the initialDelay and the initialDelayString values. + * @return the {@link TimeUnit}, by default milliseconds will be used. + */ + TimeUnit initialDelayTimeUnit() default TimeUnit.MILLISECONDS; + } diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java index 14b73d6d7f7c..385fefbeab74 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java @@ -16,35 +16,12 @@ package org.springframework.scheduling.annotation; -import java.lang.reflect.Method; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledExecutorService; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.aop.framework.AopInfrastructureBean; import org.springframework.aop.framework.AopProxyUtils; import org.springframework.aop.support.AopUtils; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.ListableBeanFactory; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.NoUniqueBeanDefinitionException; -import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.beans.factory.*; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; @@ -64,18 +41,19 @@ import org.springframework.lang.Nullable; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.Trigger; -import org.springframework.scheduling.config.CronTask; -import org.springframework.scheduling.config.FixedDelayTask; -import org.springframework.scheduling.config.FixedRateTask; -import org.springframework.scheduling.config.ScheduledTask; -import org.springframework.scheduling.config.ScheduledTaskHolder; -import org.springframework.scheduling.config.ScheduledTaskRegistrar; +import org.springframework.scheduling.config.*; import org.springframework.scheduling.support.CronTrigger; import org.springframework.scheduling.support.ScheduledMethodRunnable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.util.StringValueResolver; +import java.lang.reflect.Method; +import java.time.Duration; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledExecutorService; + /** * Bean post-processor that registers methods annotated with @{@link Scheduled} * to be invoked by a {@link org.springframework.scheduling.TaskScheduler} according @@ -398,7 +376,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Set tasks = new LinkedHashSet<>(4); // Determine initial delay - long initialDelay = scheduled.initialDelay(); + long initialDelay = scheduled.initialDelayTimeUnit().toMillis(1) * scheduled.initialDelay(); String initialDelayString = scheduled.initialDelayString(); if (StringUtils.hasText(initialDelayString)) { Assert.isTrue(initialDelay < 0, "Specify 'initialDelay' or 'initialDelayString', not both"); @@ -407,7 +385,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) } if (StringUtils.hasLength(initialDelayString)) { try { - initialDelay = parseDelayAsLong(initialDelayString); + initialDelay = scheduled.initialDelayTimeUnit().toMillis(1) * parseDelayAsLong(initialDelayString); } catch (RuntimeException ex) { throw new IllegalArgumentException( @@ -446,12 +424,13 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) } // Check fixed delay - long fixedDelay = scheduled.fixedDelay(); + long fixedDelay = scheduled.fixedDelayTimeUnit().toMillis(1) * scheduled.fixedDelay(); if (fixedDelay >= 0) { Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay))); } + String fixedDelayString = scheduled.fixedDelayString(); if (StringUtils.hasText(fixedDelayString)) { if (this.embeddedValueResolver != null) { @@ -461,7 +440,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; try { - fixedDelay = parseDelayAsLong(fixedDelayString); + fixedDelay = scheduled.fixedDelayTimeUnit().toMillis(1) * parseDelayAsLong(fixedDelayString); } catch (RuntimeException ex) { throw new IllegalArgumentException( @@ -472,7 +451,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) } // Check fixed rate - long fixedRate = scheduled.fixedRate(); + long fixedRate = scheduled.fixedRateTimeUnit().toMillis(1) * scheduled.fixedRate(); if (fixedRate >= 0) { Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; @@ -487,7 +466,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; try { - fixedRate = parseDelayAsLong(fixedRateString); + fixedRate = scheduled.fixedRateTimeUnit().toMillis(1) * parseDelayAsLong(fixedRateString); } catch (RuntimeException ex) { throw new IllegalArgumentException( From a42576561ae979e6acbf791b894aab6628bacd6e Mon Sep 17 00:00:00 2001 From: Axzial Date: Sun, 22 Aug 2021 18:51:28 +0200 Subject: [PATCH 3/6] Using TimeUnit "convert()" in ScheduledAnnotationBeanPostProcessor --- .../ScheduledAnnotationBeanPostProcessor.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java index bdd0b073412d..6683d786fd9b 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java @@ -30,6 +30,7 @@ import java.util.TimeZone; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -397,7 +398,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Set tasks = new LinkedHashSet<>(4); // Determine initial delay - long initialDelay = scheduled.initialDelayTimeUnit().toMillis(1) * scheduled.initialDelay(); + long initialDelay = TimeUnit.MILLISECONDS.convert(scheduled.initialDelay(), scheduled.initialDelayTimeUnit()); String initialDelayString = scheduled.initialDelayString(); if (StringUtils.hasText(initialDelayString)) { Assert.isTrue(initialDelay < 0, "Specify 'initialDelay' or 'initialDelayString', not both"); @@ -406,7 +407,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) } if (StringUtils.hasLength(initialDelayString)) { try { - initialDelay = scheduled.initialDelayTimeUnit().toMillis(1) * parseDelayAsLong(initialDelayString); + initialDelay = TimeUnit.MILLISECONDS.convert(parseDelayAsLong(initialDelayString), scheduled.initialDelayTimeUnit()); } catch (RuntimeException ex) { throw new IllegalArgumentException( @@ -445,7 +446,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) } // Check fixed delay - long fixedDelay = scheduled.fixedDelayTimeUnit().toMillis(1) * scheduled.fixedDelay(); + long fixedDelay = TimeUnit.MILLISECONDS.convert(scheduled.fixedDelay(), scheduled.fixedDelayTimeUnit()); if (fixedDelay >= 0) { Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; @@ -461,7 +462,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; try { - fixedDelay = scheduled.fixedDelayTimeUnit().toMillis(1) * parseDelayAsLong(fixedDelayString); + fixedDelay = TimeUnit.MILLISECONDS.convert(parseDelayAsLong(fixedDelayString), scheduled.fixedDelayTimeUnit()); } catch (RuntimeException ex) { throw new IllegalArgumentException( @@ -472,7 +473,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) } // Check fixed rate - long fixedRate = scheduled.fixedRateTimeUnit().toMillis(1) * scheduled.fixedRate(); + long fixedRate = TimeUnit.MILLISECONDS.convert(scheduled.fixedRate(), scheduled.fixedRateTimeUnit()); if (fixedRate >= 0) { Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; @@ -487,7 +488,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; try { - fixedRate = scheduled.fixedRateTimeUnit().toMillis(1) * parseDelayAsLong(fixedRateString); + fixedRate = TimeUnit.MILLISECONDS.convert(parseDelayAsLong(fixedRateString), scheduled.fixedRateTimeUnit()); } catch (RuntimeException ex) { throw new IllegalArgumentException( From 30e012b012166e5b92111311895ea743098055c4 Mon Sep 17 00:00:00 2001 From: Axzial Date: Mon, 23 Aug 2021 18:33:03 +0200 Subject: [PATCH 4/6] Using a single TimeUnit for all @Scheduled fields --- .../scheduling/annotation/Scheduled.java | 18 +++--------------- .../ScheduledAnnotationBeanPostProcessor.java | 13 +++++++------ 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java index c4f9e41c565c..c326fabcca5b 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java @@ -16,13 +16,13 @@ package org.springframework.scheduling.annotation; -import java.util.concurrent.TimeUnit; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; import org.springframework.scheduling.config.ScheduledTaskRegistrar; @@ -119,12 +119,6 @@ */ String fixedDelayString() default ""; - /** - * Specify the {@link TimeUnit} to use for the fixedDelay and the fixedDelayString values. - * @return the {@link TimeUnit}, by default milliseconds will be used. - */ - TimeUnit fixedDelayTimeUnit() default TimeUnit.MILLISECONDS; - /** * Execute the annotated method with a fixed period between * invocations. @@ -143,12 +137,6 @@ */ String fixedRateString() default ""; - /** - * Specify the {@link TimeUnit} to use for the fixedRate and the fixedRateString values. - * @return the {@link TimeUnit}, by default milliseconds will be used. - */ - TimeUnit fixedRateTimeUnit() default TimeUnit.MILLISECONDS; - /** * Number to delay before the first execution of a * {@link #fixedRate} or {@link #fixedDelay} task. @@ -169,9 +157,9 @@ String initialDelayString() default ""; /** - * Specify the {@link TimeUnit} to use for the initialDelay and the initialDelayString values. + * Specify the {@link TimeUnit} to use for initialDelay, fixedRate and fixedDelay values. * @return the {@link TimeUnit}, by default milliseconds will be used. */ - TimeUnit initialDelayTimeUnit() default TimeUnit.MILLISECONDS; + TimeUnit timeUnit() default TimeUnit.MILLISECONDS; } diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java index 6683d786fd9b..6bb2f21ad3d0 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java @@ -76,6 +76,7 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.util.StringValueResolver; + /** * Bean post-processor that registers methods annotated with @{@link Scheduled} * to be invoked by a {@link org.springframework.scheduling.TaskScheduler} according @@ -398,7 +399,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Set tasks = new LinkedHashSet<>(4); // Determine initial delay - long initialDelay = TimeUnit.MILLISECONDS.convert(scheduled.initialDelay(), scheduled.initialDelayTimeUnit()); + long initialDelay = TimeUnit.MILLISECONDS.convert(scheduled.initialDelay(), scheduled.timeUnit()); String initialDelayString = scheduled.initialDelayString(); if (StringUtils.hasText(initialDelayString)) { Assert.isTrue(initialDelay < 0, "Specify 'initialDelay' or 'initialDelayString', not both"); @@ -407,7 +408,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) } if (StringUtils.hasLength(initialDelayString)) { try { - initialDelay = TimeUnit.MILLISECONDS.convert(parseDelayAsLong(initialDelayString), scheduled.initialDelayTimeUnit()); + initialDelay = TimeUnit.MILLISECONDS.convert(parseDelayAsLong(initialDelayString), scheduled.timeUnit()); } catch (RuntimeException ex) { throw new IllegalArgumentException( @@ -446,7 +447,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) } // Check fixed delay - long fixedDelay = TimeUnit.MILLISECONDS.convert(scheduled.fixedDelay(), scheduled.fixedDelayTimeUnit()); + long fixedDelay = TimeUnit.MILLISECONDS.convert(scheduled.fixedDelay(), scheduled.timeUnit()); if (fixedDelay >= 0) { Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; @@ -462,7 +463,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; try { - fixedDelay = TimeUnit.MILLISECONDS.convert(parseDelayAsLong(fixedDelayString), scheduled.fixedDelayTimeUnit()); + fixedDelay = TimeUnit.MILLISECONDS.convert(parseDelayAsLong(fixedDelayString), scheduled.timeUnit()); } catch (RuntimeException ex) { throw new IllegalArgumentException( @@ -473,7 +474,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) } // Check fixed rate - long fixedRate = TimeUnit.MILLISECONDS.convert(scheduled.fixedRate(), scheduled.fixedRateTimeUnit()); + long fixedRate = TimeUnit.MILLISECONDS.convert(scheduled.fixedRate(), scheduled.timeUnit()); if (fixedRate >= 0) { Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; @@ -488,7 +489,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; try { - fixedRate = TimeUnit.MILLISECONDS.convert(parseDelayAsLong(fixedRateString), scheduled.fixedRateTimeUnit()); + fixedRate = TimeUnit.MILLISECONDS.convert(parseDelayAsLong(fixedRateString), scheduled.timeUnit()); } catch (RuntimeException ex) { throw new IllegalArgumentException( From b98d3a27da69739769e6d51233ab99dcba537aea Mon Sep 17 00:00:00 2001 From: Axzial Date: Mon, 23 Aug 2021 18:50:56 +0200 Subject: [PATCH 5/6] Fix @Scheduled TimeUnit feature comments --- .../springframework/scheduling/annotation/Scheduled.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java index c326fabcca5b..aa067ad0da46 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java @@ -104,7 +104,7 @@ /** * 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(). + * Using milliseconds by default with timeUnit(). * @return the delay */ long fixedDelay() default -1; @@ -122,7 +122,7 @@ /** * Execute the annotated method with a fixed period between * invocations. - * Using milliseconds by default with fixedRateTimeUnit(). + * Using milliseconds by default with timeUnit(). * @return the period */ long fixedRate() default -1; @@ -140,7 +140,7 @@ /** * Number to delay before the first execution of a * {@link #fixedRate} or {@link #fixedDelay} task. - * Using milliseconds by default with initialDelayTimeUnit(). + * Using milliseconds by default with timeUnit(). * @return the initial * @since 3.2 */ From edbfa509a7411dc4b5d7bd5e63b2c163f53e2b1b Mon Sep 17 00:00:00 2001 From: Axzial Date: Mon, 23 Aug 2021 19:12:32 +0200 Subject: [PATCH 6/6] Add @Scheduled TimeUnit tests --- ...duledAnnotationBeanPostProcessorTests.java | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) diff --git a/spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java b/spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java index 842910070227..db982c4ca53a 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.Properties; import java.util.TimeZone; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -107,6 +108,62 @@ public void fixedDelayTask() { 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 fixedDelayTasks = (List) + 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() { + BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class); + BeanDefinition targetDefinition = new RootBeanDefinition(FixedDelayWithMinutesTimeUnitTestBean.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 fixedDelayTasks = (List) + 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(180000L); + } + @Test public void fixedRateTask() { BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class); @@ -135,6 +192,62 @@ public void fixedRateTask() { 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 fixedRateTasks = (List) + 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 fixedRateTasks = (List) + 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); @@ -163,6 +276,62 @@ public void fixedRateTaskWithInitialDelay() { assertThat(task.getInterval()).isEqualTo(3000L); } + @Test + public void fixedRateTaskWithSecondsTimeUnitWithInitialDelay() { + BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class); + BeanDefinition targetDefinition = new RootBeanDefinition(FixedRateWithSecondsTimeUnitInitialDelayTestBean.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 fixedRateTasks = (List) + 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(5000L); + assertThat(task.getInterval()).isEqualTo(3000L); + } + + @Test + public void fixedRateTaskWithMinutesTimeUnitWithInitialDelay() { + BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class); + BeanDefinition targetDefinition = new RootBeanDefinition(FixedRateWithMinutesTimeUnitInitialDelayTestBean.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 fixedRateTasks = (List) + 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(60000L); + assertThat(task.getInterval()).isEqualTo(180000L); + } + @Test public void severalFixedRatesWithRepeatedScheduledAnnotation() { BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class); @@ -702,6 +871,22 @@ public void fixedDelay() { } } + static class FixedDelayWithSecondsTimeUnitTestBean { + + @Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS) + public void fixedDelay() { + } + + } + + static class FixedDelayWithMinutesTimeUnitTestBean { + + @Scheduled(fixedDelay = 3, timeUnit = TimeUnit.MINUTES) + public void fixedDelay() { + } + + } + static class FixedRateTestBean { @@ -709,6 +894,20 @@ static class FixedRateTestBean { public void fixedRate() { } } + static class FixedRateWithSecondsTimeUnitTestBean { + + @Scheduled(fixedRate = 5, timeUnit = TimeUnit.SECONDS) + public void fixedRate() { + } + } + + + static class FixedRateWithMinutesTimeUnitTestBean { + + @Scheduled(fixedRate = 3, timeUnit = TimeUnit.MINUTES) + public void fixedRate() { + } + } static class FixedRateWithInitialDelayTestBean { @@ -718,6 +917,20 @@ public void fixedRate() { } } + static class FixedRateWithSecondsTimeUnitInitialDelayTestBean { + + @Scheduled(fixedRate = 3, initialDelay = 5, timeUnit = TimeUnit.SECONDS) + public void fixedRate() { + } + } + + static class FixedRateWithMinutesTimeUnitInitialDelayTestBean { + + @Scheduled(fixedRate = 3, initialDelay = 1, timeUnit = TimeUnit.MINUTES) + public void fixedRate() { + } + } + static class SeveralFixedRatesWithSchedulesContainerAnnotationTestBean {