parent
01214b3473
commit
be8224a590
|
@ -43,7 +43,6 @@ import org.springframework.core.task.support.TaskExecutorAdapter;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.util.StringValueResolver;
|
|
||||||
import org.springframework.util.concurrent.ListenableFuture;
|
import org.springframework.util.concurrent.ListenableFuture;
|
||||||
import org.springframework.util.function.SingletonSupplier;
|
import org.springframework.util.function.SingletonSupplier;
|
||||||
|
|
||||||
|
@ -212,9 +211,8 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
||||||
" to access qualified executor '" + qualifier + "'");
|
" to access qualified executor '" + qualifier + "'");
|
||||||
}
|
}
|
||||||
if (beanFactory instanceof ConfigurableBeanFactory configurableBeanFactory) {
|
if (beanFactory instanceof ConfigurableBeanFactory configurableBeanFactory) {
|
||||||
StringValueResolver embeddedValueResolver = new EmbeddedValueResolver(configurableBeanFactory);
|
EmbeddedValueResolver embeddedValueResolver = new EmbeddedValueResolver(configurableBeanFactory);
|
||||||
String resolvedValue = embeddedValueResolver.resolveStringValue(qualifier);
|
qualifier = embeddedValueResolver.resolveStringValue(qualifier);
|
||||||
qualifier = resolvedValue != null ? resolvedValue : "";
|
|
||||||
}
|
}
|
||||||
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, Executor.class, 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.
|
* 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
|
* considered as asynchronous. Note, however, that {@code @Async} is not supported
|
||||||
* on methods declared within a
|
* on methods declared within a
|
||||||
* {@link org.springframework.context.annotation.Configuration @Configuration} class.
|
* {@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
|
* {@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,
|
* 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
|
* 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)}.
|
* or {@link java.util.concurrent.CompletableFuture#completedFuture(Object)}.
|
||||||
*
|
*
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
@ -57,16 +58,18 @@ public @interface Async {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A qualifier value for the specified asynchronous operation(s).
|
* 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
|
* <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
|
* the asynchronous operation(s), matching the qualifier value (or the bean
|
||||||
* name) of a specific {@link java.util.concurrent.Executor Executor} or
|
* name) of a specific {@link java.util.concurrent.Executor Executor} or
|
||||||
* {@link org.springframework.core.task.TaskExecutor TaskExecutor}
|
* {@link org.springframework.core.task.TaskExecutor TaskExecutor}
|
||||||
* bean definition.
|
* 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
|
* 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
|
* @since 3.1.2
|
||||||
*/
|
*/
|
||||||
String value() default "";
|
String value() default "";
|
||||||
|
|
|
@ -21,7 +21,6 @@ import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
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.UnsatisfiedDependencyException;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.context.annotation.AdviceMode;
|
import org.springframework.context.annotation.AdviceMode;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
|
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
|
||||||
|
@ -133,26 +132,25 @@ public class EnableAsyncTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void withAsyncBeanWithExecutorQualifiedByExpression() throws ExecutionException, InterruptedException {
|
public void withAsyncBeanWithExecutorQualifiedByExpressionOrPlaceholder() throws Exception {
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
System.setProperty("myExecutor", "myExecutor1");
|
||||||
System.getProperties().put("myExecutor", "myExecutor1");
|
System.setProperty("my.app.myExecutor", "myExecutor2");
|
||||||
PropertySourcesPlaceholderConfigurer placeholderConfigurer = new PropertySourcesPlaceholderConfigurer();
|
|
||||||
placeholderConfigurer.setProperties(new Properties() {{
|
|
||||||
put("my.app.myExecutor", "myExecutor2");
|
|
||||||
}});
|
|
||||||
placeholderConfigurer.postProcessBeanFactory(context.getBeanFactory());
|
|
||||||
|
|
||||||
context.register(AsyncWithExecutorQualifiedByExpressionConfig.class);
|
Class<?> configClass = AsyncWithExecutorQualifiedByExpressionConfig.class;
|
||||||
context.refresh();
|
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(configClass)) {
|
||||||
|
AsyncBeanWithExecutorQualifiedByExpressionOrPlaceholder asyncBean =
|
||||||
|
context.getBean(AsyncBeanWithExecutorQualifiedByExpressionOrPlaceholder.class);
|
||||||
|
|
||||||
AsyncBeanWithExecutorQualifiedByExpression asyncBean = context.getBean(AsyncBeanWithExecutorQualifiedByExpression.class);
|
Future<Thread> workerThread1 = asyncBean.myWork1();
|
||||||
Future<Thread> workerThread1 = asyncBean.myWork1();
|
assertThat(workerThread1.get().getName()).startsWith("myExecutor1-");
|
||||||
assertThat(workerThread1.get().getName()).doesNotStartWith("myExecutor2-").startsWith("myExecutor1-");
|
|
||||||
|
|
||||||
Future<Thread> workerThread2 = asyncBean.myWork2();
|
Future<Thread> workerThread2 = asyncBean.myWork2();
|
||||||
assertThat(workerThread2.get().getName()).startsWith("myExecutor2-");
|
assertThat(workerThread2.get().getName()).startsWith("myExecutor2-");
|
||||||
|
}
|
||||||
context.close();
|
finally {
|
||||||
|
System.clearProperty("myExecutor");
|
||||||
|
System.clearProperty("my.app.myExecutor");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -371,9 +369,9 @@ public class EnableAsyncTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class AsyncBeanWithExecutorQualifiedByExpression {
|
static class AsyncBeanWithExecutorQualifiedByExpressionOrPlaceholder {
|
||||||
|
|
||||||
@Async("#{systemProperties.myExecutor}")
|
@Async("#{environment['myExecutor']}")
|
||||||
public Future<Thread> myWork1() {
|
public Future<Thread> myWork1() {
|
||||||
return new AsyncResult<>(Thread.currentThread());
|
return new AsyncResult<>(Thread.currentThread());
|
||||||
}
|
}
|
||||||
|
@ -518,8 +516,8 @@ public class EnableAsyncTests {
|
||||||
static class AsyncWithExecutorQualifiedByExpressionConfig {
|
static class AsyncWithExecutorQualifiedByExpressionConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public AsyncBeanWithExecutorQualifiedByExpression asyncBean() {
|
public AsyncBeanWithExecutorQualifiedByExpressionOrPlaceholder asyncBean() {
|
||||||
return new AsyncBeanWithExecutorQualifiedByExpression();
|
return new AsyncBeanWithExecutorQualifiedByExpressionOrPlaceholder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|
Loading…
Reference in New Issue