diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java index 134bc2f3e02..8dce6323299 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java @@ -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". + *
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 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.
* @param task the task to execute
diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java
index 8aa0c020ac3..a758fd2576e 100644
--- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java
@@ -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;
diff --git a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj
index f11400f05ee..a985f614de1 100644
--- a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj
+++ b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj
@@ -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.
@@ -54,8 +54,8 @@ public abstract aspect AbstractAsyncExecutionAspect extends AsyncExecutionAspect
* 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() {
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java
index c3b0ad453e7..117de48dfbc 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java
@@ -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) {
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationAdvisor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationAdvisor.java
index 310c8810aea..ff321cf8e74 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationAdvisor.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationAdvisor.java
@@ -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;
}
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java
index 69c969a5768..6d699b3961a 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java
@@ -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
* 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.
+ * 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);
}
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java
index ad7c3b0416e..ef3c396e646 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java
@@ -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();
* }
* }
*
- *
* 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.
*
- * 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.
+ * 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.
*
- * 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.
+ * 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.
*
* 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.
* 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.
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java
index 944ca9a6bd0..b5f643f31b2 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java
@@ -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
* }
*
* 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}
*
* 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.
+ *
+ * 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;
* }
* }
*
- * 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.
+ * 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
+ * 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.
*
* @author Chris Beams
+ * @author Juergen Hoeller
* @since 3.1
* @see Scheduled
* @see SchedulingConfiguration
diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java
index 9aa2c9c7ade..c455380b2c2 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java
@@ -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.
@@ -89,6 +89,7 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
* The default name of the {@link TaskScheduler} bean to pick up: "taskScheduler".
* Note that the initial lookup happens by type; this is just the fallback
* in case of multiple scheduler beans found in the context.
+ * @since 4.2
*/
public static final String DEFAULT_TASK_SCHEDULER_BEAN_NAME = "taskScheduler";
@@ -118,6 +119,12 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
* Set the {@link org.springframework.scheduling.TaskScheduler} that will invoke
* the scheduled methods, or a {@link java.util.concurrent.ScheduledExecutorService}
* to be wrapped as a TaskScheduler.
+ * If not specified, default scheduler resolution will apply: searching for a
+ * unique {@link TaskScheduler} bean in the context, or for a {@link TaskScheduler}
+ * bean named "taskScheduler" otherwise; the same lookup will also be performed for
+ * a {@link ScheduledExecutorService} bean. If neither of the two is resolvable,
+ * a local single-threaded default scheduler will be created within the registrar.
+ * @see #DEFAULT_TASK_SCHEDULER_BEAN_NAME
*/
public void setScheduler(Object scheduler) {
this.scheduler = scheduler;
@@ -195,10 +202,13 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
this.beanFactory.getBean(DEFAULT_TASK_SCHEDULER_BEAN_NAME, TaskScheduler.class));
}
catch (NoSuchBeanDefinitionException ex2) {
- throw new IllegalStateException("More than one TaskScheduler bean exists within the context, and " +
- "none is named 'taskScheduler'. Mark one of them as primary or name it 'taskScheduler' "+
- "(possibly as an alias); or implement the SchedulingConfigurer interface and call " +
- "ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback.", ex);
+ if (logger.isInfoEnabled()) {
+ logger.info("More than one TaskScheduler bean exists within the context, and " +
+ "none is named 'taskScheduler'. Mark one of them as primary or name it 'taskScheduler' " +
+ "(possibly as an alias); or implement the SchedulingConfigurer interface and call " +
+ "ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback: " +
+ ex.getBeanNamesFound());
+ }
}
}
catch (NoSuchBeanDefinitionException ex) {
@@ -208,14 +218,24 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
this.registrar.setScheduler(this.beanFactory.getBean(ScheduledExecutorService.class));
}
catch (NoUniqueBeanDefinitionException ex2) {
- throw new IllegalStateException("More than one ScheduledExecutorService bean exists within " +
- "the context. Mark one of them as primary; or implement the SchedulingConfigurer " +
- "interface and call ScheduledTaskRegistrar#setScheduler explicitly within the " +
- "configureTasks() callback.", ex);
+ try {
+ this.registrar.setScheduler(
+ this.beanFactory.getBean(DEFAULT_TASK_SCHEDULER_BEAN_NAME, ScheduledExecutorService.class));
+ }
+ catch (NoSuchBeanDefinitionException ex3) {
+ if (logger.isInfoEnabled()) {
+ logger.info("More than one ScheduledExecutorService bean exists within the context, and " +
+ "none is named 'taskScheduler'. Mark one of them as primary or name it 'taskScheduler' " +
+ "(possibly as an alias); or implement the SchedulingConfigurer interface and call " +
+ "ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback: " +
+ ex2.getBeanNamesFound());
+ }
+ }
}
catch (NoSuchBeanDefinitionException ex2) {
- logger.debug("Could not find default ScheduledExecutorService bean", ex);
+ logger.debug("Could not find default ScheduledExecutorService bean", ex2);
// Giving up -> falling back to default scheduler within the registrar...
+ logger.info("No TaskScheduler/ScheduledExecutorService bean found for scheduled processing");
}
}
}
* 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
* }
* }
*
- * 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
+ *