diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java index 7122889c3f..ed5fdbeff2 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,10 +19,15 @@ package org.springframework.scheduling.annotation; import java.lang.annotation.Annotation; import java.util.concurrent.Executor; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.core.task.TaskExecutor; import org.springframework.util.Assert; @@ -54,10 +59,21 @@ import org.springframework.util.Assert; * @see Async * @see AsyncAnnotationAdvisor * @see #setBeforeExistingAdvisors + * @see ScheduledAnnotationBeanPostProcessor */ @SuppressWarnings("serial") public class AsyncAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostProcessor implements BeanFactoryAware { + /** + * The default name of the {@link TaskExecutor} bean to pick up: "taskExecutor". + *
Note that the initial lookup happens by type; this is just the fallback
+ * in case of multiple executor beans found in the context.
+ */
+ public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME = "taskExecutor";
+
+
+ protected final Log logger = LogFactory.getLog(getClass());
+
private Class extends Annotation> asyncAnnotationType;
private Executor executor;
@@ -100,9 +116,35 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostPr
this.exceptionHandler = exceptionHandler;
}
+
@Override
public void setBeanFactory(BeanFactory beanFactory) {
- AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
+ Executor executorToUse = this.executor;
+ if (executorToUse == null) {
+ try {
+ // Search for TaskExecutor bean... not plain Executor since that would
+ // match with ScheduledExecutorService as well, which is unusable for
+ // our purposes here. TaskExecutor is more clearly designed for it.
+ executorToUse = beanFactory.getBean(TaskExecutor.class);
+ }
+ catch (NoUniqueBeanDefinitionException ex) {
+ try {
+ executorToUse = beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, TaskExecutor.class);
+ }
+ catch (NoSuchBeanDefinitionException ex2) {
+ throw new IllegalStateException("More than one TaskExecutor bean exists within the context, " +
+ "and none is named 'taskExecutor'. Mark one of them as primary or name it " +
+ "'taskExecutor' (possibly as an alias); or specify the AsyncConfigurer interface " +
+ "and implement getAsyncExecutor() accordingly.", ex);
+ }
+ }
+ catch (NoSuchBeanDefinitionException ex) {
+ logger.debug("Could not find default TaskExecutor bean", ex);
+ // Giving up -> falling back to default executor within the advisor...
+ }
+ }
+
+ AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(executorToUse, this.exceptionHandler);
if (this.asyncAnnotationType != null) {
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
}
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java
index 0375ae0ac4..560f214d60 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java
@@ -82,13 +82,14 @@ import org.springframework.util.StringValueResolver;
* @see SchedulingConfigurer
* @see org.springframework.scheduling.TaskScheduler
* @see org.springframework.scheduling.config.ScheduledTaskRegistrar
+ * @see AsyncAnnotationBeanPostProcessor
*/
public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered,
EmbeddedValueResolverAware, BeanFactoryAware, ApplicationContextAware,
SmartInitializingSingleton, ApplicationListener Note that the initial lookup happens by type; this is just the fallback
* in case of multiple scheduler beans found in the context.
*/
diff --git a/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java b/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java
index 75ebf30697..e9ff9524e4 100644
--- a/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java
+++ b/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java
@@ -87,6 +87,60 @@ public class AsyncAnnotationBeanPostProcessorTests {
context.close();
}
+ @Test
+ public void taskExecutorByBeanType() {
+ StaticApplicationContext context = new StaticApplicationContext();
+
+ BeanDefinition processorDefinition = new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class);
+ context.registerBeanDefinition("postProcessor", processorDefinition);
+
+ BeanDefinition executorDefinition = new RootBeanDefinition(ThreadPoolTaskExecutor.class);
+ executorDefinition.getPropertyValues().add("threadNamePrefix", "testExecutor");
+ context.registerBeanDefinition("myExecutor", executorDefinition);
+
+ BeanDefinition targetDefinition =
+ new RootBeanDefinition(AsyncAnnotationBeanPostProcessorTests.TestBean.class);
+ context.registerBeanDefinition("target", targetDefinition);
+
+ context.refresh();
+
+ ITestBean testBean = context.getBean("target", ITestBean.class);
+ testBean.test();
+ testBean.await(3000);
+ Thread asyncThread = testBean.getThread();
+ assertTrue(asyncThread.getName().startsWith("testExecutor"));
+ context.close();
+ }
+
+ @Test
+ public void taskExecutorByBeanName() {
+ StaticApplicationContext context = new StaticApplicationContext();
+
+ BeanDefinition processorDefinition = new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class);
+ context.registerBeanDefinition("postProcessor", processorDefinition);
+
+ BeanDefinition executorDefinition = new RootBeanDefinition(ThreadPoolTaskExecutor.class);
+ executorDefinition.getPropertyValues().add("threadNamePrefix", "testExecutor");
+ context.registerBeanDefinition("myExecutor", executorDefinition);
+
+ BeanDefinition executorDefinition2 = new RootBeanDefinition(ThreadPoolTaskExecutor.class);
+ executorDefinition2.getPropertyValues().add("threadNamePrefix", "testExecutor2");
+ context.registerBeanDefinition("taskExecutor", executorDefinition2);
+
+ BeanDefinition targetDefinition =
+ new RootBeanDefinition(AsyncAnnotationBeanPostProcessorTests.TestBean.class);
+ context.registerBeanDefinition("target", targetDefinition);
+
+ context.refresh();
+
+ ITestBean testBean = context.getBean("target", ITestBean.class);
+ testBean.test();
+ testBean.await(3000);
+ Thread asyncThread = testBean.getThread();
+ assertTrue(asyncThread.getName().startsWith("testExecutor2"));
+ context.close();
+ }
+
@Test
public void configuredThroughNamespace() {
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
@@ -264,6 +318,7 @@ public class AsyncAnnotationBeanPostProcessorTests {
}
}
+
private static class DirectExecutor implements Executor {
@Override
@@ -272,6 +327,7 @@ public class AsyncAnnotationBeanPostProcessorTests {
}
}
+
@Configuration
@EnableAsync
static class ConfigWithExceptionHandler extends AsyncConfigurerSupport {
@@ -292,5 +348,4 @@ public class AsyncAnnotationBeanPostProcessorTests {
}
}
-
}