Restored lazy resolution of default executor for AsyncAnnotationBeanPostProcessor
Issue: SPR-14030
(cherry picked from commit 155fa37)
This commit is contained in:
parent
05ab769555
commit
7c5aac122a
|
|
@ -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");
|
||||
* 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.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
|
||||
import org.springframework.core.task.AsyncListenableTaskExecutor;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.core.task.support.TaskExecutorAdapter;
|
||||
import org.springframework.lang.UsesJava8;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
|
@ -58,6 +61,15 @@ import org.springframework.util.concurrent.ListenableFuture;
|
|||
*/
|
||||
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?
|
||||
private static final boolean completableFuturePresent = ClassUtils.isPresent(
|
||||
"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 Executor defaultExecutor;
|
||||
private volatile Executor defaultExecutor;
|
||||
|
||||
private AsyncUncaughtExceptionHandler exceptionHandler;
|
||||
|
||||
|
|
@ -75,10 +87,22 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
|||
|
||||
|
||||
/**
|
||||
* Create a new {@link AsyncExecutionAspectSupport}, using the provided default
|
||||
* executor unless individual async methods indicate via qualifier that a more
|
||||
* specific executor should be used.
|
||||
* @param defaultExecutor the executor to use when executing asynchronous methods
|
||||
* Create a new instance with a default {@link AsyncUncaughtExceptionHandler}.
|
||||
* @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
|
||||
*/
|
||||
public AsyncExecutionAspectSupport(Executor defaultExecutor) {
|
||||
this(defaultExecutor, new SimpleAsyncUncaughtExceptionHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, AsyncUncaughtExceptionHandler exceptionHandler) {
|
||||
|
|
@ -86,23 +110,16 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
|||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with a default {@link AsyncUncaughtExceptionHandler}.
|
||||
*/
|
||||
public AsyncExecutionAspectSupport(Executor defaultExecutor) {
|
||||
this(defaultExecutor, new SimpleAsyncUncaughtExceptionHandler());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Supply the executor to be used when executing async methods.
|
||||
* @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.
|
||||
* @see #getExecutorQualifier
|
||||
* @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
|
||||
* @see #getExecutorQualifier(Method)
|
||||
* @see #setBeanFactory(BeanFactory)
|
||||
* @see #getDefaultExecutor(BeanFactory)
|
||||
*/
|
||||
public void setExecutor(Executor defaultExecutor) {
|
||||
this.defaultExecutor = defaultExecutor;
|
||||
|
|
@ -117,7 +134,10 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the {@link BeanFactory} to be used when looking up executors by qualifier.
|
||||
* Set the {@link BeanFactory} to be used when looking up executors by qualifier
|
||||
* or when relying on the default executor lookup algorithm.
|
||||
* @see #findQualifiedExecutor(BeanFactory, String)
|
||||
* @see #getDefaultExecutor(BeanFactory)
|
||||
*/
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
|
|
@ -128,26 +148,32 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
|||
/**
|
||||
* Determine the specific executor to use when executing the given method.
|
||||
* 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) {
|
||||
AsyncTaskExecutor executor = this.executors.get(method);
|
||||
if (executor == null) {
|
||||
Executor executorToUse = this.defaultExecutor;
|
||||
Executor targetExecutor;
|
||||
String qualifier = getExecutorQualifier(method);
|
||||
if (StringUtils.hasLength(qualifier)) {
|
||||
if (this.beanFactory == null) {
|
||||
throw new IllegalStateException("BeanFactory must be set on " + getClass().getSimpleName() +
|
||||
" to access qualified executor '" + qualifier + "'");
|
||||
}
|
||||
executorToUse = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
|
||||
this.beanFactory, Executor.class, qualifier);
|
||||
targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
|
||||
}
|
||||
else if (executorToUse == null) {
|
||||
else {
|
||||
targetExecutor = this.defaultExecutor;
|
||||
if (targetExecutor == null) {
|
||||
synchronized (this.executors) {
|
||||
if (this.defaultExecutor == null) {
|
||||
this.defaultExecutor = getDefaultExecutor(this.beanFactory);
|
||||
}
|
||||
targetExecutor = this.defaultExecutor;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (targetExecutor == null) {
|
||||
return null;
|
||||
}
|
||||
executor = (executorToUse instanceof AsyncListenableTaskExecutor ?
|
||||
(AsyncListenableTaskExecutor) executorToUse : new TaskExecutorAdapter(executorToUse));
|
||||
executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
|
||||
(AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
|
||||
this.executors.put(method, executor);
|
||||
}
|
||||
return executor;
|
||||
|
|
@ -160,11 +186,69 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
|||
* been specified and that the {@linkplain #setExecutor(Executor) default executor}
|
||||
* should be used.
|
||||
* @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 #findQualifiedExecutor(BeanFactory, String)
|
||||
*/
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
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.
|
||||
* @param task the task to execute
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -26,9 +26,11 @@ import org.aopalliance.intercept.MethodInterceptor;
|
|||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
|
|
@ -65,22 +67,27 @@ import org.springframework.util.ClassUtils;
|
|||
*/
|
||||
public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered {
|
||||
|
||||
/**
|
||||
* Create a new instance with a default {@link AsyncUncaughtExceptionHandler}.
|
||||
* @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
|
||||
* or {@link java.util.concurrent.ExecutorService}) to delegate to;
|
||||
* as of 4.2.6, a local executor for this interceptor will be built otherwise
|
||||
*/
|
||||
public AsyncExecutionInterceptor(Executor defaultExecutor) {
|
||||
super(defaultExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code AsyncExecutionInterceptor}.
|
||||
* @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
|
||||
* or {@link java.util.concurrent.ExecutorService}) to delegate to
|
||||
* or {@link java.util.concurrent.ExecutorService}) to delegate to;
|
||||
* as of 4.2.6, a local executor for this interceptor will be built otherwise
|
||||
* @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use
|
||||
*/
|
||||
public AsyncExecutionInterceptor(Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) {
|
||||
super(defaultExecutor, exceptionHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with a default {@link AsyncUncaughtExceptionHandler}.
|
||||
*/
|
||||
public AsyncExecutionInterceptor(Executor defaultExecutor) {
|
||||
super(defaultExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept the given method invocation, submit the actual calling of the method to
|
||||
|
|
@ -136,6 +143,20 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport imple
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation searches for a unique {@link org.springframework.core.task.TaskExecutor}
|
||||
* bean in the context, or for an {@link Executor} bean named "taskExecutor" otherwise.
|
||||
* If neither of the two is resolvable (e.g. if no {@code BeanFactory} was configured at all),
|
||||
* this implementation falls back to a newly created {@link SimpleAsyncTaskExecutor} instance
|
||||
* for local use if no default could be found.
|
||||
* @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME
|
||||
*/
|
||||
@Override
|
||||
protected Executor getDefaultExecutor(BeanFactory beanFactory) {
|
||||
Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
|
||||
return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -17,7 +17,6 @@
|
|||
package org.springframework.scheduling.aspectj;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.aspectj.lang.annotation.SuppressAjWarnings;
|
||||
|
|
@ -29,33 +28,38 @@ import org.springframework.core.task.AsyncTaskExecutor;
|
|||
/**
|
||||
* Abstract aspect that routes selected methods asynchronously.
|
||||
*
|
||||
* <p>This aspect needs to be injected with an implementation of
|
||||
* {@link Executor} to activate it for a specific thread pool.
|
||||
* Otherwise it will simply delegate all calls synchronously.
|
||||
* <p>This aspect needs to be injected with an implementation of a task-oriented
|
||||
* {@link java.util.concurrent.Executor} to activate it for a specific thread pool,
|
||||
* or with a {@link org.springframework.beans.factory.BeanFactory} for default
|
||||
* executor lookup. Otherwise it will simply delegate all calls synchronously.
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Stephane Nicoll
|
||||
* @since 3.0.5
|
||||
* @see #setExecutor
|
||||
* @see #setBeanFactory
|
||||
* @see #getDefaultExecutor
|
||||
*/
|
||||
public abstract aspect AbstractAsyncExecutionAspect extends AsyncExecutionAspectSupport {
|
||||
|
||||
/**
|
||||
* Create an {@code AnnotationAsyncExecutionAspect} with a {@code null} default
|
||||
* executor, which should instead be set via {@code #aspectOf} and
|
||||
* {@link #setExecutor(Executor)}. The same applies for {@link #setExceptionHandler}
|
||||
* Create an {@code AnnotationAsyncExecutionAspect} with a {@code null}
|
||||
* default executor, which should instead be set via {@code #aspectOf} and
|
||||
* {@link #setExecutor}. The same applies for {@link #setExceptionHandler}.
|
||||
*/
|
||||
public AbstractAsyncExecutionAspect() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply around advice to methods matching the {@link #asyncMethod()} pointcut,
|
||||
* submit the actual calling of the method to the correct task executor and return
|
||||
* immediately to the caller.
|
||||
* @return {@link Future} if the original method returns {@code Future}; {@code null}
|
||||
* otherwise.
|
||||
* @return {@link Future} if the original method returns {@code Future};
|
||||
* {@code null} otherwise
|
||||
*/
|
||||
@SuppressAjWarnings("adviceDidNotMatch")
|
||||
Object around() : asyncMethod() {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -33,9 +33,17 @@ import org.springframework.scheduling.annotation.Async;
|
|||
* on the return type. If, however, a class marked with {@code @Async} contains a method
|
||||
* that violates this constraint, it produces only a warning.
|
||||
*
|
||||
* <p>This aspect needs to be injected with an implementation of a task-oriented
|
||||
* {@link java.util.concurrent.Executor} to activate it for a specific thread pool,
|
||||
* or with a {@link org.springframework.beans.factory.BeanFactory} for default
|
||||
* executor lookup. Otherwise it will simply delegate all calls synchronously.
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
* @author Chris Beams
|
||||
* @since 3.0.5
|
||||
* @see #setExecutor
|
||||
* @see #setBeanFactory
|
||||
* @see #getDefaultExecutor
|
||||
*/
|
||||
public aspect AnnotationAsyncExecutionAspect extends AbstractAsyncExecutionAspect {
|
||||
|
||||
|
|
@ -74,7 +82,6 @@ public aspect AnnotationAsyncExecutionAspect extends AbstractAsyncExecutionAspec
|
|||
|
||||
declare warning:
|
||||
execution(!(void || Future+) (@Async *).*(..)):
|
||||
"Methods in a class marked with @Async that do not return void or Future will " +
|
||||
"be routed synchronously";
|
||||
"Methods in a class marked with @Async that do not return void or Future will be routed synchronously";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -42,7 +42,8 @@ public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionIntercept
|
|||
* Create a new {@code AnnotationAsyncExecutionInterceptor} with the given executor
|
||||
* and a simple {@link AsyncUncaughtExceptionHandler}.
|
||||
* @param defaultExecutor the executor to be used by default if no more specific
|
||||
* executor has been qualified at the method level using {@link Async#value()}
|
||||
* executor has been qualified at the method level using {@link Async#value()};
|
||||
* as of 4.2.6, a local executor for this interceptor will be built otherwise
|
||||
*/
|
||||
public AnnotationAsyncExecutionInterceptor(Executor defaultExecutor) {
|
||||
super(defaultExecutor);
|
||||
|
|
@ -51,7 +52,8 @@ public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionIntercept
|
|||
/**
|
||||
* Create a new {@code AnnotationAsyncExecutionInterceptor} with the given executor.
|
||||
* @param defaultExecutor the executor to be used by default if no more specific
|
||||
* executor has been qualified at the method level using {@link Async#value()}
|
||||
* executor has been qualified at the method level using {@link Async#value()};
|
||||
* as of 4.2.6, a local executor for this interceptor will be built otherwise
|
||||
* @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use to
|
||||
* handle exceptions thrown by asynchronous method executions with {@code void}
|
||||
* return type
|
||||
|
|
@ -74,7 +76,7 @@ public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionIntercept
|
|||
*/
|
||||
@Override
|
||||
protected String getExecutorQualifier(Method method) {
|
||||
// maintainer's note: changes made here should also be made in
|
||||
// Maintainer's note: changes made here should also be made in
|
||||
// AnnotationAsyncExecutionAspect#getExecutorQualifier
|
||||
Async async = AnnotationUtils.findAnnotation(method, Async.class);
|
||||
if (async == null) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -32,7 +32,6 @@ import org.springframework.aop.support.ComposablePointcut;
|
|||
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
|
|
@ -73,8 +72,10 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements B
|
|||
/**
|
||||
* Create a new {@code AsyncAnnotationAdvisor} for the given task executor.
|
||||
* @param executor the task executor to use for asynchronous methods
|
||||
* @param exceptionHandler the {@link org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler} to use to
|
||||
* (can be {@code null} to trigger default executor resolution)
|
||||
* @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use to
|
||||
* handle unexpected exception thrown by asynchronous method executions
|
||||
* @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public AsyncAnnotationAdvisor(Executor executor, AsyncUncaughtExceptionHandler exceptionHandler) {
|
||||
|
|
@ -87,9 +88,6 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements B
|
|||
catch (ClassNotFoundException ex) {
|
||||
// If EJB 3.1 API not present, simply ignore.
|
||||
}
|
||||
if (executor == null) {
|
||||
executor = new SimpleAsyncTaskExecutor();
|
||||
}
|
||||
if (exceptionHandler != null) {
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -25,8 +25,6 @@ import org.apache.commons.logging.LogFactory;
|
|||
import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor;
|
||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
|
@ -68,8 +66,10 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd
|
|||
* <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
|
||||
* @see AnnotationAsyncExecutionInterceptor#DEFAULT_TASK_EXECUTOR_BEAN_NAME
|
||||
*/
|
||||
public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME = "taskExecutor";
|
||||
public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME =
|
||||
AnnotationAsyncExecutionInterceptor.DEFAULT_TASK_EXECUTOR_BEAN_NAME;
|
||||
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
|
@ -102,6 +102,13 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd
|
|||
|
||||
/**
|
||||
* Set the {@link Executor} to use when invoking methods asynchronously.
|
||||
* <p>If not specified, default executor resolution will apply: searching 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,
|
||||
* a local default executor will be created within the interceptor.
|
||||
* @see AsyncAnnotationAdvisor#AsyncAnnotationAdvisor(Executor, AsyncUncaughtExceptionHandler)
|
||||
* @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory)
|
||||
* @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME
|
||||
*/
|
||||
public void setExecutor(Executor executor) {
|
||||
this.executor = executor;
|
||||
|
|
@ -121,31 +128,7 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd
|
|||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
super.setBeanFactory(beanFactory);
|
||||
|
||||
Executor executorToUse = this.executor;
|
||||
if (executorToUse == 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.
|
||||
executorToUse = beanFactory.getBean(TaskExecutor.class);
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
try {
|
||||
executorToUse = beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex2) {
|
||||
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 annotation processing.");
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
logger.info("No TaskExecutor bean found for async annotation processing.");
|
||||
// Giving up -> falling back to default executor within the advisor...
|
||||
}
|
||||
}
|
||||
|
||||
AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(executorToUse, this.exceptionHandler);
|
||||
AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
|
||||
if (this.asyncAnnotationType != null) {
|
||||
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -41,26 +41,28 @@ import org.springframework.core.Ordered;
|
|||
* @Configuration
|
||||
* @EnableAsync
|
||||
* public class AppConfig {
|
||||
*
|
||||
* @Bean
|
||||
* public MyAsyncBean asyncBean() {
|
||||
* return new MyAsyncBean();
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
*
|
||||
* <p>The {@link #mode} attribute controls how advice is applied; if the mode is
|
||||
* {@link AdviceMode#PROXY} (the default), then the other attributes control the behavior
|
||||
* of the proxying.
|
||||
*
|
||||
* <p>Note that if the {@linkplain #mode} is set to {@link AdviceMode#ASPECTJ}, then
|
||||
* the value of the {@link #proxyTargetClass} attribute will be ignored. Note also
|
||||
* that in this case the {@code spring-aspects} module JAR must be present on the
|
||||
* classpath.
|
||||
* <p>Note that if the {@linkplain #mode} is set to {@link AdviceMode#ASPECTJ}, then the
|
||||
* value of the {@link #proxyTargetClass} attribute will be ignored. Note also that in
|
||||
* this case the {@code spring-aspects} module JAR must be present on the classpath.
|
||||
*
|
||||
* <p>By default, a {@link org.springframework.core.task.SimpleAsyncTaskExecutor
|
||||
* SimpleAsyncTaskExecutor} will be used to process async method invocations. Besides,
|
||||
* annotated methods having a {@code void} return type cannot transmit any exception
|
||||
* back to the caller. By default, such uncaught exceptions are only logged.
|
||||
* <p>By default, Spring will be searching for an associated thread pool definition:
|
||||
* either a unique {@link org.springframework.core.task.TaskExecutor} bean in the context,
|
||||
* or an {@link java.util.concurrent.Executor} bean named "taskExecutor" otherwise. If
|
||||
* neither of the two is resolvable, a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}
|
||||
* will be used to process async method invocations. Besides, annotated methods having a
|
||||
* {@code void} return type cannot transmit any exception back to the caller. By default,
|
||||
* such uncaught exceptions are only logged.
|
||||
*
|
||||
* <p>To customize all this, implement {@link AsyncConfigurer} and
|
||||
* provide:
|
||||
|
|
@ -129,6 +131,7 @@ import org.springframework.core.Ordered;
|
|||
* through direct access to actual componentry.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
* @since 3.1
|
||||
|
|
@ -175,9 +178,8 @@ public @interface EnableAsync {
|
|||
AdviceMode mode() default AdviceMode.PROXY;
|
||||
|
||||
/**
|
||||
* Indicate the order in which the
|
||||
* {@link org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor
|
||||
* AsyncAnnotationBeanPostProcessor} should be applied.
|
||||
* Indicate the order in which the {@link AsyncAnnotationBeanPostProcessor}
|
||||
* should be applied.
|
||||
* <p>The default is {@link Ordered#LOWEST_PRECEDENCE} in order to run
|
||||
* after all other post-processors, so that it can add an advisor to
|
||||
* existing proxies rather than double-proxy.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -37,16 +37,18 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
|||
* @Configuration
|
||||
* @EnableScheduling
|
||||
* public class AppConfig {
|
||||
*
|
||||
* // various @Bean definitions
|
||||
* }</pre>
|
||||
*
|
||||
* This enables detection of @{@link Scheduled} annotations on any Spring-managed
|
||||
* bean in the container. For example, given a class {@code MyTask}
|
||||
* bean in the container. For example, given a class {@code MyTask}
|
||||
*
|
||||
* <pre class="code">
|
||||
* package com.myco.tasks;
|
||||
*
|
||||
* public class MyTask {
|
||||
*
|
||||
* @Scheduled(fixedRate=1000)
|
||||
* public void work() {
|
||||
* // task execution logic
|
||||
|
|
@ -60,6 +62,7 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
|||
* @Configuration
|
||||
* @EnableScheduling
|
||||
* public class AppConfig {
|
||||
*
|
||||
* @Bean
|
||||
* public MyTask task() {
|
||||
* return new MyTask();
|
||||
|
|
@ -84,14 +87,21 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
|||
* @Configuration
|
||||
* @EnableScheduling
|
||||
* public class AppConfig {
|
||||
*
|
||||
* @Scheduled(fixedRate=1000)
|
||||
* public void work() {
|
||||
* // task execution logic
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* In all of the above scenarios, a default single-threaded task executor is used.
|
||||
* When more control is desired, a {@code @Configuration} class may implement
|
||||
* <p>By default, will be searching for an associated scheduler definition: either
|
||||
* a unique {@link org.springframework.scheduling.TaskScheduler} bean in the context,
|
||||
* or a {@code TaskScheduler} bean named "taskScheduler" otherwise; the same lookup
|
||||
* will also be performed for a {@link java.util.concurrent.ScheduledExecutorService}
|
||||
* bean. If neither of the two is resolvable, a local single-threaded default
|
||||
* scheduler will be created and used within the registrar.
|
||||
*
|
||||
* <p>When more control is desired, a {@code @Configuration} class may implement
|
||||
* {@link SchedulingConfigurer}. This allows access to the underlying
|
||||
* {@link ScheduledTaskRegistrar} instance. For example, the following example
|
||||
* demonstrates how to customize the {@link Executor} used to execute scheduled
|
||||
|
|
@ -101,6 +111,7 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
|||
* @Configuration
|
||||
* @EnableScheduling
|
||||
* public class AppConfig implements SchedulingConfigurer {
|
||||
*
|
||||
* @Override
|
||||
* public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
||||
* taskRegistrar.setScheduler(taskExecutor());
|
||||
|
|
@ -112,11 +123,11 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
|||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* Note in the example above the use of {@code @Bean(destroyMethod="shutdown")}. This
|
||||
* ensures that the task executor is properly shut down when the Spring application
|
||||
* context itself is closed.
|
||||
* <p>Note in the example above the use of {@code @Bean(destroyMethod="shutdown")}.
|
||||
* This ensures that the task executor is properly shut down when the Spring
|
||||
* application context itself is closed.
|
||||
*
|
||||
* Implementing {@code SchedulingConfigurer} also allows for fine-grained
|
||||
* <p>Implementing {@code SchedulingConfigurer} also allows for fine-grained
|
||||
* control over task registration via the {@code ScheduledTaskRegistrar}.
|
||||
* For example, the following configures the execution of a particular bean
|
||||
* method per a custom {@code Trigger} implementation:
|
||||
|
|
@ -125,6 +136,7 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
|||
* @Configuration
|
||||
* @EnableScheduling
|
||||
* public class AppConfig implements SchedulingConfigurer {
|
||||
*
|
||||
* @Override
|
||||
* public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
||||
* taskRegistrar.setScheduler(taskScheduler());
|
||||
|
|
@ -167,6 +179,7 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
|||
* through direct access to actual componentry.<p>
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see Scheduled
|
||||
* @see SchedulingConfiguration
|
||||
|
|
|
|||
Loading…
Reference in New Issue