diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java new file mode 100644 index 00000000000..e08a993c98b --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java @@ -0,0 +1,144 @@ +/* + * Copyright 2002-2009 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. + * 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; + +import org.springframework.aop.framework.Advised; +import org.springframework.aop.framework.AopInfrastructureBean; +import org.springframework.aop.framework.ProxyConfig; +import org.springframework.aop.framework.ProxyFactory; +import org.springframework.aop.support.AopUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.core.Ordered; +import org.springframework.core.task.TaskExecutor; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +import java.util.concurrent.Executor; + +/** + * 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). + * + *
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
+ * as the EJB 3.1 javax.ejb.Asynchronous annotation.
+ *
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ * @since 3.0
+ * @see Async
+ * @see AsyncAnnotationAdvisor
+ */
+public class AsyncAnnotationBeanPostProcessor extends ProxyConfig
+ implements BeanPostProcessor, BeanClassLoaderAware, BeanFactoryAware, Ordered {
+
+ private Class extends Annotation> asyncAnnotationType;
+
+ private Executor executor;
+
+ private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
+
+ private AsyncAnnotationAdvisor asyncAnnotationAdvisor;
+
+
+ /**
+ * 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
+ * javax.ejb.Asynchronous annotation will be detected.
+ *
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; + } + + public void setBeanClassLoader(ClassLoader classLoader) { + this.beanClassLoader = classLoader; + } + + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.asyncAnnotationAdvisor = (this.executor != null) ? + new AsyncAnnotationAdvisor(this.executor) : new AsyncAnnotationAdvisor(); + if (this.asyncAnnotationType != null) { + this.asyncAnnotationAdvisor.setAsyncAnnotationType(asyncAnnotationType); + } + } + + public int getOrder() { + // This should run after all other post-processors, so that it can just add + // an advisor to existing proxies rather than double-proxy. + return LOWEST_PRECEDENCE; + } + + + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof AopInfrastructureBean) { + // Ignore AOP infrastructure such as scoped proxies. + return bean; + } + + Class> targetClass = AopUtils.getTargetClass(bean); + if (targetClass == null) { + // Can't do much here. + return bean; + } + + if (AopUtils.canApply(this.asyncAnnotationAdvisor, targetClass)) { + if (bean instanceof Advised) { + ((Advised) bean).addAdvisor(this.asyncAnnotationAdvisor); + return bean; + } + else { + ProxyFactory proxyFactory = new ProxyFactory(bean); + // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig. + proxyFactory.copyFrom(this); + proxyFactory.addAdvisor(this.asyncAnnotationAdvisor); + return proxyFactory.getProxy(this.beanClassLoader); + } + } + else { + // No async proxy needed. + return bean; + } + } + +}