|
17 | 17 | package org.springframework.scheduling.annotation;
|
18 | 18 |
|
19 | 19 | import java.lang.reflect.Method;
|
| 20 | +import java.lang.reflect.Modifier; |
20 | 21 | import java.util.HashMap;
|
21 | 22 | import java.util.LinkedHashSet;
|
22 | 23 | import java.util.Map;
|
23 | 24 | import java.util.Set;
|
24 | 25 | import java.util.concurrent.ConcurrentHashMap;
|
25 | 26 | import java.util.concurrent.ScheduledExecutorService;
|
26 | 27 |
|
| 28 | +import org.apache.commons.logging.LogFactory; |
| 29 | + |
27 | 30 | import org.springframework.aop.support.AopUtils;
|
28 | 31 | import org.springframework.beans.factory.DisposableBean;
|
29 | 32 | import org.springframework.beans.factory.config.BeanPostProcessor;
|
@@ -188,17 +191,27 @@ private void processScheduled(Scheduled scheduled, Method method, Object bean) {
|
188 | 191 | }
|
189 | 192 | catch (NoSuchMethodException ex) {
|
190 | 193 | 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())); |
196 | 208 | }
|
197 | 209 | }
|
198 | 210 |
|
199 | 211 | Runnable runnable = new ScheduledMethodRunnable(bean, method);
|
200 | 212 | 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"; |
202 | 215 |
|
203 | 216 | // Determine initial delay
|
204 | 217 | long initialDelay = scheduled.initialDelay();
|
|
0 commit comments