Skip to content

Commit 473d973

Browse files
committed
Explicitly detect (and log) private @scheduled methods on CGLIB proxies
Issue: SPR-12308 (cherry picked from commit 47ed4d6)
1 parent 76ab5b9 commit 473d973

File tree

1 file changed

+19
-6
lines changed

1 file changed

+19
-6
lines changed

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

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@
1717
package org.springframework.scheduling.annotation;
1818

1919
import java.lang.reflect.Method;
20+
import java.lang.reflect.Modifier;
2021
import java.util.HashMap;
2122
import java.util.LinkedHashSet;
2223
import java.util.Map;
2324
import java.util.Set;
2425
import java.util.concurrent.ConcurrentHashMap;
2526
import java.util.concurrent.ScheduledExecutorService;
2627

28+
import org.apache.commons.logging.LogFactory;
29+
2730
import org.springframework.aop.support.AopUtils;
2831
import org.springframework.beans.factory.DisposableBean;
2932
import org.springframework.beans.factory.config.BeanPostProcessor;
@@ -188,17 +191,27 @@ private void processScheduled(Scheduled scheduled, Method method, Object bean) {
188191
}
189192
catch (NoSuchMethodException ex) {
190193
throw new IllegalStateException(String.format(
191-
"@Scheduled method '%s' found on bean target class '%s', " +
192-
"but not found in any interface(s) for bean JDK proxy. Either " +
193-
"pull the method up to an interface or switch to subclass (CGLIB) " +
194-
"proxies by setting proxy-target-class/proxyTargetClass " +
195-
"attribute to 'true'", method.getName(), method.getDeclaringClass().getSimpleName()));
194+
"@Scheduled method '%s' found on bean target class '%s' but not " +
195+
"found in any interface(s) for a dynamic proxy. Either pull the " +
196+
"method up to a declared interface or switch to subclass (CGLIB) " +
197+
"proxies by setting proxy-target-class/proxyTargetClass to 'true'",
198+
method.getName(), method.getDeclaringClass().getSimpleName()));
199+
}
200+
}
201+
else if (AopUtils.isCglibProxy(bean)) {
202+
// Common problem: private methods end up in the proxy instance, not getting delegated.
203+
if (Modifier.isPrivate(method.getModifiers())) {
204+
LogFactory.getLog(ScheduledAnnotationBeanPostProcessor.class).warn(String.format(
205+
"@Scheduled method '%s' found on CGLIB proxy for target class '%s' but cannot " +
206+
"be delegated to target bean. Switch its visibility to package or protected.",
207+
method.getName(), method.getDeclaringClass().getSimpleName()));
196208
}
197209
}
198210

199211
Runnable runnable = new ScheduledMethodRunnable(bean, method);
200212
boolean processedSchedule = false;
201-
String errorMessage = "Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";
213+
String errorMessage =
214+
"Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";
202215

203216
// Determine initial delay
204217
long initialDelay = scheduled.initialDelay();

0 commit comments

Comments
 (0)