001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.scheduling.annotation;
018
019import java.lang.annotation.Annotation;
020import java.util.concurrent.Executor;
021import java.util.function.Supplier;
022
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025
026import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor;
027import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
028import org.springframework.beans.factory.BeanFactory;
029import org.springframework.core.task.TaskExecutor;
030import org.springframework.lang.Nullable;
031import org.springframework.util.Assert;
032import org.springframework.util.function.SingletonSupplier;
033
034/**
035 * Bean post-processor that automatically applies asynchronous invocation
036 * behavior to any bean that carries the {@link Async} annotation at class or
037 * method-level by adding a corresponding {@link AsyncAnnotationAdvisor} to the
038 * exposed proxy (either an existing AOP proxy or a newly generated proxy that
039 * implements all of the target's interfaces).
040 *
041 * <p>The {@link TaskExecutor} responsible for the asynchronous execution may
042 * be provided as well as the annotation type that indicates a method should be
043 * invoked asynchronously. If no annotation type is specified, this post-
044 * processor will detect both Spring's {@link Async @Async} annotation as well
045 * as the EJB 3.1 {@code javax.ejb.Asynchronous} annotation.
046 *
047 * <p>For methods having a {@code void} return type, any exception thrown
048 * during the asynchronous method invocation cannot be accessed by the
049 * caller. An {@link AsyncUncaughtExceptionHandler} can be specified to handle
050 * these cases.
051 *
052 * <p>Note: The underlying async advisor applies before existing advisors by default,
053 * in order to switch to async execution as early as possible in the invocation chain.
054 *
055 * @author Mark Fisher
056 * @author Juergen Hoeller
057 * @author Stephane Nicoll
058 * @since 3.0
059 * @see Async
060 * @see AsyncAnnotationAdvisor
061 * @see #setBeforeExistingAdvisors
062 * @see ScheduledAnnotationBeanPostProcessor
063 */
064@SuppressWarnings("serial")
065public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
066
067        /**
068         * The default name of the {@link TaskExecutor} bean to pick up: "taskExecutor".
069         * <p>Note that the initial lookup happens by type; this is just the fallback
070         * in case of multiple executor beans found in the context.
071         * @since 4.2
072         * @see AnnotationAsyncExecutionInterceptor#DEFAULT_TASK_EXECUTOR_BEAN_NAME
073         */
074        public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME =
075                        AnnotationAsyncExecutionInterceptor.DEFAULT_TASK_EXECUTOR_BEAN_NAME;
076
077
078        protected final Log logger = LogFactory.getLog(getClass());
079
080        @Nullable
081        private Supplier<Executor> executor;
082
083        @Nullable
084        private Supplier<AsyncUncaughtExceptionHandler> exceptionHandler;
085
086        @Nullable
087        private Class<? extends Annotation> asyncAnnotationType;
088
089
090
091        public AsyncAnnotationBeanPostProcessor() {
092                setBeforeExistingAdvisors(true);
093        }
094
095
096        /**
097         * Configure this post-processor with the given executor and exception handler suppliers,
098         * applying the corresponding default if a supplier is not resolvable.
099         * @since 5.1
100         */
101        public void configure(
102                        @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
103
104                this.executor = executor;
105                this.exceptionHandler = exceptionHandler;
106        }
107
108        /**
109         * Set the {@link Executor} to use when invoking methods asynchronously.
110         * <p>If not specified, default executor resolution will apply: searching for a
111         * unique {@link TaskExecutor} bean in the context, or for an {@link Executor}
112         * bean named "taskExecutor" otherwise. If neither of the two is resolvable,
113         * a local default executor will be created within the interceptor.
114         * @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory)
115         * @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME
116         */
117        public void setExecutor(Executor executor) {
118                this.executor = SingletonSupplier.of(executor);
119        }
120
121        /**
122         * Set the {@link AsyncUncaughtExceptionHandler} to use to handle uncaught
123         * exceptions thrown by asynchronous method executions.
124         * @since 4.1
125         */
126        public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) {
127                this.exceptionHandler = SingletonSupplier.of(exceptionHandler);
128        }
129
130        /**
131         * Set the 'async' annotation type to be detected at either class or method
132         * level. By default, both the {@link Async} annotation and the EJB 3.1
133         * {@code javax.ejb.Asynchronous} annotation will be detected.
134         * <p>This setter property exists so that developers can provide their own
135         * (non-Spring-specific) annotation type to indicate that a method (or all
136         * methods of a given class) should be invoked asynchronously.
137         * @param asyncAnnotationType the desired annotation type
138         */
139        public void setAsyncAnnotationType(Class<? extends Annotation> asyncAnnotationType) {
140                Assert.notNull(asyncAnnotationType, "'asyncAnnotationType' must not be null");
141                this.asyncAnnotationType = asyncAnnotationType;
142        }
143
144
145        @Override
146        public void setBeanFactory(BeanFactory beanFactory) {
147                super.setBeanFactory(beanFactory);
148
149                AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
150                if (this.asyncAnnotationType != null) {
151                        advisor.setAsyncAnnotationType(this.asyncAnnotationType);
152                }
153                advisor.setBeanFactory(beanFactory);
154                this.advisor = advisor;
155        }
156
157}