diff --git a/org.springframework.aspects/ivy.xml b/org.springframework.aspects/ivy.xml index a28137f51e1..95e500ed17e 100644 --- a/org.springframework.aspects/ivy.xml +++ b/org.springframework.aspects/ivy.xml @@ -13,6 +13,7 @@ + @@ -32,7 +33,7 @@ - + diff --git a/org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AspectJAsyncConfiguration.java b/org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AspectJAsyncConfiguration.java new file mode 100644 index 00000000000..9f8c83ea73a --- /dev/null +++ b/org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AspectJAsyncConfiguration.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002-2011 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.aspectj; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.AnnotationConfigUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Role; +import org.springframework.scheduling.annotation.AbstractAsyncConfiguration; +import org.springframework.scheduling.annotation.EnableAsync; + +/** + * Enables AspectJ-based asynchronous method execution. + * + * @author Chris Beams + * @since 3.1 + * @see EnableAsync + * @see EnableAsync#mode + */ +@Configuration +public class AspectJAsyncConfiguration extends AbstractAsyncConfiguration { + + @Override + @Bean(name=AnnotationConfigUtils.ASYNC_EXECUTION_ASPECT_BEAN_NAME) + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public AnnotationAsyncExecutionAspect asyncAdvisor() { + AnnotationAsyncExecutionAspect asyncAspect = AnnotationAsyncExecutionAspect.aspectOf(); + if (this.executor != null) { + asyncAspect.setExecutor(this.executor); + } + return asyncAspect; + } + +} diff --git a/org.springframework.aspects/template.mf b/org.springframework.aspects/template.mf index 594bb189ebd..a1cd0a1e916 100644 --- a/org.springframework.aspects/template.mf +++ b/org.springframework.aspects/template.mf @@ -6,6 +6,7 @@ Import-Template: javax.persistence;version="[1.0.0,3.0.0)";resolution:=optional, org.apache.commons.logging.*;version="[1.1.1, 2.0.0)", org.aspectj.*;version=${aj.osgi.range};resolution:=optional, + org.springframework.context.*;version=${spring.osgi.range}, org.springframework.beans.*;version=${spring.osgi.range}, org.springframework.cache.*;version=${spring.osgi.range};resolution:=optional, org.springframework.core.*;version=${spring.osgi.range}, diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java index 2dabee201b3..19f7ded835f 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java @@ -95,6 +95,12 @@ public class AnnotationConfigUtils { public static final String ASYNC_EXECUTION_ASPECT_CLASS_NAME = "org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect"; + /** + * The name of the AspectJ async execution aspect @{@code Configuration} class. + */ + public static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = + "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"; + /** * The bean name of the internally managed cache advisor. */ diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java new file mode 100644 index 00000000000..903deebec96 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java @@ -0,0 +1,73 @@ +/* + * Copyright 2002-2011 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.util.Collection; +import java.util.Map; +import java.util.concurrent.Executor; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportAware; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.util.Assert; + +/** + * Abstract base class providing common structure for enabling Spring's asynchronous + * method execution capability. + * + * @author Chris Beams + * @since 3.1 + * @see EnableAsync + */ +@Configuration +public abstract class AbstractAsyncConfiguration implements ImportAware { + + protected Map enableAsync; + protected Executor executor; + + public void setImportMetadata(AnnotationMetadata importMetadata) { + enableAsync = importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false); + Assert.notNull(enableAsync, + "@EnableAsync is not present on importing class " + + importMetadata.getClassName()); + } + + /** + * The component that will apply async execution advice to beans annotated with + * the async annotation. Subclasses will provide either a BeanPostProcessor in + * the case of proxy-based advice, or an AspectJ aspect if weaving is preferred. + */ + public abstract Object asyncAdvisor(); + + /** + * Collect any {@link AsyncConfigurer} beans through autowiring. + */ + @Autowired(required = false) + void setConfigurers(Collection configurers) { + if (configurers == null || configurers.isEmpty()) { + return; + } + + if (configurers.size() > 1) { + throw new IllegalStateException("only one AsyncConfigurer may exist"); + } + + AsyncConfigurer configurer = configurers.iterator().next(); + this.executor = configurer.getExecutor(); + } +} 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 index 31d4585c265..8f2adc277ba 100644 --- 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 @@ -63,6 +63,12 @@ public class AsyncAnnotationBeanPostProcessor extends ProxyConfig private AsyncAnnotationAdvisor asyncAnnotationAdvisor; + /** + * This should run after all other post-processors, so that it can just add + * an advisor to existing proxies rather than double-proxy. + */ + private int order = Ordered.LOWEST_PRECEDENCE; + /** * Set the 'async' annotation type to be detected at either class or method @@ -98,9 +104,11 @@ public class AsyncAnnotationBeanPostProcessor extends ProxyConfig } 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; + return this.order; + } + + public void setOrder(int order) { + this.order = order; } diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurationSelector.java b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurationSelector.java new file mode 100644 index 00000000000..2b58b49ed10 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurationSelector.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002-2011 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.util.Map; + +import org.springframework.context.annotation.AnnotationConfigUtils; +import org.springframework.context.annotation.ImportSelector; +import org.springframework.context.config.AdviceMode; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.util.Assert; + +/** + * Select which implementation of {@link AbstractAsyncConfiguration} + * should be used based on the value of {@link EnableAsync#mode} on the + * importing @{@link Configuration} class. + * + * @author Chris Beams + * @since 3.1 + * @see EnableAsync + * @see AbstractAsyncConfiguration + * @see ProxyAsyncConfiguration + * @see AnnotationConfigUtils#ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME + */ +public class AsyncConfigurationSelector implements ImportSelector { + + public String[] selectImports(AnnotationMetadata importingClassMetadata) { + Map enableAsync = + importingClassMetadata.getAnnotationAttributes(EnableAsync.class.getName()); + Assert.notNull(enableAsync, + "@EnableAsync is not present on importing class " + + importingClassMetadata.getClassName()); + + switch ((AdviceMode) enableAsync.get("mode")) { + case PROXY: + return new String[] {ProxyAsyncConfiguration.class.getName()}; + case ASPECTJ: + return new String[] {AnnotationConfigUtils.ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME}; + default: + throw new IllegalArgumentException("Unknown AdviceMode " + enableAsync.get("mode")); + } + } + +} diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurer.java b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurer.java new file mode 100644 index 00000000000..a7b026c0dbc --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurer.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002-2011 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.util.concurrent.Executor; + +/** + * Interface to be implemented by @{@link Configuration} classes + * annotated with @{@link EnableAsync} that wish to customize the + * {@link Executor} instance used when processing async method invocations. + * + *

See {@link EnableAsync} for usage examples. + * + * @author Chris Beams + * @since 3.1 + * @see AbstractAsyncConfiguration + * @see EnableAsync + */ +public interface AsyncConfigurer { + + /** + * The {@link Executor} instance to be used when processing async + * method invocations. + */ + Executor getExecutor(); + +} diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java new file mode 100644 index 00000000000..3749bc03838 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java @@ -0,0 +1,121 @@ +/* + * Copyright 2002-2011 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 java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Import; +import org.springframework.context.config.AdviceMode; +import org.springframework.core.Ordered; + +/** + * Enables Spring's asynchronous method execution capability. To be used + * on @{@link Configuration} classes as follows: + * + *

+ * @Configuration
+ * @EnableAsync
+ * public class AppConfig {
+ *     @Bean
+ *     public MyAsyncBean asyncBean() {
+ *         return new MyAsyncBean();
+ *     }
+ * }
+ * + *

The various attributes of the annotation control how advice + * is applied ({@link #mode()}), and if the mode is {@link AdviceMode#PROXY} + * (the default), the other attributes control the behavior of the proxying. + * + *

Note that if the {@linkplain #mode} is set to {@link AdviceMode#ASPECTJ} + * the {@code org.springframework.aspects} module must be present on the classpath. + * + *

By default, a {@link org.springframework.core.task.SimpleAsyncTaskExecutor + * SimpleAsyncTaskExecutor} will be used to process async method invocations. To + * customize this behavior, implement {@link AsyncConfigurer} and + * provide your own {@link java.util.concurrent.Executor Executor} through the + * {@link AsyncConfigurer#getExecutor() getExecutor()} method. + * + *

+ * @Configuration
+ * @EnableAsync
+ * public class AppConfig implements AsyncConfigurer {
+ *
+ *     @Bean
+ *     public MyAsyncBean asyncBean() {
+ *         return new MyAsyncBean();
+ *     }
+ *
+ *     public Executor getExecutor() {
+ *         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ *         executor.setThreadNamePrefix("Custom-");
+ *         executor.initialize();
+ *         return executor;
+ *     }
+ * }
+ * + * @author Chris Beams + * @since 3.1 + * @see Async + * @see AsyncConfigurer + * @see AsyncConfigurationSelector + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Import(AsyncConfigurationSelector.class) +@Documented +public @interface EnableAsync { + + /** + * Indicate 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. + */ + Class annotation() default Annotation.class; + + /** + * Indicate whether class-based (CGLIB) proxies are to be created as opposed + * to standard Java interface-based proxies. The default is {@code false} + * + *

Note: Class-based proxies require the async {@link #annotation()} + * to be defined on the concrete class. Annotations in interfaces will + * not work in that case (they will rather only work with interface-based proxies)! + */ + boolean proxyTargetClass() default false; + + /** + * Indicate how async advice should be applied. + * The default is {@link AdviceMode#PROXY}. + */ + AdviceMode mode() default AdviceMode.PROXY; + + /** + * Indicate the order in which the + * {@link org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor} + * should be applied. Defaults to Order.LOWEST_PRIORITY in order to run + * after all other post-processors, so that it can add an advisor to + * existing proxies rather than double-proxy. + */ + int order() default Ordered.LOWEST_PRECEDENCE; +} diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ProxyAsyncConfiguration.java b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ProxyAsyncConfiguration.java new file mode 100644 index 00000000000..cc0136795b0 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ProxyAsyncConfiguration.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002-2011 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.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.AnnotationConfigUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Role; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.util.Assert; + +/** + * Enables proxy-based asynchronous method execution. + * + * @author Chris Beams + * @since 3.1 + * @see EnableAsync + * @see EnableAsync#mode + */ +@Configuration +public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration { + + @Override + @Bean(name=AnnotationConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME) + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public AsyncAnnotationBeanPostProcessor asyncAdvisor() { + Assert.notNull(enableAsync, "@EnableAsync annotation metadata was not injected"); + + AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor(); + + @SuppressWarnings("unchecked") + Class customAsyncAnnotation = + (Class) enableAsync.get("annotation"); + if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) { + bpp.setAsyncAnnotationType(customAsyncAnnotation); + } + + if (this.executor != null) { + bpp.setExecutor(this.executor); + } + + bpp.setProxyTargetClass((Boolean) enableAsync.get("proxyTargetClass")); + + bpp.setOrder(((Integer) enableAsync.get("order"))); + + return bpp; + } + +} diff --git a/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/EnableAsyncTests.java b/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/EnableAsyncTests.java new file mode 100644 index 00000000000..8a4ca9ce863 --- /dev/null +++ b/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/EnableAsyncTests.java @@ -0,0 +1,211 @@ +/* + * Copyright 2002-2011 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 static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.Executor; + +import org.junit.Test; +import org.springframework.aop.Advisor; +import org.springframework.aop.framework.Advised; +import org.springframework.aop.support.AopUtils; +import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.config.AdviceMode; +import org.springframework.core.Ordered; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +/** + * Tests use of @EnableAsync on @Configuration classes. + * + * @author Chris Beams + * @since 3.1 + */ +public class EnableAsyncTests { + + @Test + public void proxyingOccurs() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(AsyncConfig.class); + ctx.refresh(); + + AsyncBean asyncBean = ctx.getBean(AsyncBean.class); + assertThat(AopUtils.isAopProxy(asyncBean), is(true)); + asyncBean.work(); + } + + + @Configuration + @EnableAsync + static class AsyncConfig { + @Bean + public AsyncBean asyncBean() { + return new AsyncBean(); + } + } + + + static class AsyncBean { + private Thread threadOfExecution; + + @Async + public void work() { + this.threadOfExecution = Thread.currentThread(); + } + + public Thread getThreadOfExecution() { + return threadOfExecution; + } + } + + + @Test + public void asyncProcessorIsOrderedLowestPrecedenceByDefault() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(AsyncConfig.class); + ctx.refresh(); + + AsyncAnnotationBeanPostProcessor bpp = ctx.getBean(AsyncAnnotationBeanPostProcessor.class); + assertThat(bpp.getOrder(), is(Ordered.LOWEST_PRECEDENCE)); + } + + + @Test + public void orderAttributeIsPropagated() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(OrderedAsyncConfig.class); + ctx.refresh(); + + AsyncAnnotationBeanPostProcessor bpp = ctx.getBean(AsyncAnnotationBeanPostProcessor.class); + assertThat(bpp.getOrder(), is(Ordered.HIGHEST_PRECEDENCE)); + } + + + @Configuration + @EnableAsync(order=Ordered.HIGHEST_PRECEDENCE) + static class OrderedAsyncConfig { + @Bean + public AsyncBean asyncBean() { + return new AsyncBean(); + } + } + + + @Test + public void customAsyncAnnotationIsPropagated() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(CustomAsyncAnnotationConfig.class); + ctx.refresh(); + + Object bean = ctx.getBean(CustomAsyncBean.class); + assertTrue(AopUtils.isAopProxy(bean)); + boolean isAsyncAdvised = false; + for (Advisor advisor : ((Advised)bean).getAdvisors()) { + if (advisor instanceof AsyncAnnotationAdvisor) { + isAsyncAdvised = true; + break; + } + } + assertTrue("bean was not async advised as expected", isAsyncAdvised); + } + + + @Configuration + @EnableAsync(annotation=CustomAsync.class) + static class CustomAsyncAnnotationConfig { + @Bean + public CustomAsyncBean asyncBean() { + return new CustomAsyncBean(); + } + } + + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + @interface CustomAsync { + } + + + static class CustomAsyncBean { + @CustomAsync + public void work() { + } + } + + + /** + * Fails with classpath errors on trying to classload AnnotationAsyncExecutionAspect + */ + @Test(expected=BeanDefinitionStoreException.class) + public void aspectModeAspectJAttemptsToRegisterAsyncAspect() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(AspectJAsyncAnnotationConfig.class); + ctx.refresh(); + } + + + @Configuration + @EnableAsync(mode=AdviceMode.ASPECTJ) + static class AspectJAsyncAnnotationConfig { + @Bean + public AsyncBean asyncBean() { + return new AsyncBean(); + } + } + + + @Test + public void customExecutorIsPropagated() throws InterruptedException { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(CustomExecutorAsyncConfig.class); + ctx.refresh(); + + AsyncBean asyncBean = ctx.getBean(AsyncBean.class); + asyncBean.work(); + Thread.sleep(500); + ctx.close(); + assertThat(asyncBean.getThreadOfExecution().getName(), startsWith("Custom-")); + } + + + @Configuration + @EnableAsync + static class CustomExecutorAsyncConfig implements AsyncConfigurer { + @Bean + public AsyncBean asyncBean() { + return new AsyncBean(); + } + + public Executor getExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setThreadNamePrefix("Custom-"); + executor.initialize(); + return executor; + } + + } +}