AsyncAnnotationBeanPostProcessor.java 6.0 KB
Newer Older
1
/*
2
 * Copyright 2002-2015 the original author or authors.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.scheduling.annotation;

import java.lang.annotation.Annotation;
20
import java.util.concurrent.Executor;
21

22 23 24
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

25
import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor;
26
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
27
import org.springframework.beans.factory.BeanFactory;
28 29
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
30 31 32 33 34 35 36 37 38 39 40 41 42 43
import org.springframework.core.task.TaskExecutor;
import org.springframework.util.Assert;

/**
 * Bean post-processor that automatically applies asynchronous invocation
 * behavior to any bean that carries the {@link Async} annotation at class or
 * method-level by adding a corresponding {@link AsyncAnnotationAdvisor} to the
 * exposed proxy (either an existing AOP proxy or a newly generated proxy that
 * implements all of the target's interfaces).
 *
 * <p>The {@link TaskExecutor} responsible for the asynchronous execution may
 * be provided as well as the annotation type that indicates a method should be
 * invoked asynchronously. If no annotation type is specified, this post-
 * processor will detect both Spring's {@link Async @Async} annotation as well
44
 * as the EJB 3.1 {@code javax.ejb.Asynchronous} annotation.
45
 *
46 47 48 49 50
 * <p>For methods having a {@code void} return type, any exception thrown
 * during the asynchronous method invocation cannot be accessed by the
 * caller. An {@link AsyncUncaughtExceptionHandler} can be specified to handle
 * these cases.
 *
51 52 53
 * <p>Note: The underlying async advisor applies before existing advisors by default,
 * in order to switch to async execution as early as possible in the invocation chain.
 *
54 55
 * @author Mark Fisher
 * @author Juergen Hoeller
56
 * @author Stephane Nicoll
57 58 59
 * @since 3.0
 * @see Async
 * @see AsyncAnnotationAdvisor
60
 * @see #setBeforeExistingAdvisors
61
 * @see ScheduledAnnotationBeanPostProcessor
62
 */
63
@SuppressWarnings("serial")
64
public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
65

66 67 68 69 70 71 72 73 74 75
	/**
	 * 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.
	 */
	public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME = "taskExecutor";


	protected final Log logger = LogFactory.getLog(getClass());

76 77 78
	private Class<? extends Annotation> asyncAnnotationType;

	private Executor executor;
J
Juergen Hoeller 已提交
79

80
	private AsyncUncaughtExceptionHandler exceptionHandler;
81 82


83 84 85 86
	public AsyncAnnotationBeanPostProcessor() {
		setBeforeExistingAdvisors(true);
	}

J
Juergen Hoeller 已提交
87

88 89 90
	/**
	 * Set the 'async' annotation type to be detected at either class or method
	 * level. By default, both the {@link Async} annotation and the EJB 3.1
91
	 * {@code javax.ejb.Asynchronous} annotation will be detected.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
	 * <p>This setter property exists so that developers can provide their own
	 * (non-Spring-specific) annotation type to indicate that a method (or all
	 * methods of a given class) should be invoked asynchronously.
	 * @param asyncAnnotationType the desired annotation type
	 */
	public void setAsyncAnnotationType(Class<? extends Annotation> asyncAnnotationType) {
		Assert.notNull(asyncAnnotationType, "'asyncAnnotationType' must not be null");
		this.asyncAnnotationType = asyncAnnotationType;
	}

	/**
	 * Set the {@link Executor} to use when invoking methods asynchronously.
	 */
	public void setExecutor(Executor executor) {
		this.executor = executor;
	}

109 110 111
	/**
	 * Set the {@link AsyncUncaughtExceptionHandler} to use to handle uncaught
	 * exceptions thrown by asynchronous method executions.
J
Juergen Hoeller 已提交
112
	 * @since 4.1
113 114 115 116 117
	 */
	public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) {
		this.exceptionHandler = exceptionHandler;
	}

118

119
	@Override
120
	public void setBeanFactory(BeanFactory beanFactory) {
121 122
		super.setBeanFactory(beanFactory);

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
		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, TaskExecutor.class);
				}
				catch (NoSuchBeanDefinitionException ex2) {
					throw new IllegalStateException("More than one TaskExecutor bean exists within the context, " +
							"and none is named 'taskExecutor'. Mark one of them as primary or name it " +
							"'taskExecutor' (possibly as an alias); or specify the AsyncConfigurer interface " +
							"and implement getAsyncExecutor() accordingly.", ex);
				}
			}
			catch (NoSuchBeanDefinitionException ex) {
				logger.debug("Could not find default TaskExecutor bean", ex);
				// Giving up -> falling back to default executor within the advisor...
			}
		}

		AsyncAnnotationAdvisor advisor =  new AsyncAnnotationAdvisor(executorToUse, this.exceptionHandler);
149
		if (this.asyncAnnotationType != null) {
150
			advisor.setAsyncAnnotationType(this.asyncAnnotationType);
151
		}
152 153
		advisor.setBeanFactory(beanFactory);
		this.advisor = advisor;
154 155 156
	}

}