Changed the way the default executor is picked; fixes gh-1212
This commit is contained in:
@@ -20,8 +20,13 @@ import java.util.concurrent.Executor;
|
||||
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.interceptor.AsyncExecutionAspectSupport;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
@@ -32,6 +37,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
|
||||
|
||||
@@ -70,12 +76,71 @@ public class AsyncDefaultAutoConfiguration {
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static class DefaultAsyncConfigurerSupport extends AsyncConfigurerSupport {
|
||||
|
||||
private static final Log log = LogFactory
|
||||
.getLog(DefaultAsyncConfigurerSupport.class);
|
||||
|
||||
@Autowired
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public Executor getAsyncExecutor() {
|
||||
return new LazyTraceExecutor(this.beanFactory, new SimpleAsyncTaskExecutor());
|
||||
Executor delegate = getDefaultExecutor();
|
||||
return new LazyTraceExecutor(this.beanFactory, delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve or build a default executor for this advice instance. An executor
|
||||
* returned from here will be cached for further use.
|
||||
* <p>
|
||||
* The default implementation searches for a unique {@link TaskExecutor} bean in
|
||||
* the context, or for an {@link Executor} bean named "taskExecutor" otherwise. If
|
||||
* neither of the two is resolvable, this implementation will return {@code null}.
|
||||
* @return the default executor, or {@code null} if none available
|
||||
* @see AsyncExecutionAspectSupport#getDefaultExecutor(org.springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
private Executor getDefaultExecutor() {
|
||||
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.
|
||||
return this.beanFactory.getBean(TaskExecutor.class);
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
log.debug("Could not find unique TaskExecutor bean", ex);
|
||||
try {
|
||||
return this.beanFactory.getBean(
|
||||
AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME,
|
||||
Executor.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex2) {
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info(
|
||||
"More than one TaskExecutor bean found within the context, and none is named "
|
||||
+ "'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly "
|
||||
+ "as an alias) in order to use it for async processing: "
|
||||
+ ex.getBeanNamesFound());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
log.debug("Could not find default TaskExecutor bean", ex);
|
||||
try {
|
||||
return this.beanFactory.getBean(
|
||||
AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME,
|
||||
Executor.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex2) {
|
||||
log.info("No task executor bean found for async processing: "
|
||||
+ "no bean of type TaskExecutor and no bean named 'taskExecutor' either");
|
||||
}
|
||||
// Giving up -> either using local default executor or none at all...
|
||||
}
|
||||
// backward compatibility
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info(
|
||||
"For backward compatibility, will fallback to the default, SimpleAsyncTaskExecutor implementation");
|
||||
}
|
||||
return new SimpleAsyncTaskExecutor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.async.issues.issue1212;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.aop.interceptor.AsyncExecutionAspectSupport;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Bertrand Renuart
|
||||
*/
|
||||
public class GH1212Tests {
|
||||
|
||||
@Test
|
||||
public void defaultTaskExecutor() throws Exception {
|
||||
try (ConfigurableApplicationContext ctx = new SpringApplicationBuilder(App.class,
|
||||
DefaultTaskExecutorConfig.class).run()) {
|
||||
String asyncThreadName = getAsyncThreadName(ctx);
|
||||
assertThat(asyncThreadName).startsWith("defaultTaskExecutor");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleTaskExecutor() throws Exception {
|
||||
try (ConfigurableApplicationContext ctx = new SpringApplicationBuilder(App.class,
|
||||
SingleTaskExecutorConfig.class).run()) {
|
||||
String asyncThreadName = getAsyncThreadName(ctx);
|
||||
assertThat(asyncThreadName).startsWith("singleTaskExecutor");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleTaskExecutors() throws Exception {
|
||||
try (ConfigurableApplicationContext ctx = new SpringApplicationBuilder(App.class,
|
||||
MultipleTaskExecutorConfig.class).run()) {
|
||||
String asyncThreadName = getAsyncThreadName(ctx);
|
||||
assertThat(asyncThreadName).doesNotStartWith("multipleTaskExecutor");
|
||||
assertThat(asyncThreadName).startsWith("SimpleAsyncTaskExecutor"); // <--
|
||||
// comes
|
||||
// from
|
||||
// Sleuth's
|
||||
// own
|
||||
// AsyncConfigurer
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customAsyncConfigurer() throws Exception {
|
||||
try (ConfigurableApplicationContext ctx = new SpringApplicationBuilder(App.class,
|
||||
CustomAsyncConfigurerConfig.class).run()) {
|
||||
String asyncThreadName = getAsyncThreadName(ctx);
|
||||
assertThat(asyncThreadName).startsWith("customAsyncConfigurer");
|
||||
}
|
||||
}
|
||||
|
||||
private String getAsyncThreadName(ApplicationContext ctx) throws Exception {
|
||||
return ctx.getBean(AsyncComponent.class).asyncMethod().get();
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
@EnableAsync
|
||||
static class App {
|
||||
|
||||
@Bean
|
||||
AsyncComponent asyncComponent() {
|
||||
return new AsyncComponent();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class AsyncComponent {
|
||||
|
||||
@Async
|
||||
public CompletableFuture<String> asyncMethod() {
|
||||
LoggerFactory.getLogger("test").info("asyncMethod invoked");
|
||||
return CompletableFuture.completedFuture(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Configuration with a single Executor named `taskExecutor`
|
||||
*/
|
||||
@Configuration
|
||||
static class DefaultTaskExecutorConfig {
|
||||
|
||||
@Bean(name = AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME)
|
||||
public Executor taskExecutor() {
|
||||
return new SimpleAsyncTaskExecutor("defaultTaskExecutor");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Configuration with a single TaskExecutor
|
||||
*/
|
||||
@Configuration
|
||||
static class SingleTaskExecutorConfig {
|
||||
|
||||
@Bean
|
||||
// there's the task
|
||||
@Primary
|
||||
public TaskExecutor singleTaskExecutor() {
|
||||
return new SimpleAsyncTaskExecutor("singleTaskExecutor");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Configuration with a multiple TaskExecutors --> Spring won't pick any unless one
|
||||
* is @Primary
|
||||
*/
|
||||
@Configuration
|
||||
static class MultipleTaskExecutorConfig {
|
||||
|
||||
@Bean
|
||||
public TaskExecutor multipleTaskExecutor1() {
|
||||
return new SimpleAsyncTaskExecutor("multipleTaskExecutor1");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskExecutor multipleTaskExecutor2() {
|
||||
return new SimpleAsyncTaskExecutor("multipleTaskExecutor2");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Configuration where a custom AsyncConfigurer is provided
|
||||
*/
|
||||
@Configuration
|
||||
static class CustomAsyncConfigurerConfig {
|
||||
|
||||
@Bean
|
||||
public AsyncConfigurer customAsyncConfigurer() {
|
||||
return new AsyncConfigurerSupport() {
|
||||
@Override
|
||||
public Executor getAsyncExecutor() {
|
||||
return new SimpleAsyncTaskExecutor("customAsyncConfigurer");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user