Skip to content

Commit c3e57dd

Browse files
committed
AsyncAnnotationBeanPostProcessor tries to find TaskExecutor by type/name
Issue: SPR-13248
1 parent fca33f5 commit c3e57dd

File tree

3 files changed

+102
-4
lines changed

3 files changed

+102
-4
lines changed

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

Lines changed: 44 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-2015 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.
@@ -19,10 +19,15 @@
1919
import java.lang.annotation.Annotation;
2020
import java.util.concurrent.Executor;
2121

22+
import org.apache.commons.logging.Log;
23+
import org.apache.commons.logging.LogFactory;
24+
2225
import org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor;
2326
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
2427
import org.springframework.beans.factory.BeanFactory;
2528
import org.springframework.beans.factory.BeanFactoryAware;
29+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
30+
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
2631
import org.springframework.core.task.TaskExecutor;
2732
import org.springframework.util.Assert;
2833

@@ -54,10 +59,21 @@
5459
* @see Async
5560
* @see AsyncAnnotationAdvisor
5661
* @see #setBeforeExistingAdvisors
62+
* @see ScheduledAnnotationBeanPostProcessor
5763
*/
5864
@SuppressWarnings("serial")
5965
public class AsyncAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostProcessor implements BeanFactoryAware {
6066

67+
/**
68+
* The default name of the {@link TaskExecutor} bean to pick up: "taskExecutor".
69+
* <p>Note that the initial lookup happens by type; this is just the fallback
70+
* in case of multiple executor beans found in the context.
71+
*/
72+
public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME = "taskExecutor";
73+
74+
75+
protected final Log logger = LogFactory.getLog(getClass());
76+
6177
private Class<? extends Annotation> asyncAnnotationType;
6278

6379
private Executor executor;
@@ -100,9 +116,35 @@ public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler)
100116
this.exceptionHandler = exceptionHandler;
101117
}
102118

119+
103120
@Override
104121
public void setBeanFactory(BeanFactory beanFactory) {
105-
AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
122+
Executor executorToUse = this.executor;
123+
if (executorToUse == null) {
124+
try {
125+
// Search for TaskExecutor bean... not plain Executor since that would
126+
// match with ScheduledExecutorService as well, which is unusable for
127+
// our purposes here. TaskExecutor is more clearly designed for it.
128+
executorToUse = beanFactory.getBean(TaskExecutor.class);
129+
}
130+
catch (NoUniqueBeanDefinitionException ex) {
131+
try {
132+
executorToUse = beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, TaskExecutor.class);
133+
}
134+
catch (NoSuchBeanDefinitionException ex2) {
135+
throw new IllegalStateException("More than one TaskExecutor bean exists within the context, " +
136+
"and none is named 'taskExecutor'. Mark one of them as primary or name it " +
137+
"'taskExecutor' (possibly as an alias); or specify the AsyncConfigurer interface " +
138+
"and implement getAsyncExecutor() accordingly.", ex);
139+
}
140+
}
141+
catch (NoSuchBeanDefinitionException ex) {
142+
logger.debug("Could not find default TaskExecutor bean", ex);
143+
// Giving up -> falling back to default executor within the advisor...
144+
}
145+
}
146+
147+
AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(executorToUse, this.exceptionHandler);
106148
if (this.asyncAnnotationType != null) {
107149
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
108150
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,14 @@
8282
* @see SchedulingConfigurer
8383
* @see org.springframework.scheduling.TaskScheduler
8484
* @see org.springframework.scheduling.config.ScheduledTaskRegistrar
85+
* @see AsyncAnnotationBeanPostProcessor
8586
*/
8687
public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered,
8788
EmbeddedValueResolverAware, BeanFactoryAware, ApplicationContextAware,
8889
SmartInitializingSingleton, ApplicationListener<ContextRefreshedEvent>, DisposableBean {
8990

9091
/**
91-
* The default name of the TaskScheduler bean to pick up: "taskScheduler".
92+
* The default name of the {@link TaskScheduler} bean to pick up: "taskScheduler".
9293
* <p>Note that the initial lookup happens by type; this is just the fallback
9394
* in case of multiple scheduler beans found in the context.
9495
*/

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

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,60 @@ public void threadNamePrefix() {
8787
context.close();
8888
}
8989

90+
@Test
91+
public void taskExecutorByBeanType() {
92+
StaticApplicationContext context = new StaticApplicationContext();
93+
94+
BeanDefinition processorDefinition = new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class);
95+
context.registerBeanDefinition("postProcessor", processorDefinition);
96+
97+
BeanDefinition executorDefinition = new RootBeanDefinition(ThreadPoolTaskExecutor.class);
98+
executorDefinition.getPropertyValues().add("threadNamePrefix", "testExecutor");
99+
context.registerBeanDefinition("myExecutor", executorDefinition);
100+
101+
BeanDefinition targetDefinition =
102+
new RootBeanDefinition(AsyncAnnotationBeanPostProcessorTests.TestBean.class);
103+
context.registerBeanDefinition("target", targetDefinition);
104+
105+
context.refresh();
106+
107+
ITestBean testBean = context.getBean("target", ITestBean.class);
108+
testBean.test();
109+
testBean.await(3000);
110+
Thread asyncThread = testBean.getThread();
111+
assertTrue(asyncThread.getName().startsWith("testExecutor"));
112+
context.close();
113+
}
114+
115+
@Test
116+
public void taskExecutorByBeanName() {
117+
StaticApplicationContext context = new StaticApplicationContext();
118+
119+
BeanDefinition processorDefinition = new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class);
120+
context.registerBeanDefinition("postProcessor", processorDefinition);
121+
122+
BeanDefinition executorDefinition = new RootBeanDefinition(ThreadPoolTaskExecutor.class);
123+
executorDefinition.getPropertyValues().add("threadNamePrefix", "testExecutor");
124+
context.registerBeanDefinition("myExecutor", executorDefinition);
125+
126+
BeanDefinition executorDefinition2 = new RootBeanDefinition(ThreadPoolTaskExecutor.class);
127+
executorDefinition2.getPropertyValues().add("threadNamePrefix", "testExecutor2");
128+
context.registerBeanDefinition("taskExecutor", executorDefinition2);
129+
130+
BeanDefinition targetDefinition =
131+
new RootBeanDefinition(AsyncAnnotationBeanPostProcessorTests.TestBean.class);
132+
context.registerBeanDefinition("target", targetDefinition);
133+
134+
context.refresh();
135+
136+
ITestBean testBean = context.getBean("target", ITestBean.class);
137+
testBean.test();
138+
testBean.await(3000);
139+
Thread asyncThread = testBean.getThread();
140+
assertTrue(asyncThread.getName().startsWith("testExecutor2"));
141+
context.close();
142+
}
143+
90144
@Test
91145
public void configuredThroughNamespace() {
92146
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
@@ -264,6 +318,7 @@ public void await(long timeout) {
264318
}
265319
}
266320

321+
267322
private static class DirectExecutor implements Executor {
268323

269324
@Override
@@ -272,6 +327,7 @@ public void execute(Runnable r) {
272327
}
273328
}
274329

330+
275331
@Configuration
276332
@EnableAsync
277333
static class ConfigWithExceptionHandler extends AsyncConfigurerSupport {
@@ -292,5 +348,4 @@ public TestableAsyncUncaughtExceptionHandler exceptionHandler() {
292348
}
293349
}
294350

295-
296351
}

0 commit comments

Comments
 (0)