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}