Skip to content

Commit b51685b

Browse files
committed
Post-processors consistently ignore ScopedObject/AopInfrastructureBean
Issue: SPR-17166
1 parent 687d350 commit b51685b

File tree

5 files changed

+82
-47
lines changed

5 files changed

+82
-47
lines changed

spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -90,7 +90,7 @@ public Object postProcessAfterInitialization(Object bean, String beanName) {
9090
return proxyFactory.getProxy(getProxyClassLoader());
9191
}
9292

93-
// No async proxy needed.
93+
// No proxy needed.
9494
return bean;
9595
}
9696

@@ -155,7 +155,7 @@ protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
155155
* Subclasses may choose to implement this: for example,
156156
* to change the interfaces exposed.
157157
* <p>The default implementation is empty.
158-
* @param proxyFactory ProxyFactory that is already configured with
158+
* @param proxyFactory the ProxyFactory that is already configured with
159159
* target, advisor and interfaces and will be used to create the proxy
160160
* immediately after this method returns
161161
* @since 4.2.3

spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyFactoryBean.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -50,7 +50,8 @@
5050
* @see #setProxyTargetClass
5151
*/
5252
@SuppressWarnings("serial")
53-
public class ScopedProxyFactoryBean extends ProxyConfig implements FactoryBean<Object>, BeanFactoryAware {
53+
public class ScopedProxyFactoryBean extends ProxyConfig
54+
implements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean {
5455

5556
/** The TargetSource that manages scoping */
5657
private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.apache.commons.logging.Log;
3333
import org.apache.commons.logging.LogFactory;
3434

35+
import org.springframework.aop.framework.AopInfrastructureBean;
3536
import org.springframework.aop.framework.AopProxyUtils;
3637
import org.springframework.aop.support.AopUtils;
3738
import org.springframework.beans.factory.BeanFactory;
@@ -306,7 +307,12 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) {
306307
}
307308

308309
@Override
309-
public Object postProcessAfterInitialization(final Object bean, String beanName) {
310+
public Object postProcessAfterInitialization(Object bean, String beanName) {
311+
if (bean instanceof AopInfrastructureBean) {
312+
// Ignore AOP infrastructure such as scoped proxies.
313+
return bean;
314+
}
315+
310316
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
311317
if (!this.nonAnnotatedClasses.contains(targetClass)) {
312318
Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
@@ -341,6 +347,12 @@ public Set<Scheduled> inspect(Method method) {
341347
return bean;
342348
}
343349

350+
/**
351+
* Process the given {@code @Scheduled} method declaration on the given bean.
352+
* @param scheduled the @Scheduled annotation
353+
* @param method the method that the annotation has been declared on
354+
* @param bean the target bean instance
355+
*/
344356
protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
345357
try {
346358
Assert.isTrue(method.getParameterTypes().length == 0,

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

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,8 +27,6 @@
2727
import org.springframework.context.annotation.Bean;
2828
import org.springframework.context.annotation.Configuration;
2929
import org.springframework.scheduling.TaskScheduler;
30-
import org.springframework.scheduling.Trigger;
31-
import org.springframework.scheduling.TriggerContext;
3230
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
3331
import org.springframework.scheduling.config.IntervalTask;
3432
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
@@ -447,19 +445,8 @@ public AtomicInteger counter() {
447445
public TaskScheduler scheduler() {
448446
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
449447
scheduler.initialize();
450-
scheduler.schedule(
451-
new Runnable() {
452-
@Override
453-
public void run() {
454-
counter().incrementAndGet();
455-
}
456-
},
457-
new Trigger() {
458-
@Override
459-
public Date nextExecutionTime(TriggerContext triggerContext) {
460-
return new Date(new Date().getTime()+10);
461-
}
462-
});
448+
scheduler.schedule(() -> counter().incrementAndGet(),
449+
triggerContext -> new Date(new Date().getTime()+10));
463450
return scheduler;
464451
}
465452
}

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

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,15 @@
3434
import org.junit.Test;
3535

3636
import org.springframework.aop.framework.ProxyFactory;
37+
import org.springframework.aop.scope.ScopedProxyUtils;
3738
import org.springframework.beans.DirectFieldAccessor;
3839
import org.springframework.beans.factory.BeanCreationException;
3940
import org.springframework.beans.factory.config.BeanDefinition;
4041
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
4142
import org.springframework.beans.factory.support.RootBeanDefinition;
43+
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
44+
import org.springframework.context.annotation.Scope;
45+
import org.springframework.context.annotation.ScopedProxyMode;
4246
import org.springframework.context.support.StaticApplicationContext;
4347
import org.springframework.core.annotation.AliasFor;
4448
import org.springframework.scheduling.Trigger;
@@ -49,8 +53,7 @@
4953
import org.springframework.scheduling.support.CronTrigger;
5054
import org.springframework.scheduling.support.ScheduledMethodRunnable;
5155
import org.springframework.scheduling.support.SimpleTriggerContext;
52-
import org.springframework.tests.Assume;
53-
import org.springframework.tests.TestGroup;
56+
import org.springframework.stereotype.Component;
5457
import org.springframework.validation.annotation.Validated;
5558
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
5659

@@ -222,9 +225,7 @@ private void severalFixedRates(StaticApplicationContext context,
222225
}
223226

224227
@Test
225-
public void cronTask() throws InterruptedException {
226-
Assume.group(TestGroup.LONG_RUNNING);
227-
228+
public void cronTask() {
228229
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
229230
BeanDefinition targetDefinition = new RootBeanDefinition(CronTestBean.class);
230231
context.registerBeanDefinition("postProcessor", processorDefinition);
@@ -246,13 +247,10 @@ public void cronTask() throws InterruptedException {
246247
assertEquals(target, targetObject);
247248
assertEquals("cron", targetMethod.getName());
248249
assertEquals("*/7 * * * * ?", task.getExpression());
249-
Thread.sleep(10000);
250250
}
251251

252252
@Test
253-
public void cronTaskWithZone() throws InterruptedException {
254-
Assume.group(TestGroup.LONG_RUNNING);
255-
253+
public void cronTaskWithZone() {
256254
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
257255
BeanDefinition targetDefinition = new RootBeanDefinition(CronWithTimezoneTestBean.class);
258256
context.registerBeanDefinition("postProcessor", processorDefinition);
@@ -280,34 +278,30 @@ public void cronTaskWithZone() throws InterruptedException {
280278
CronTrigger cronTrigger = (CronTrigger) trigger;
281279
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+10"));
282280
cal.clear();
283-
cal.set(2013, 3, 15, 4, 0); // 15-04-2013 4:00 GMT+10
281+
cal.set(2013, 3, 15, 4, 0); // 15-04-2013 4:00 GMT+10
284282
Date lastScheduledExecutionTime = cal.getTime();
285283
Date lastActualExecutionTime = cal.getTime();
286-
cal.add(Calendar.MINUTE, 30); // 4:30
284+
cal.add(Calendar.MINUTE, 30); // 4:30
287285
Date lastCompletionTime = cal.getTime();
288286
TriggerContext triggerContext = new SimpleTriggerContext(
289287
lastScheduledExecutionTime, lastActualExecutionTime, lastCompletionTime);
290288
cal.add(Calendar.MINUTE, 30);
291-
cal.add(Calendar.HOUR_OF_DAY, 1); // 6:00
289+
cal.add(Calendar.HOUR_OF_DAY, 1); // 6:00
292290
Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext);
293-
assertEquals(cal.getTime(), nextExecutionTime); // assert that 6:00 is next execution time
294-
Thread.sleep(10000);
291+
assertEquals(cal.getTime(), nextExecutionTime); // assert that 6:00 is next execution time
295292
}
296293

297294
@Test(expected = BeanCreationException.class)
298-
public void cronTaskWithInvalidZone() throws InterruptedException {
299-
Assume.group(TestGroup.LONG_RUNNING);
300-
295+
public void cronTaskWithInvalidZone() {
301296
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
302297
BeanDefinition targetDefinition = new RootBeanDefinition(CronWithInvalidTimezoneTestBean.class);
303298
context.registerBeanDefinition("postProcessor", processorDefinition);
304299
context.registerBeanDefinition("target", targetDefinition);
305300
context.refresh();
306-
Thread.sleep(10000);
307301
}
308302

309303
@Test(expected = BeanCreationException.class)
310-
public void cronTaskWithMethodValidation() throws InterruptedException {
304+
public void cronTaskWithMethodValidation() {
311305
BeanDefinition validationDefinition = new RootBeanDefinition(MethodValidationPostProcessor.class);
312306
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
313307
BeanDefinition targetDefinition = new RootBeanDefinition(CronTestBean.class);
@@ -317,6 +311,29 @@ public void cronTaskWithMethodValidation() throws InterruptedException {
317311
context.refresh();
318312
}
319313

314+
@Test
315+
public void cronTaskWithScopedProxy() {
316+
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
317+
context.registerBeanDefinition("postProcessor", processorDefinition);
318+
new AnnotatedBeanDefinitionReader(context).register(ProxiedCronTestBean.class, ProxiedCronTestBeanDependent.class);
319+
context.refresh();
320+
321+
Object postProcessor = context.getBean("postProcessor");
322+
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
323+
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
324+
@SuppressWarnings("unchecked")
325+
List<CronTask> cronTasks = (List<CronTask>)
326+
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
327+
assertEquals(1, cronTasks.size());
328+
CronTask task = cronTasks.get(0);
329+
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
330+
Object targetObject = runnable.getTarget();
331+
Method targetMethod = runnable.getMethod();
332+
assertEquals(context.getBean(ScopedProxyUtils.getTargetBeanName("target")), targetObject);
333+
assertEquals("cron", targetMethod.getName());
334+
assertEquals("*/7 * * * * ?", task.getExpression());
335+
}
336+
320337
@Test
321338
public void metaAnnotationWithFixedRate() {
322339
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
@@ -352,11 +369,11 @@ public void composedAnnotationWithInitialDelayAndFixedRate() {
352369

353370
Object postProcessor = context.getBean("postProcessor");
354371
Object target = context.getBean("target");
355-
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar) new DirectFieldAccessor(
356-
postProcessor).getPropertyValue("registrar");
372+
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
373+
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
357374
@SuppressWarnings("unchecked")
358-
List<IntervalTask> fixedRateTasks = (List<IntervalTask>) new DirectFieldAccessor(registrar).getPropertyValue(
359-
"fixedRateTasks");
375+
List<IntervalTask> fixedRateTasks = (List<IntervalTask>)
376+
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
360377
assertEquals(1, fixedRateTasks.size());
361378
IntervalTask task = fixedRateTasks.get(0);
362379
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
@@ -627,8 +644,7 @@ public void fixedRate() {
627644

628645
static class SeveralFixedRatesWithSchedulesContainerAnnotationTestBean {
629646

630-
@Schedules({ @Scheduled(fixedRate = 4000),
631-
@Scheduled(fixedRate = 4000, initialDelay = 2000) })
647+
@Schedules({@Scheduled(fixedRate = 4000), @Scheduled(fixedRate = 4000, initialDelay = 2000)})
632648
public void fixedRate() {
633649
}
634650
}
@@ -705,6 +721,24 @@ public void cron() throws IOException {
705721
}
706722

707723

724+
@Component("target")
725+
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
726+
static class ProxiedCronTestBean {
727+
728+
@Scheduled(cron = "*/7 * * * * ?")
729+
public void cron() throws IOException {
730+
throw new IOException("no no no");
731+
}
732+
}
733+
734+
735+
static class ProxiedCronTestBeanDependent {
736+
737+
public ProxiedCronTestBeanDependent(ProxiedCronTestBean testBean) {
738+
}
739+
}
740+
741+
708742
static class NonVoidReturnTypeTestBean {
709743

710744
@Scheduled(cron = "0 0 9-17 * * MON-FRI")
@@ -785,6 +819,7 @@ public void generateReport() {
785819
}
786820
}
787821

822+
788823
static class PropertyPlaceholderWithCronTestBean {
789824

790825
@Scheduled(cron = "${schedules.businessHours}")

0 commit comments

Comments
 (0)