From 8c9b64c9481b3c2970519b1dcf91f7b67767d8b0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Oct 2010 20:50:23 +0000 Subject: [PATCH] added mode="proxy"/"aspectj" and proxy-target-class options to task namespace; switched to concise names for async aspects --- ...ect.aj => AbstractAsyncExecutionAspect.aj} | 19 +- ...t.aj => AnnotationAsyncExecutionAspect.aj} | 2 +- .../aspectj/AbstractTransactionAspect.aj | 186 +++++++++--------- .../aspectj/AnnotationTransactionAspect.aj | 151 +++++++------- .../src/main/resources/META-INF/aop.xml | 35 ++-- ... AnnotationAsyncExecutionAspectTests.java} | 8 +- .../AnnotationDrivenBeanDefinitionParser.java | 62 ++++-- .../scheduling/config/spring-task-3.0.xsd | 33 ++++ ...AsyncAnnotationBeanPostProcessorTests.java | 24 ++- .../annotation/taskNamespaceTests.xml | 22 +++ 10 files changed, 324 insertions(+), 218 deletions(-) rename org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/{AbstractAsynchronousExecutionAspect.aj => AbstractAsyncExecutionAspect.aj} (77%) rename org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/{AnnotationDrivenAsynchronousExecutionAspect.aj => AnnotationAsyncExecutionAspect.aj} (92%) rename org.springframework.aspects/src/test/java/org/springframework/scheduling/aspectj/{AnnotationDrivenAsynchronousExecutionAspectTests.java => AnnotationAsyncExecutionAspectTests.java} (92%) create mode 100644 org.springframework.context/src/test/java/org/springframework/scheduling/annotation/taskNamespaceTests.xml diff --git a/org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsynchronousExecutionAspect.aj b/org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj similarity index 77% rename from org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsynchronousExecutionAspect.aj rename to org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj index ba2db4aec33..bb54a9c5b79 100644 --- a/org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsynchronousExecutionAspect.aj +++ b/org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj @@ -28,22 +28,18 @@ import org.springframework.core.task.support.TaskExecutorAdapter; /** * Abstract aspect that routes selected methods asynchronously. * - *

This aspect, by default, uses {@link SimpleAsyncTaskExecutor} to route - * method execution. However, you may inject it with any implementation of - * {@link Executor} to override the default. + *

This aspect needs to be injected with an implementation of + * {@link Executor} to activate it for a specific thread pool. + * Otherwise it will simply delegate all calls synchronously. * * @author Ramnivas Laddad + * @author Juergen Hoeller * @since 3.0.5 */ -public abstract aspect AbstractAsynchronousExecutionAspect { +public abstract aspect AbstractAsyncExecutionAspect { private AsyncTaskExecutor asyncExecutor; - public AbstractAsynchronousExecutionAspect() { - // Set default executor, which may be replaced by calling setExecutor. - setExecutor(new SimpleAsyncTaskExecutor()); - } - public void setExecutor(Executor executor) { if (executor instanceof AsyncTaskExecutor) { this.asyncExecutor = (AsyncTaskExecutor) executor; @@ -54,6 +50,9 @@ public abstract aspect AbstractAsynchronousExecutionAspect { } Object around() : asyncMethod() { + if (this.asyncExecutor == null) { + return proceed(); + } Callable callable = new Callable() { public Object call() throws Exception { Object result = proceed(); @@ -62,7 +61,7 @@ public abstract aspect AbstractAsynchronousExecutionAspect { } return null; }}; - Future result = asyncExecutor.submit(callable); + Future result = this.asyncExecutor.submit(callable); if (Future.class.isAssignableFrom(((MethodSignature) thisJoinPointStaticPart.getSignature()).getReturnType())) { return result; } diff --git a/org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationDrivenAsynchronousExecutionAspect.aj b/org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj similarity index 92% rename from org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationDrivenAsynchronousExecutionAspect.aj rename to org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj index 87e15b7e524..ee37fd25e8a 100644 --- a/org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationDrivenAsynchronousExecutionAspect.aj +++ b/org.springframework.aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj @@ -33,7 +33,7 @@ import org.springframework.scheduling.annotation.Async; * @author Ramnivas Laddad * @since 3.0.5 */ -public aspect AnnotationDrivenAsynchronousExecutionAspect extends AbstractAsynchronousExecutionAspect { +public aspect AnnotationAsyncExecutionAspect extends AbstractAsyncExecutionAspect { private pointcut asyncMarkedMethod() : execution(@Async (void || Future+) *(..)); diff --git a/org.springframework.aspects/src/main/java/org/springframework/transaction/aspectj/AbstractTransactionAspect.aj b/org.springframework.aspects/src/main/java/org/springframework/transaction/aspectj/AbstractTransactionAspect.aj index 655b492f769..985ac62fb41 100644 --- a/org.springframework.aspects/src/main/java/org/springframework/transaction/aspectj/AbstractTransactionAspect.aj +++ b/org.springframework.aspects/src/main/java/org/springframework/transaction/aspectj/AbstractTransactionAspect.aj @@ -1,93 +1,93 @@ -/* - * Copyright 2002-2007 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.transaction.aspectj; - -import java.lang.reflect.Method; - -import org.aspectj.lang.annotation.SuppressAjWarnings; -import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.transaction.interceptor.TransactionAspectSupport; -import org.springframework.transaction.interceptor.TransactionAttributeSource; - -/** - * Abstract superaspect for AspectJ transaction aspects. Concrete - * subaspects will implement the transactionalMethodExecution() - * pointcut using a strategy such as Java 5 annotations. - * - *

Suitable for use inside or outside the Spring IoC container. - * Set the "transactionManager" property appropriately, allowing - * use of any transaction implementation supported by Spring. - * - *

NB: If a method implements an interface that is itself - * transactionally annotated, the relevant Spring transaction attribute - * will not be resolved. This behavior will vary from that of Spring AOP - * if proxying an interface (but not when proxying a class). We recommend that - * transaction annotations should be added to classes, rather than business - * interfaces, as they are an implementation detail rather than a contract - * specification validation. - * - * @author Rod Johnson - * @author Ramnivas Laddad - * @since 2.0 - */ -public abstract aspect AbstractTransactionAspect extends TransactionAspectSupport { - - /** - * Construct object using the given transaction metadata retrieval strategy. - * @param tas TransactionAttributeSource implementation, retrieving Spring - * transaction metadata for each joinpoint. Write the subclass to pass in null - * if it's intended to be configured by Setter Injection. - */ - protected AbstractTransactionAspect(TransactionAttributeSource tas) { - setTransactionAttributeSource(tas); - } - - @SuppressAjWarnings("adviceDidNotMatch") - before(Object txObject) : transactionalMethodExecution(txObject) { - MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature(); - Method method = methodSignature.getMethod(); - TransactionInfo txInfo = createTransactionIfNecessary(method, txObject.getClass()); - } - - @SuppressAjWarnings("adviceDidNotMatch") - after(Object txObject) throwing(Throwable t) : transactionalMethodExecution(txObject) { - try { - completeTransactionAfterThrowing(TransactionAspectSupport.currentTransactionInfo(), t); - } - catch (Throwable t2) { - logger.error("Failed to close transaction after throwing in a transactional method", t2); - } - } - - @SuppressAjWarnings("adviceDidNotMatch") - after(Object txObject) returning() : transactionalMethodExecution(txObject) { - commitTransactionAfterReturning(TransactionAspectSupport.currentTransactionInfo()); - } - - @SuppressAjWarnings("adviceDidNotMatch") - after(Object txObject) : transactionalMethodExecution(txObject) { - cleanupTransactionInfo(TransactionAspectSupport.currentTransactionInfo()); - } - - /** - * Concrete subaspects must implement this pointcut, to identify - * transactional methods. For each selected joinpoint, TransactionMetadata - * will be retrieved using Spring's TransactionAttributeSource interface. - */ - protected abstract pointcut transactionalMethodExecution(Object txObject); - -} +/* + * Copyright 2002-2010 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.transaction.aspectj; + +import java.lang.reflect.Method; + +import org.aspectj.lang.annotation.SuppressAjWarnings; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.transaction.interceptor.TransactionAspectSupport; +import org.springframework.transaction.interceptor.TransactionAttributeSource; + +/** + * Abstract superaspect for AspectJ transaction aspects. Concrete + * subaspects will implement the transactionalMethodExecution() + * pointcut using a strategy such as Java 5 annotations. + * + *

Suitable for use inside or outside the Spring IoC container. + * Set the "transactionManager" property appropriately, allowing + * use of any transaction implementation supported by Spring. + * + *

NB: If a method implements an interface that is itself + * transactionally annotated, the relevant Spring transaction attribute + * will not be resolved. This behavior will vary from that of Spring AOP + * if proxying an interface (but not when proxying a class). We recommend that + * transaction annotations should be added to classes, rather than business + * interfaces, as they are an implementation detail rather than a contract + * specification validation. + * + * @author Rod Johnson + * @author Ramnivas Laddad + * @since 2.0 + */ +public abstract aspect AbstractTransactionAspect extends TransactionAspectSupport { + + /** + * Construct object using the given transaction metadata retrieval strategy. + * @param tas TransactionAttributeSource implementation, retrieving Spring + * transaction metadata for each joinpoint. Write the subclass to pass in null + * if it's intended to be configured by Setter Injection. + */ + protected AbstractTransactionAspect(TransactionAttributeSource tas) { + setTransactionAttributeSource(tas); + } + + @SuppressAjWarnings("adviceDidNotMatch") + before(Object txObject) : transactionalMethodExecution(txObject) { + MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature(); + Method method = methodSignature.getMethod(); + TransactionInfo txInfo = createTransactionIfNecessary(method, txObject.getClass()); + } + + @SuppressAjWarnings("adviceDidNotMatch") + after(Object txObject) throwing(Throwable t) : transactionalMethodExecution(txObject) { + try { + completeTransactionAfterThrowing(TransactionAspectSupport.currentTransactionInfo(), t); + } + catch (Throwable t2) { + logger.error("Failed to close transaction after throwing in a transactional method", t2); + } + } + + @SuppressAjWarnings("adviceDidNotMatch") + after(Object txObject) returning() : transactionalMethodExecution(txObject) { + commitTransactionAfterReturning(TransactionAspectSupport.currentTransactionInfo()); + } + + @SuppressAjWarnings("adviceDidNotMatch") + after(Object txObject) : transactionalMethodExecution(txObject) { + cleanupTransactionInfo(TransactionAspectSupport.currentTransactionInfo()); + } + + /** + * Concrete subaspects must implement this pointcut, to identify + * transactional methods. For each selected joinpoint, TransactionMetadata + * will be retrieved using Spring's TransactionAttributeSource interface. + */ + protected abstract pointcut transactionalMethodExecution(Object txObject); + +} diff --git a/org.springframework.aspects/src/main/java/org/springframework/transaction/aspectj/AnnotationTransactionAspect.aj b/org.springframework.aspects/src/main/java/org/springframework/transaction/aspectj/AnnotationTransactionAspect.aj index 1282b660b73..d6f887b259a 100644 --- a/org.springframework.aspects/src/main/java/org/springframework/transaction/aspectj/AnnotationTransactionAspect.aj +++ b/org.springframework.aspects/src/main/java/org/springframework/transaction/aspectj/AnnotationTransactionAspect.aj @@ -1,76 +1,75 @@ -/* - * Copyright 2002-2008 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.transaction.aspectj; - -import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource; -import org.springframework.transaction.annotation.Transactional; - -/** - * Concrete AspectJ transaction aspect using Spring Transactional annotation - * for JDK 1.5+. - * - *

When using this aspect, you must annotate the implementation class - * (and/or methods within that class), not the interface (if any) that - * the class implements. AspectJ follows Java's rule that annotations on - * interfaces are not inherited. - * - *

A @Transactional annotation on a class specifies the default transaction - * semantics for the execution of any public operation in the class. - * - *

A @Transactional annotation on a method within the class overrides the - * default transaction semantics given by the class annotation (if present). - * Any method may be annotated (regardless of visibility). - * Annotating non-public methods directly is the only way - * to get transaction demarcation for the execution of such operations. - * - * @author Rod Johnson - * @author Ramnivas Laddad - * @author Adrian Colyer - * @since 2.0 - * @see org.springframework.transaction.annotation.Transactional - */ -public aspect AnnotationTransactionAspect extends AbstractTransactionAspect { - - public AnnotationTransactionAspect() { - super(new AnnotationTransactionAttributeSource(false)); - } - - /** - * Matches the execution of any public method in a type with the - * Transactional annotation, or any subtype of a type with the - * Transactional annotation. - */ - private pointcut executionOfAnyPublicMethodInAtTransactionalType() : - execution(public * ((@Transactional *)+).*(..)) && @this(Transactional); - - /** - * Matches the execution of any method with the - * Transactional annotation. - */ - private pointcut executionOfTransactionalMethod() : - execution(* *(..)) && @annotation(Transactional); - - /** - * Definition of pointcut from super aspect - matched join points - * will have Spring transaction management applied. - */ - protected pointcut transactionalMethodExecution(Object txObject) : - (executionOfAnyPublicMethodInAtTransactionalType() - || executionOfTransactionalMethod() ) - && this(txObject); - -} +/* + * Copyright 2002-2010 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.transaction.aspectj; + +import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource; +import org.springframework.transaction.annotation.Transactional; + +/** + * Concrete AspectJ transaction aspect using Spring's @Transactional annotation. + * + *

When using this aspect, you must annotate the implementation class + * (and/or methods within that class), not the interface (if any) that + * the class implements. AspectJ follows Java's rule that annotations on + * interfaces are not inherited. + * + *

A @Transactional annotation on a class specifies the default transaction + * semantics for the execution of any public operation in the class. + * + *

A @Transactional annotation on a method within the class overrides the + * default transaction semantics given by the class annotation (if present). + * Any method may be annotated (regardless of visibility). + * Annotating non-public methods directly is the only way + * to get transaction demarcation for the execution of such operations. + * + * @author Rod Johnson + * @author Ramnivas Laddad + * @author Adrian Colyer + * @since 2.0 + * @see org.springframework.transaction.annotation.Transactional + */ +public aspect AnnotationTransactionAspect extends AbstractTransactionAspect { + + public AnnotationTransactionAspect() { + super(new AnnotationTransactionAttributeSource(false)); + } + + /** + * Matches the execution of any public method in a type with the + * Transactional annotation, or any subtype of a type with the + * Transactional annotation. + */ + private pointcut executionOfAnyPublicMethodInAtTransactionalType() : + execution(public * ((@Transactional *)+).*(..)) && @this(Transactional); + + /** + * Matches the execution of any method with the + * Transactional annotation. + */ + private pointcut executionOfTransactionalMethod() : + execution(* *(..)) && @annotation(Transactional); + + /** + * Definition of pointcut from super aspect - matched join points + * will have Spring transaction management applied. + */ + protected pointcut transactionalMethodExecution(Object txObject) : + (executionOfAnyPublicMethodInAtTransactionalType() + || executionOfTransactionalMethod() ) + && this(txObject); + +} diff --git a/org.springframework.aspects/src/main/resources/META-INF/aop.xml b/org.springframework.aspects/src/main/resources/META-INF/aop.xml index 78a97e6525f..d21ef49b9b5 100644 --- a/org.springframework.aspects/src/main/resources/META-INF/aop.xml +++ b/org.springframework.aspects/src/main/resources/META-INF/aop.xml @@ -1,17 +1,18 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/org.springframework.aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationDrivenAsynchronousExecutionAspectTests.java b/org.springframework.aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspectTests.java similarity index 92% rename from org.springframework.aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationDrivenAsynchronousExecutionAspectTests.java rename to org.springframework.aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspectTests.java index bb66c8958ba..f8d55a43d24 100644 --- a/org.springframework.aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationDrivenAsynchronousExecutionAspectTests.java +++ b/org.springframework.aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspectTests.java @@ -33,7 +33,7 @@ import org.springframework.scheduling.annotation.AsyncResult; /** * @author Ramnivas Laddad */ -public class AnnotationDrivenAsynchronousExecutionAspectTests { +public class AnnotationAsyncExecutionAspectTests { private static final long WAIT_TIME = 1000; //milli seconds @@ -42,7 +42,7 @@ public class AnnotationDrivenAsynchronousExecutionAspectTests { @Before public void setUp() { executor = new CountingExecutor(); - AnnotationDrivenAsynchronousExecutionAspect.aspectOf().setExecutor(executor); + AnnotationAsyncExecutionAspect.aspectOf().setExecutor(executor); } @Test @@ -150,7 +150,7 @@ public class AnnotationDrivenAsynchronousExecutionAspectTests { // We need to keep this commented out, otherwise there will be a compile-time error. // Please uncomment and re-comment this periodically to check that the compiler // produces an error message due to the 'declare error' statement - // in AnnotationDrivenAsynchronousExecutionAspect + // in AnnotationAsyncExecutionAspect // @Async public int getInt() { // return 0; // } @@ -164,7 +164,7 @@ public class AnnotationDrivenAsynchronousExecutionAspectTests { counter++; } - // Manually check that there is a warning from the 'declare warning' statement in AnnotationDrivenAsynchronousExecutionAspect + // Manually check that there is a warning from the 'declare warning' statement in AnnotationAsynchExecutionAspect public int return5() { return 5; } diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/config/AnnotationDrivenBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/scheduling/config/AnnotationDrivenBeanDefinitionParser.java index 6480c5c0d7a..41275f1109e 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/config/AnnotationDrivenBeanDefinitionParser.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/config/AnnotationDrivenBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -18,6 +18,7 @@ package org.springframework.scheduling.config; import org.w3c.dom.Element; +import org.springframework.aop.config.AopNamespaceUtils; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.parsing.BeanComponentDefinition; @@ -32,16 +33,26 @@ import org.springframework.util.StringUtils; * Parser for the 'annotation-driven' element of the 'task' namespace. * * @author Mark Fisher + * @author Juergen Hoeller * @since 3.0 */ public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { /** - * The bean name of the internally managed async annotation processor. + * The bean name of the internally managed async annotation processor (mode="proxy"). */ public static final String ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.scheduling.annotation.internalAsyncAnnotationProcessor"; + /** + * The bean name of the internally managed transaction aspect (mode="aspectj"). + */ + public static final String ASYNC_EXECUTION_ASPECT_BEAN_NAME = + "org.springframework.transaction.config.internalTransactionAspect"; + + private static final String ASYNC_EXECUTION_ASPECT_CLASS_NAME = + "org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect"; + /** * The bean name of the internally managed scheduled annotation processor. */ @@ -58,20 +69,33 @@ public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParse // Nest the concrete post-processor bean in the surrounding component. BeanDefinitionRegistry registry = parserContext.getRegistry(); - if (registry.containsBeanDefinition(ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)) { - parserContext.getReaderContext().error( - "Only one AsyncAnnotationBeanPostProcessor may exist within the context.", source); + + String mode = element.getAttribute("mode"); + if ("aspectj".equals(mode)) { + // mode="aspectj" + registerAsyncExecutionAspect(element, parserContext); } else { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( - "org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor"); - builder.getRawBeanDefinition().setSource(source); - String executor = element.getAttribute("executor"); - if (StringUtils.hasText(executor)) { - builder.addPropertyReference("executor", executor); + // mode="proxy" + if (registry.containsBeanDefinition(ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)) { + parserContext.getReaderContext().error( + "Only one AsyncAnnotationBeanPostProcessor may exist within the context.", source); + } + else { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( + "org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor"); + builder.getRawBeanDefinition().setSource(source); + String executor = element.getAttribute("executor"); + if (StringUtils.hasText(executor)) { + builder.addPropertyReference("executor", executor); + } + if (Boolean.valueOf(element.getAttribute(AopNamespaceUtils.PROXY_TARGET_CLASS_ATTRIBUTE))) { + builder.addPropertyValue("proxyTargetClass", true); + } + registerPostProcessor(parserContext, builder, ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME); } - registerPostProcessor(parserContext, builder, ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME); } + if (registry.containsBeanDefinition(SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)) { parserContext.getReaderContext().error( "Only one ScheduledAnnotationBeanPostProcessor may exist within the context.", source); @@ -93,6 +117,20 @@ public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParse return null; } + private void registerAsyncExecutionAspect(Element element, ParserContext parserContext) { + if (!parserContext.getRegistry().containsBeanDefinition(ASYNC_EXECUTION_ASPECT_BEAN_NAME)) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( + ASYNC_EXECUTION_ASPECT_CLASS_NAME); + builder.setFactoryMethod("aspectOf"); + String executor = element.getAttribute("executor"); + if (StringUtils.hasText(executor)) { + builder.addPropertyReference("executor", executor); + } + parserContext.registerBeanComponent( + new BeanComponentDefinition(builder.getBeanDefinition(), ASYNC_EXECUTION_ASPECT_BEAN_NAME)); + } + } + private static void registerPostProcessor( ParserContext parserContext, BeanDefinitionBuilder builder, String beanName) { diff --git a/org.springframework.context/src/main/resources/org/springframework/scheduling/config/spring-task-3.0.xsd b/org.springframework.context/src/main/resources/org/springframework/scheduling/config/spring-task-3.0.xsd index ce51a1a4f7e..7e484d0d013 100644 --- a/org.springframework.context/src/main/resources/org/springframework/scheduling/config/spring-task-3.0.xsd +++ b/org.springframework.context/src/main/resources/org/springframework/scheduling/config/spring-task-3.0.xsd @@ -44,6 +44,39 @@ ]]> + + + + + + + + + + + + + + + + diff --git a/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java b/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java index f0d425cfce1..be6ef187d25 100644 --- a/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java +++ b/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -16,22 +16,23 @@ package org.springframework.scheduling.annotation; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertTrue; - import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static org.junit.Assert.*; import org.junit.Test; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.support.GenericXmlApplicationContext; import org.springframework.context.support.StaticApplicationContext; +import org.springframework.core.io.ClassPathResource; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; /** * @author Mark Fisher + * @author Juergen Hoeller */ public class AsyncAnnotationBeanPostProcessorTests { @@ -85,6 +86,19 @@ public class AsyncAnnotationBeanPostProcessorTests { context.close(); } + @Test + public void configuredThroughNamespace() { + GenericXmlApplicationContext context = new GenericXmlApplicationContext(); + context.load(new ClassPathResource("taskNamespaceTests.xml", getClass())); + context.refresh(); + ITestBean testBean = (ITestBean) context.getBean("target"); + testBean.test(); + testBean.await(3000); + Thread asyncThread = testBean.getThread(); + assertTrue(asyncThread.getName().startsWith("testExecutor")); + context.close(); + } + private static interface ITestBean { @@ -96,7 +110,7 @@ public class AsyncAnnotationBeanPostProcessorTests { } - private static class TestBean implements ITestBean { + public static class TestBean implements ITestBean { private Thread thread; diff --git a/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/taskNamespaceTests.xml b/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/taskNamespaceTests.xml new file mode 100644 index 00000000000..9f613201377 --- /dev/null +++ b/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/taskNamespaceTests.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + +