diff --git a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj index cbd592f5042eca9bac7dba96a6f577e7db1407e2..6df1662a19f00308c33ee174ef753f6a57283252 100644 --- a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 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. @@ -23,15 +23,15 @@ import org.springframework.core.annotation.AnnotationUtils; import org.springframework.scheduling.annotation.Async; /** - * Aspect to route methods based on the {@link Async} annotation. + * Aspect to route methods based on Spring's {@link Async} annotation. * - *
This aspect routes methods marked with the {@link Async} annotation - * as well as methods in classes marked with the same. Any method expected - * to be routed asynchronously must return either {@code void}, {@link Future}, - * or a subtype of {@link Future}. This aspect, therefore, will produce - * a compile-time error for methods that violate this constraint on the return type. - * If, however, a class marked with {@code @Async} contains a method that violates this - * constraint, it produces only a warning. + *
This aspect routes methods marked with the {@link Async} annotation as well as methods + * in classes marked with the same. Any method expected to be routed asynchronously must + * return either {@code void}, {@link Future}, or a subtype of {@link Future} (in particular, + * Spring's {@link org.springframework.util.concurrent.ListenableFuture}). This aspect, + * therefore, will produce a compile-time error for methods that violate this constraint + * on the return type. If, however, a class marked with {@code @Async} contains a method + * that violates this constraint, it produces only a warning. * * @author Ramnivas Laddad * @author Chris Beams @@ -39,42 +39,41 @@ import org.springframework.scheduling.annotation.Async; */ public aspect AnnotationAsyncExecutionAspect extends AbstractAsyncExecutionAspect { - private pointcut asyncMarkedMethod() - : execution(@Async (void || Future+) *(..)); + private pointcut asyncMarkedMethod() : execution(@Async (void || Future+) *(..)); - private pointcut asyncTypeMarkedMethod() - : execution((void || Future+) (@Async *).*(..)); + private pointcut asyncTypeMarkedMethod() : execution((void || Future+) (@Async *).*(..)); public pointcut asyncMethod() : asyncMarkedMethod() || asyncTypeMarkedMethod(); + /** - * {@inheritDoc} - *
This implementation inspects the given method and its declaring class for the
- * {@code @Async} annotation, returning the qualifier value expressed by
- * {@link Async#value()}. If {@code @Async} is specified at both the method and class level, the
- * method's {@code #value} takes precedence (even if empty string, indicating that
- * the default executor should be used preferentially).
+ * This implementation inspects the given method and its declaring class for the
+ * {@code @Async} annotation, returning the qualifier value expressed by {@link Async#value()}.
+ * If {@code @Async} is specified at both the method and class level, the method's
+ * {@code #value} takes precedence (even if empty string, indicating that the default
+ * executor should be used preferentially).
* @return the qualifier if specified, otherwise empty string indicating that the
- * {@linkplain #setExecutor(Executor) default executor} should be used
+ * {@linkplain #setExecutor default executor} should be used
* @see #determineAsyncExecutor(Method)
*/
@Override
protected String getExecutorQualifier(Method method) {
- // maintainer's note: changes made here should also be made in
+ // Maintainer's note: changes made here should also be made in
// AnnotationAsyncExecutionInterceptor#getExecutorQualifier
Async async = AnnotationUtils.findAnnotation(method, Async.class);
if (async == null) {
async = AnnotationUtils.findAnnotation(method.getDeclaringClass(), Async.class);
}
- return async == null ? null : async.value();
+ return (async != null ? async.value() : null);
}
+
declare error:
- execution(@Async !(void||Future) *(..)):
+ execution(@Async !(void || Future+) *(..)):
"Only methods that return void or Future may have an @Async annotation";
declare warning:
- execution(!(void||Future) (@Async *).*(..)):
+ execution(!(void || Future+) (@Async *).*(..)):
"Methods in a class marked with @Async that do not return void or Future will " +
"be routed synchronously";
diff --git a/spring-aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspectTests.java b/spring-aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspectTests.java
index 5610d7ba1573841f304fc26bb254ce6a3dcb9c85..f9277e379066a81ee92602128914c954f7feae58 100644
--- a/spring-aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspectTests.java
+++ b/spring-aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspectTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -35,6 +35,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.tests.Assume;
import org.springframework.tests.TestGroup;
import org.springframework.util.ReflectionUtils;
+import org.springframework.util.concurrent.ListenableFuture;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.Matchers.not;
@@ -50,9 +51,10 @@ public class AnnotationAsyncExecutionAspectTests {
private static final long WAIT_TIME = 1000; //milliseconds
+ private final AsyncUncaughtExceptionHandler defaultExceptionHandler = new SimpleAsyncUncaughtExceptionHandler();
+
private CountingExecutor executor;
- private AsyncUncaughtExceptionHandler defaultExceptionHandler = new SimpleAsyncUncaughtExceptionHandler();
@Before
public void setUp() {
@@ -62,6 +64,7 @@ public class AnnotationAsyncExecutionAspectTests {
AnnotationAsyncExecutionAspect.aspectOf().setExecutor(executor);
}
+
@Test
public void asyncMethodGetsRoutedAsynchronously() {
ClassWithoutAsyncAnnotation obj = new ClassWithoutAsyncAnnotation();
@@ -184,7 +187,9 @@ public class AnnotationAsyncExecutionAspectTests {
@SuppressWarnings("serial")
private static class CountingExecutor extends SimpleAsyncTaskExecutor {
+
int submitStartCounter;
+
int submitCompleteCounter;
@Override
@@ -209,6 +214,7 @@ public class AnnotationAsyncExecutionAspectTests {
static class ClassWithoutAsyncAnnotation {
+
int counter;
@Async public void incrementAsync() {
@@ -239,6 +245,7 @@ public class AnnotationAsyncExecutionAspectTests {
@Async
static class ClassWithAsyncAnnotation {
+
int counter;
public void increment() {
@@ -261,17 +268,19 @@ public class AnnotationAsyncExecutionAspectTests {
static class ClassWithQualifiedAsyncMethods {
+
@Async
public Future