Polish contribution

See gh-27818
This commit is contained in:
Sam Brannen 2022-02-19 15:55:11 +01:00
parent 01214b3473
commit be8224a590
3 changed files with 32 additions and 33 deletions

View File

@ -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);
} }

View File

@ -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 "";

View File

@ -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