AsyncAnnotationBeanPostProcessor tries to find TaskExecutor by type/name
Issue: SPR-13248
This commit is contained in:
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.lang.annotation.Annotation;
|
||||||
import java.util.concurrent.Executor;
|
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.framework.AbstractAdvisingBeanPostProcessor;
|
||||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.BeanFactoryAware;
|
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.core.task.TaskExecutor;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@@ -54,10 +59,21 @@ import org.springframework.util.Assert;
|
|||||||
* @see Async
|
* @see Async
|
||||||
* @see AsyncAnnotationAdvisor
|
* @see AsyncAnnotationAdvisor
|
||||||
* @see #setBeforeExistingAdvisors
|
* @see #setBeforeExistingAdvisors
|
||||||
|
* @see ScheduledAnnotationBeanPostProcessor
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class AsyncAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostProcessor implements BeanFactoryAware {
|
public class AsyncAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostProcessor implements BeanFactoryAware {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default name of the {@link TaskExecutor} bean to pick up: "taskExecutor".
|
||||||
|
* <p>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 Class<? extends Annotation> asyncAnnotationType;
|
||||||
|
|
||||||
private Executor executor;
|
private Executor executor;
|
||||||
@@ -100,9 +116,35 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostPr
|
|||||||
this.exceptionHandler = exceptionHandler;
|
this.exceptionHandler = exceptionHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBeanFactory(BeanFactory beanFactory) {
|
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) {
|
if (this.asyncAnnotationType != null) {
|
||||||
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
|
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,13 +82,14 @@ import org.springframework.util.StringValueResolver;
|
|||||||
* @see SchedulingConfigurer
|
* @see SchedulingConfigurer
|
||||||
* @see org.springframework.scheduling.TaskScheduler
|
* @see org.springframework.scheduling.TaskScheduler
|
||||||
* @see org.springframework.scheduling.config.ScheduledTaskRegistrar
|
* @see org.springframework.scheduling.config.ScheduledTaskRegistrar
|
||||||
|
* @see AsyncAnnotationBeanPostProcessor
|
||||||
*/
|
*/
|
||||||
public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered,
|
public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered,
|
||||||
EmbeddedValueResolverAware, BeanFactoryAware, ApplicationContextAware,
|
EmbeddedValueResolverAware, BeanFactoryAware, ApplicationContextAware,
|
||||||
SmartInitializingSingleton, ApplicationListener<ContextRefreshedEvent>, DisposableBean {
|
SmartInitializingSingleton, ApplicationListener<ContextRefreshedEvent>, DisposableBean {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default name of the TaskScheduler bean to pick up: "taskScheduler".
|
* The default name of the {@link TaskScheduler} bean to pick up: "taskScheduler".
|
||||||
* <p>Note that the initial lookup happens by type; this is just the fallback
|
* <p>Note that the initial lookup happens by type; this is just the fallback
|
||||||
* in case of multiple scheduler beans found in the context.
|
* in case of multiple scheduler beans found in the context.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -87,6 +87,60 @@ public class AsyncAnnotationBeanPostProcessorTests {
|
|||||||
context.close();
|
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
|
@Test
|
||||||
public void configuredThroughNamespace() {
|
public void configuredThroughNamespace() {
|
||||||
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
|
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
|
||||||
@@ -264,6 +318,7 @@ public class AsyncAnnotationBeanPostProcessorTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class DirectExecutor implements Executor {
|
private static class DirectExecutor implements Executor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -272,6 +327,7 @@ public class AsyncAnnotationBeanPostProcessorTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
static class ConfigWithExceptionHandler extends AsyncConfigurerSupport {
|
static class ConfigWithExceptionHandler extends AsyncConfigurerSupport {
|
||||||
@@ -292,5 +348,4 @@ public class AsyncAnnotationBeanPostProcessorTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user