parent
01214b3473
commit
be8224a590
|
@ -43,7 +43,6 @@ 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;
|
||||
|
||||
|
@ -212,9 +211,8 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
|||
" to access qualified executor '" + qualifier + "'");
|
||||
}
|
||||
if (beanFactory instanceof ConfigurableBeanFactory configurableBeanFactory) {
|
||||
StringValueResolver embeddedValueResolver = new EmbeddedValueResolver(configurableBeanFactory);
|
||||
String resolvedValue = embeddedValueResolver.resolveStringValue(qualifier);
|
||||
qualifier = resolvedValue != null ? resolvedValue : "";
|
||||
EmbeddedValueResolver embeddedValueResolver = new EmbeddedValueResolver(configurableBeanFactory);
|
||||
qualifier = embeddedValueResolver.resolveStringValue(qualifier);
|
||||
}
|
||||
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, Executor.class, qualifier);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@ import java.lang.annotation.Target;
|
|||
|
||||
/**
|
||||
* Annotation that marks a method as a candidate for <i>asynchronous</i> execution.
|
||||
* Can also be used at the type level, in which case all of the type's methods are
|
||||
*
|
||||
* <p>Can also be used at the type level, in which case all of the type's methods are
|
||||
* considered as asynchronous. Note, however, that {@code @Async} is not supported
|
||||
* on methods declared within a
|
||||
* {@link org.springframework.context.annotation.Configuration @Configuration} class.
|
||||
|
@ -41,7 +42,7 @@ import java.lang.annotation.Target;
|
|||
* {@code Future} that can be used to track the result of the asynchronous method
|
||||
* execution. However, since the target method needs to implement the same signature,
|
||||
* it will have to return a temporary {@code Future} handle that just passes a value
|
||||
* through: e.g. Spring's {@link AsyncResult}, EJB 3.1's {@link jakarta.ejb.AsyncResult},
|
||||
* through: for example, Spring's {@link AsyncResult}, EJB 3.1's {@link jakarta.ejb.AsyncResult},
|
||||
* or {@link java.util.concurrent.CompletableFuture#completedFuture(Object)}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
|
@ -57,16 +58,18 @@ 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
|
||||
* {@link org.springframework.core.task.TaskExecutor TaskExecutor}
|
||||
* bean definition.
|
||||
* <p>When specified on a class-level {@code @Async} annotation, indicates that the
|
||||
* <p>When specified in a class-level {@code @Async} annotation, indicates that the
|
||||
* given executor should be used for all methods within the class. Method-level use
|
||||
* of {@code Async#value} always overrides any value set at the class level.
|
||||
* of {@code Async#value} always overrides any qualifier value configured at
|
||||
* the class level.
|
||||
* <p>The qualifier value will be resolved dynamically if supplied as a SpEL
|
||||
* expression (for example, {@code "#{environment['myExecutor']}"}) or a
|
||||
* property placeholder (for example, {@code "${my.app.myExecutor}"}).
|
||||
* @since 3.1.2
|
||||
*/
|
||||
String value() default "";
|
||||
|
|
|
@ -21,7 +21,6 @@ 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;
|
||||
|
@ -42,13 +41,13 @@ import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
|
|||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
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;
|
||||
|
@ -133,26 +132,25 @@ public class EnableAsyncTests {
|
|||
}
|
||||
|
||||
@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());
|
||||
public void withAsyncBeanWithExecutorQualifiedByExpressionOrPlaceholder() throws Exception {
|
||||
System.setProperty("myExecutor", "myExecutor1");
|
||||
System.setProperty("my.app.myExecutor", "myExecutor2");
|
||||
|
||||
context.register(AsyncWithExecutorQualifiedByExpressionConfig.class);
|
||||
context.refresh();
|
||||
Class<?> configClass = AsyncWithExecutorQualifiedByExpressionConfig.class;
|
||||
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(configClass)) {
|
||||
AsyncBeanWithExecutorQualifiedByExpressionOrPlaceholder asyncBean =
|
||||
context.getBean(AsyncBeanWithExecutorQualifiedByExpressionOrPlaceholder.class);
|
||||
|
||||
AsyncBeanWithExecutorQualifiedByExpression asyncBean = context.getBean(AsyncBeanWithExecutorQualifiedByExpression.class);
|
||||
Future<Thread> workerThread1 = asyncBean.myWork1();
|
||||
assertThat(workerThread1.get().getName()).doesNotStartWith("myExecutor2-").startsWith("myExecutor1-");
|
||||
Future<Thread> workerThread1 = asyncBean.myWork1();
|
||||
assertThat(workerThread1.get().getName()).startsWith("myExecutor1-");
|
||||
|
||||
Future<Thread> workerThread2 = asyncBean.myWork2();
|
||||
assertThat(workerThread2.get().getName()).startsWith("myExecutor2-");
|
||||
|
||||
context.close();
|
||||
Future<Thread> workerThread2 = asyncBean.myWork2();
|
||||
assertThat(workerThread2.get().getName()).startsWith("myExecutor2-");
|
||||
}
|
||||
finally {
|
||||
System.clearProperty("myExecutor");
|
||||
System.clearProperty("my.app.myExecutor");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -371,9 +369,9 @@ public class EnableAsyncTests {
|
|||
}
|
||||
}
|
||||
|
||||
static class AsyncBeanWithExecutorQualifiedByExpression {
|
||||
static class AsyncBeanWithExecutorQualifiedByExpressionOrPlaceholder {
|
||||
|
||||
@Async("#{systemProperties.myExecutor}")
|
||||
@Async("#{environment['myExecutor']}")
|
||||
public Future<Thread> myWork1() {
|
||||
return new AsyncResult<>(Thread.currentThread());
|
||||
}
|
||||
|
@ -518,8 +516,8 @@ public class EnableAsyncTests {
|
|||
static class AsyncWithExecutorQualifiedByExpressionConfig {
|
||||
|
||||
@Bean
|
||||
public AsyncBeanWithExecutorQualifiedByExpression asyncBean() {
|
||||
return new AsyncBeanWithExecutorQualifiedByExpression();
|
||||
public AsyncBeanWithExecutorQualifiedByExpressionOrPlaceholder asyncBean() {
|
||||
return new AsyncBeanWithExecutorQualifiedByExpressionOrPlaceholder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
Loading…
Reference in New Issue