|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* |
|
|
|
/* |
|
|
|
* Copyright 2002-2015 the original author or authors. |
|
|
|
* Copyright 2002-2016 the original author or authors. |
|
|
|
* |
|
|
|
* |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
@ -31,9 +31,12 @@ import org.apache.commons.logging.LogFactory; |
|
|
|
|
|
|
|
|
|
|
|
import org.springframework.beans.factory.BeanFactory; |
|
|
|
import org.springframework.beans.factory.BeanFactory; |
|
|
|
import org.springframework.beans.factory.BeanFactoryAware; |
|
|
|
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.annotation.BeanFactoryAnnotationUtils; |
|
|
|
import org.springframework.core.task.AsyncListenableTaskExecutor; |
|
|
|
import org.springframework.core.task.AsyncListenableTaskExecutor; |
|
|
|
import org.springframework.core.task.AsyncTaskExecutor; |
|
|
|
import org.springframework.core.task.AsyncTaskExecutor; |
|
|
|
|
|
|
|
import org.springframework.core.task.TaskExecutor; |
|
|
|
import org.springframework.core.task.support.TaskExecutorAdapter; |
|
|
|
import org.springframework.core.task.support.TaskExecutorAdapter; |
|
|
|
import org.springframework.lang.UsesJava8; |
|
|
|
import org.springframework.lang.UsesJava8; |
|
|
|
import org.springframework.util.ClassUtils; |
|
|
|
import org.springframework.util.ClassUtils; |
|
|
|
@ -58,6 +61,15 @@ import org.springframework.util.concurrent.ListenableFuture; |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { |
|
|
|
public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* The default name of the {@link TaskExecutor} bean to pick up: "taskExecutor". |
|
|
|
|
|
|
|
* <p>Note that the initial lookup happens by type; this is just the fallback |
|
|
|
|
|
|
|
* in case of multiple executor beans found in the context. |
|
|
|
|
|
|
|
* @since 4.2.6 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME = "taskExecutor"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Java 8's CompletableFuture type present?
|
|
|
|
// Java 8's CompletableFuture type present?
|
|
|
|
private static final boolean completableFuturePresent = ClassUtils.isPresent( |
|
|
|
private static final boolean completableFuturePresent = ClassUtils.isPresent( |
|
|
|
"java.util.concurrent.CompletableFuture", AsyncExecutionInterceptor.class.getClassLoader()); |
|
|
|
"java.util.concurrent.CompletableFuture", AsyncExecutionInterceptor.class.getClassLoader()); |
|
|
|
@ -67,7 +79,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { |
|
|
|
|
|
|
|
|
|
|
|
private final Map<Method, AsyncTaskExecutor> executors = new ConcurrentHashMap<Method, AsyncTaskExecutor>(16); |
|
|
|
private final Map<Method, AsyncTaskExecutor> executors = new ConcurrentHashMap<Method, AsyncTaskExecutor>(16); |
|
|
|
|
|
|
|
|
|
|
|
private Executor defaultExecutor; |
|
|
|
private volatile Executor defaultExecutor; |
|
|
|
|
|
|
|
|
|
|
|
private AsyncUncaughtExceptionHandler exceptionHandler; |
|
|
|
private AsyncUncaughtExceptionHandler exceptionHandler; |
|
|
|
|
|
|
|
|
|
|
|
@ -75,33 +87,37 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Create a new {@link AsyncExecutionAspectSupport}, using the provided default |
|
|
|
* Create a new instance with a default {@link AsyncUncaughtExceptionHandler}. |
|
|
|
* executor unless individual async methods indicate via qualifier that a more |
|
|
|
* @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor} |
|
|
|
* specific executor should be used. |
|
|
|
* or {@link java.util.concurrent.ExecutorService}) to delegate to, unless a more specific |
|
|
|
* @param defaultExecutor the executor to use when executing asynchronous methods |
|
|
|
* executor has been requested via a qualifier on the async method, in which case the |
|
|
|
* @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use |
|
|
|
* executor will be looked up at invocation time against the enclosing bean factory |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public AsyncExecutionAspectSupport(Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) { |
|
|
|
public AsyncExecutionAspectSupport(Executor defaultExecutor) { |
|
|
|
this.defaultExecutor = defaultExecutor; |
|
|
|
this(defaultExecutor, new SimpleAsyncUncaughtExceptionHandler()); |
|
|
|
this.exceptionHandler = exceptionHandler; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Create a new instance with a default {@link AsyncUncaughtExceptionHandler}. |
|
|
|
* Create a new {@link AsyncExecutionAspectSupport} with the given exception handler. |
|
|
|
|
|
|
|
* @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor} |
|
|
|
|
|
|
|
* or {@link java.util.concurrent.ExecutorService}) to delegate to, unless a more specific |
|
|
|
|
|
|
|
* executor has been requested via a qualifier on the async method, in which case the |
|
|
|
|
|
|
|
* executor will be looked up at invocation time against the enclosing bean factory |
|
|
|
|
|
|
|
* @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public AsyncExecutionAspectSupport(Executor defaultExecutor) { |
|
|
|
public AsyncExecutionAspectSupport(Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) { |
|
|
|
this(defaultExecutor, new SimpleAsyncUncaughtExceptionHandler()); |
|
|
|
this.defaultExecutor = defaultExecutor; |
|
|
|
|
|
|
|
this.exceptionHandler = exceptionHandler; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Supply the executor to be used when executing async methods. |
|
|
|
* Supply the executor to be used when executing async methods. |
|
|
|
* @param defaultExecutor the {@code Executor} (typically a Spring {@code |
|
|
|
* @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor} |
|
|
|
* AsyncTaskExecutor} or {@link java.util.concurrent.ExecutorService}) to delegate to |
|
|
|
* or {@link java.util.concurrent.ExecutorService}) to delegate to, unless a more specific |
|
|
|
* unless a more specific executor has been requested via a qualifier on the async |
|
|
|
* executor has been requested via a qualifier on the async method, in which case the |
|
|
|
* method, in which case the executor will be looked up at invocation time against the |
|
|
|
* executor will be looked up at invocation time against the enclosing bean factory |
|
|
|
* enclosing bean factory. |
|
|
|
* @see #getExecutorQualifier(Method) |
|
|
|
* @see #getExecutorQualifier |
|
|
|
|
|
|
|
* @see #setBeanFactory(BeanFactory) |
|
|
|
* @see #setBeanFactory(BeanFactory) |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setExecutor(Executor defaultExecutor) { |
|
|
|
public void setExecutor(Executor defaultExecutor) { |
|
|
|
@ -128,26 +144,32 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { |
|
|
|
/** |
|
|
|
/** |
|
|
|
* Determine the specific executor to use when executing the given method. |
|
|
|
* Determine the specific executor to use when executing the given method. |
|
|
|
* Should preferably return an {@link AsyncListenableTaskExecutor} implementation. |
|
|
|
* Should preferably return an {@link AsyncListenableTaskExecutor} implementation. |
|
|
|
* @return the executor to use (or {@code null}, but just if no default executor has been set) |
|
|
|
* @return the executor to use (or {@code null}, but just if no default executor is available) |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected AsyncTaskExecutor determineAsyncExecutor(Method method) { |
|
|
|
protected AsyncTaskExecutor determineAsyncExecutor(Method method) { |
|
|
|
AsyncTaskExecutor executor = this.executors.get(method); |
|
|
|
AsyncTaskExecutor executor = this.executors.get(method); |
|
|
|
if (executor == null) { |
|
|
|
if (executor == null) { |
|
|
|
Executor executorToUse = this.defaultExecutor; |
|
|
|
Executor targetExecutor; |
|
|
|
String qualifier = getExecutorQualifier(method); |
|
|
|
String qualifier = getExecutorQualifier(method); |
|
|
|
if (StringUtils.hasLength(qualifier)) { |
|
|
|
if (StringUtils.hasLength(qualifier)) { |
|
|
|
if (this.beanFactory == null) { |
|
|
|
targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier); |
|
|
|
throw new IllegalStateException("BeanFactory must be set on " + getClass().getSimpleName() + |
|
|
|
} |
|
|
|
" to access qualified executor '" + qualifier + "'"); |
|
|
|
else { |
|
|
|
|
|
|
|
targetExecutor = this.defaultExecutor; |
|
|
|
|
|
|
|
if (targetExecutor == null) { |
|
|
|
|
|
|
|
synchronized (this.executors) { |
|
|
|
|
|
|
|
if (this.defaultExecutor == null) { |
|
|
|
|
|
|
|
this.defaultExecutor = getDefaultExecutor(this.beanFactory); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
targetExecutor = this.defaultExecutor; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
executorToUse = BeanFactoryAnnotationUtils.qualifiedBeanOfType( |
|
|
|
|
|
|
|
this.beanFactory, Executor.class, qualifier); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
else if (executorToUse == null) { |
|
|
|
if (targetExecutor == null) { |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
executor = (executorToUse instanceof AsyncListenableTaskExecutor ? |
|
|
|
executor = (targetExecutor instanceof AsyncListenableTaskExecutor ? |
|
|
|
(AsyncListenableTaskExecutor) executorToUse : new TaskExecutorAdapter(executorToUse)); |
|
|
|
(AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor)); |
|
|
|
this.executors.put(method, executor); |
|
|
|
this.executors.put(method, executor); |
|
|
|
} |
|
|
|
} |
|
|
|
return executor; |
|
|
|
return executor; |
|
|
|
@ -160,11 +182,69 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { |
|
|
|
* been specified and that the {@linkplain #setExecutor(Executor) default executor} |
|
|
|
* been specified and that the {@linkplain #setExecutor(Executor) default executor} |
|
|
|
* should be used. |
|
|
|
* should be used. |
|
|
|
* @param method the method to inspect for executor qualifier metadata |
|
|
|
* @param method the method to inspect for executor qualifier metadata |
|
|
|
* @return the qualifier if specified, otherwise empty string or {@code null} |
|
|
|
* @return the qualifier if specified, otherwise empty String or {@code null} |
|
|
|
* @see #determineAsyncExecutor(Method) |
|
|
|
* @see #determineAsyncExecutor(Method) |
|
|
|
|
|
|
|
* @see #findQualifiedExecutor(BeanFactory, String) |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected abstract String getExecutorQualifier(Method method); |
|
|
|
protected abstract String getExecutorQualifier(Method method); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Retrieve a target executor for the given qualifier. |
|
|
|
|
|
|
|
* @param qualifier the qualifier to resolve |
|
|
|
|
|
|
|
* @return the target executor, or {@code null} if none available |
|
|
|
|
|
|
|
* @since 4.2.6 |
|
|
|
|
|
|
|
* @see #getExecutorQualifier(Method) |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
protected Executor findQualifiedExecutor(BeanFactory beanFactory, String qualifier) { |
|
|
|
|
|
|
|
if (beanFactory == null) { |
|
|
|
|
|
|
|
throw new IllegalStateException("BeanFactory must be set on " + getClass().getSimpleName() + |
|
|
|
|
|
|
|
" to access qualified executor '" + qualifier + "'"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, Executor.class, qualifier); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Retrieve or build a default executor for this advice instance. |
|
|
|
|
|
|
|
* An executor returned from here will be cached for further use. |
|
|
|
|
|
|
|
* <p>The default implementation searches for a unique {@link TaskExecutor} bean |
|
|
|
|
|
|
|
* in the context, or for an {@link Executor} bean named "taskExecutor" otherwise. |
|
|
|
|
|
|
|
* If neither of the two is resolvable, this implementation will return {@code null}. |
|
|
|
|
|
|
|
* @param beanFactory the BeanFactory to use for a default executor lookup |
|
|
|
|
|
|
|
* @return the default executor, or {@code null} if none available |
|
|
|
|
|
|
|
* @since 4.2.6 |
|
|
|
|
|
|
|
* @see #findQualifiedExecutor(BeanFactory, String) |
|
|
|
|
|
|
|
* @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
protected Executor getDefaultExecutor(BeanFactory beanFactory) { |
|
|
|
|
|
|
|
if (beanFactory != null) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
// Search for TaskExecutor bean... not plain Executor since that would
|
|
|
|
|
|
|
|
// match with ScheduledExecutorService as well, which is unusable for
|
|
|
|
|
|
|
|
// our purposes here. TaskExecutor is more clearly designed for it.
|
|
|
|
|
|
|
|
return beanFactory.getBean(TaskExecutor.class); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (NoUniqueBeanDefinitionException ex) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (NoSuchBeanDefinitionException ex2) { |
|
|
|
|
|
|
|
if (logger.isInfoEnabled()) { |
|
|
|
|
|
|
|
logger.info("More than one TaskExecutor bean found within the context, and none is named " + |
|
|
|
|
|
|
|
"'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly " + |
|
|
|
|
|
|
|
"as an alias) in order to use it for async processing: " + ex.getBeanNamesFound()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (NoSuchBeanDefinitionException ex) { |
|
|
|
|
|
|
|
logger.debug("Could not find default TaskExecutor bean", ex); |
|
|
|
|
|
|
|
// Giving up -> either using local default executor or none at all...
|
|
|
|
|
|
|
|
logger.info("No TaskExecutor bean found for async processing"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Delegate for actually executing the given task with the chosen executor. |
|
|
|
* Delegate for actually executing the given task with the chosen executor. |
|
|
|
* @param task the task to execute |
|
|
|
* @param task the task to execute |
|
|
|
|