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 4b8328fadd..7a9195eeab 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 @@ -136,9 +136,12 @@ public @interface Scheduled { * last invocation and the start of the next. *
The time unit is milliseconds by default but can be overridden via * {@link #timeUnit}. + *
This attribute variant supports Spring-style "${...}" placeholders + * as well as SpEL expressions. * @return the delay as a String value — for example, a placeholder * or a {@link java.time.Duration#parse java.time.Duration} compliant value * @since 3.2.2 + * @see #fixedDelay() */ String fixedDelayString() default ""; @@ -154,9 +157,12 @@ public @interface Scheduled { * Execute the annotated method with a fixed period between invocations. *
The time unit is milliseconds by default but can be overridden via * {@link #timeUnit}. + *
This attribute variant supports Spring-style "${...}" placeholders + * as well as SpEL expressions. * @return the period as a String value — for example, a placeholder * or a {@link java.time.Duration#parse java.time.Duration} compliant value * @since 3.2.2 + * @see #fixedRate() */ String fixedRateString() default ""; @@ -175,9 +181,12 @@ public @interface Scheduled { * {@link #fixedRate} or {@link #fixedDelay} task. *
The time unit is milliseconds by default but can be overridden via * {@link #timeUnit}. + *
This attribute variant supports Spring-style "${...}" placeholders + * as well as SpEL expressions. * @return the initial delay as a String value — for example, a placeholder * or a {@link java.time.Duration#parse java.time.Duration} compliant value * @since 3.2.2 + * @see #initialDelay() */ String initialDelayString() default ""; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java index 46c3ab1543..5086cf055e 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java @@ -780,11 +780,24 @@ public class MvcUriComponentsBuilder { else if (controllerType.isInterface()) { ClassLoader classLoader = controllerType.getClassLoader(); - if (classLoader == null || classLoader.getParent() == null) { - // JDK interface type from bootstrap loader or platform loader -> - // use higher-level loader which can see Spring infrastructure classes + if (classLoader == null) { + // JDK bootstrap loader -> use MethodInvocationInfo ClassLoader instead. classLoader = MethodInvocationInfo.class.getClassLoader(); } + else if (classLoader.getParent() == null) { + // Potentially the JDK platform loader on JDK 9+ + ClassLoader miiClassLoader = MethodInvocationInfo.class.getClassLoader(); + ClassLoader miiParent = miiClassLoader.getParent(); + while (miiParent != null) { + if (classLoader == miiParent) { + // Suggested ClassLoader is ancestor of MethodInvocationInfo ClassLoader + // -> use MethodInvocationInfo ClassLoader itself instead. + classLoader = miiClassLoader; + break; + } + miiParent = miiParent.getParent(); + } + } Class>[] ifcs = new Class>[] {controllerType, MethodInvocationInfo.class}; return (T) Proxy.newProxyInstance(classLoader, ifcs, interceptor); }