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

View File

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

View File

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