Allow @Async qualifier to be declared with a placeholder or SpEL expression
Closes gh-27818
This commit is contained in:
parent
6a73d2655f
commit
01214b3473
|
|
@ -34,6 +34,8 @@ import org.springframework.beans.factory.BeanFactoryAware;
|
|||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.EmbeddedValueResolver;
|
||||
import org.springframework.core.task.AsyncListenableTaskExecutor;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
|
|
@ -41,6 +43,7 @@ import org.springframework.core.task.support.TaskExecutorAdapter;
|
|||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
||||
|
|
@ -208,6 +211,11 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
|||
throw new IllegalStateException("BeanFactory must be set on " + getClass().getSimpleName() +
|
||||
" to access qualified executor '" + qualifier + "'");
|
||||
}
|
||||
if (beanFactory instanceof ConfigurableBeanFactory configurableBeanFactory) {
|
||||
StringValueResolver embeddedValueResolver = new EmbeddedValueResolver(configurableBeanFactory);
|
||||
String resolvedValue = embeddedValueResolver.resolveStringValue(qualifier);
|
||||
qualifier = resolvedValue != null ? resolvedValue : "";
|
||||
}
|
||||
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, Executor.class, qualifier);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
|
@ -57,6 +57,8 @@ public @interface Async {
|
|||
|
||||
/**
|
||||
* A qualifier value for the specified asynchronous operation(s).
|
||||
* <p>The value support expression such as <code>#{systemProperties.myExecutor}</code>
|
||||
* or property placeholder such as <code>${my.app.myExecutor}</code>.
|
||||
* <p>May be used to determine the target executor to be used when executing
|
||||
* the asynchronous operation(s), matching the qualifier value (or the bean
|
||||
* name) of a specific {@link java.util.concurrent.Executor Executor} or
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
|
@ -21,6 +21,7 @@ import java.lang.annotation.Retention;
|
|||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
|
@ -47,6 +48,7 @@ import org.springframework.context.annotation.Bean;
|
|||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
|
||||
|
|
@ -130,6 +132,29 @@ public class EnableAsyncTests {
|
|||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withAsyncBeanWithExecutorQualifiedByExpression() throws ExecutionException, InterruptedException {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
System.getProperties().put("myExecutor", "myExecutor1");
|
||||
PropertySourcesPlaceholderConfigurer placeholderConfigurer = new PropertySourcesPlaceholderConfigurer();
|
||||
placeholderConfigurer.setProperties(new Properties() {{
|
||||
put("my.app.myExecutor", "myExecutor2");
|
||||
}});
|
||||
placeholderConfigurer.postProcessBeanFactory(context.getBeanFactory());
|
||||
|
||||
context.register(AsyncWithExecutorQualifiedByExpressionConfig.class);
|
||||
context.refresh();
|
||||
|
||||
AsyncBeanWithExecutorQualifiedByExpression asyncBean = context.getBean(AsyncBeanWithExecutorQualifiedByExpression.class);
|
||||
Future<Thread> workerThread1 = asyncBean.myWork1();
|
||||
assertThat(workerThread1.get().getName()).doesNotStartWith("myExecutor2-").startsWith("myExecutor1-");
|
||||
|
||||
Future<Thread> workerThread2 = asyncBean.myWork2();
|
||||
assertThat(workerThread2.get().getName()).startsWith("myExecutor2-");
|
||||
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asyncProcessorIsOrderedLowestPrecedenceByDefault() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
|
|
@ -346,6 +371,19 @@ public class EnableAsyncTests {
|
|||
}
|
||||
}
|
||||
|
||||
static class AsyncBeanWithExecutorQualifiedByExpression {
|
||||
|
||||
@Async("#{systemProperties.myExecutor}")
|
||||
public Future<Thread> myWork1() {
|
||||
return new AsyncResult<>(Thread.currentThread());
|
||||
}
|
||||
|
||||
@Async("${my.app.myExecutor}")
|
||||
public Future<Thread> myWork2() {
|
||||
return new AsyncResult<>(Thread.currentThread());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class AsyncBean {
|
||||
|
||||
|
|
@ -475,6 +513,27 @@ public class EnableAsyncTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
static class AsyncWithExecutorQualifiedByExpressionConfig {
|
||||
|
||||
@Bean
|
||||
public AsyncBeanWithExecutorQualifiedByExpression asyncBean() {
|
||||
return new AsyncBeanWithExecutorQualifiedByExpression();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Executor myExecutor1() {
|
||||
return new ThreadPoolTaskExecutor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Qualifier("myExecutor")
|
||||
public Executor myExecutor2() {
|
||||
return new ThreadPoolTaskExecutor();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
|
|
|
|||
Loading…
Reference in New Issue