Skip to content

Commit dd4bc63

Browse files
committed
Common code in AsyncExecutionAspectSupport allows for CompletableFuture processing with AspectJ as well
Issue: SPR-13128
1 parent e134e3e commit dd4bc63

File tree

3 files changed

+70
-68
lines changed

3 files changed

+70
-68
lines changed

spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@
1818

1919
import java.lang.reflect.Method;
2020
import java.util.Map;
21+
import java.util.concurrent.Callable;
22+
import java.util.concurrent.CompletableFuture;
23+
import java.util.concurrent.CompletionException;
2124
import java.util.concurrent.ConcurrentHashMap;
2225
import java.util.concurrent.Executor;
2326
import java.util.concurrent.Future;
27+
import java.util.function.Supplier;
2428

2529
import org.apache.commons.logging.Log;
2630
import org.apache.commons.logging.LogFactory;
@@ -31,9 +35,12 @@
3135
import org.springframework.core.task.AsyncListenableTaskExecutor;
3236
import org.springframework.core.task.AsyncTaskExecutor;
3337
import org.springframework.core.task.support.TaskExecutorAdapter;
38+
import org.springframework.lang.UsesJava8;
3439
import org.springframework.util.Assert;
40+
import org.springframework.util.ClassUtils;
3541
import org.springframework.util.ReflectionUtils;
3642
import org.springframework.util.StringUtils;
43+
import org.springframework.util.concurrent.ListenableFuture;
3744

3845
/**
3946
* Base class for asynchronous method execution aspects, such as
@@ -52,6 +59,11 @@
5259
*/
5360
public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
5461

62+
// Java 8's CompletableFuture type present?
63+
private static final boolean completableFuturePresent = ClassUtils.isPresent(
64+
"java.util.concurrent.CompletableFuture", AsyncExecutionInterceptor.class.getClassLoader());
65+
66+
5567
protected final Log logger = LogFactory.getLog(getClass());
5668

5769
private final Map<Method, AsyncTaskExecutor> executors = new ConcurrentHashMap<Method, AsyncTaskExecutor>(16);
@@ -152,6 +164,32 @@ else if (executorToUse == null) {
152164
*/
153165
protected abstract String getExecutorQualifier(Method method);
154166

167+
/**
168+
* Delegate for actually executing the given task with the chosen executor.
169+
* @param task the task to execute
170+
* @param executor the chosen executor
171+
* @param returnType the declared return type (potentially a {@link Future} variant)
172+
* @return the execution result (potentially a corresponding {@link Future} handle)
173+
*/
174+
protected Object doSubmit(Callable<Object> task, AsyncTaskExecutor executor, Class<?> returnType) {
175+
if (completableFuturePresent) {
176+
Future<Object> result = CompletableFutureDelegate.processCompletableFuture(returnType, task, executor);
177+
if (result != null) {
178+
return result;
179+
}
180+
}
181+
if (ListenableFuture.class.isAssignableFrom(returnType)) {
182+
return ((AsyncListenableTaskExecutor) executor).submitListenable(task);
183+
}
184+
else if (Future.class.isAssignableFrom(returnType)) {
185+
return executor.submit(task);
186+
}
187+
else {
188+
executor.submit(task);
189+
return null;
190+
}
191+
}
192+
155193
/**
156194
* Handles a fatal error thrown while asynchronously invoking the specified
157195
* {@link Method}.
@@ -180,4 +218,29 @@ protected void handleError(Throwable ex, Method method, Object... params) throws
180218
}
181219
}
182220

221+
222+
/**
223+
* Inner class to avoid a hard dependency on Java 8.
224+
*/
225+
@UsesJava8
226+
private static class CompletableFutureDelegate {
227+
228+
public static <T> Future<T> processCompletableFuture(Class<?> returnType, final Callable<T> task, Executor executor) {
229+
if (!CompletableFuture.class.isAssignableFrom(returnType)) {
230+
return null;
231+
}
232+
return CompletableFuture.supplyAsync(new Supplier<T>() {
233+
@Override
234+
public T get() {
235+
try {
236+
return task.call();
237+
}
238+
catch (Throwable ex) {
239+
throw new CompletionException(ex);
240+
}
241+
}
242+
}, executor);
243+
}
244+
}
245+
183246
}

spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java

Lines changed: 2 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,18 @@
1818

1919
import java.lang.reflect.Method;
2020
import java.util.concurrent.Callable;
21-
import java.util.concurrent.CompletableFuture;
22-
import java.util.concurrent.CompletionException;
2321
import java.util.concurrent.ExecutionException;
2422
import java.util.concurrent.Executor;
2523
import java.util.concurrent.Future;
26-
import java.util.function.Supplier;
2724

2825
import org.aopalliance.intercept.MethodInterceptor;
2926
import org.aopalliance.intercept.MethodInvocation;
3027

3128
import org.springframework.aop.support.AopUtils;
3229
import org.springframework.core.BridgeMethodResolver;
3330
import org.springframework.core.Ordered;
34-
import org.springframework.core.task.AsyncListenableTaskExecutor;
3531
import org.springframework.core.task.AsyncTaskExecutor;
36-
import org.springframework.lang.UsesJava8;
3732
import org.springframework.util.ClassUtils;
38-
import org.springframework.util.concurrent.ListenableFuture;
3933

4034
/**
4135
* AOP Alliance {@code MethodInterceptor} that processes method invocations
@@ -69,13 +63,7 @@
6963
* @see org.springframework.scheduling.annotation.AsyncAnnotationAdvisor
7064
* @see org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor
7165
*/
72-
public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport
73-
implements MethodInterceptor, Ordered {
74-
75-
// Java 8's CompletableFuture type present?
76-
private static final boolean completableFuturePresent = ClassUtils.isPresent(
77-
"java.util.concurrent.CompletableFuture", AsyncExecutionInterceptor.class.getClassLoader());
78-
66+
public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered {
7967

8068
/**
8169
* Create a new {@code AsyncExecutionInterceptor}.
@@ -132,23 +120,7 @@ public Object call() throws Exception {
132120
}
133121
};
134122

135-
Class<?> returnType = invocation.getMethod().getReturnType();
136-
if (completableFuturePresent) {
137-
Future<Object> result = CompletableFutureDelegate.processCompletableFuture(returnType, task, executor);
138-
if (result != null) {
139-
return result;
140-
}
141-
}
142-
if (ListenableFuture.class.isAssignableFrom(returnType)) {
143-
return ((AsyncListenableTaskExecutor) executor).submitListenable(task);
144-
}
145-
else if (Future.class.isAssignableFrom(returnType)) {
146-
return executor.submit(task);
147-
}
148-
else {
149-
executor.submit(task);
150-
return null;
151-
}
123+
return doSubmit(task, executor, invocation.getMethod().getReturnType());
152124
}
153125

154126
/**
@@ -169,29 +141,4 @@ public int getOrder() {
169141
return Ordered.HIGHEST_PRECEDENCE;
170142
}
171143

172-
173-
/**
174-
* Inner class to avoid a hard dependency on Java 8.
175-
*/
176-
@UsesJava8
177-
private static class CompletableFutureDelegate {
178-
179-
public static <T> Future<T> processCompletableFuture(Class<?> returnType, final Callable<T> task, Executor executor) {
180-
if (!CompletableFuture.class.isAssignableFrom(returnType)) {
181-
return null;
182-
}
183-
return CompletableFuture.supplyAsync(new Supplier<T>() {
184-
@Override
185-
public T get() {
186-
try {
187-
return task.call();
188-
}
189-
catch (Throwable ex) {
190-
throw new CompletionException(ex);
191-
}
192-
}
193-
}, executor);
194-
}
195-
}
196-
197144
}

spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -62,11 +62,13 @@ public abstract aspect AbstractAsyncExecutionAspect extends AsyncExecutionAspect
6262
@SuppressAjWarnings("adviceDidNotMatch")
6363
Object around() : asyncMethod() {
6464
final MethodSignature methodSignature = (MethodSignature) thisJoinPointStaticPart.getSignature();
65+
6566
AsyncTaskExecutor executor = determineAsyncExecutor(methodSignature.getMethod());
6667
if (executor == null) {
6768
return proceed();
6869
}
69-
Callable<Object> callable = new Callable<Object>() {
70+
71+
Callable<Object> task = new Callable<Object>() {
7072
public Object call() throws Exception {
7173
try {
7274
Object result = proceed();
@@ -80,17 +82,7 @@ public abstract aspect AbstractAsyncExecutionAspect extends AsyncExecutionAspect
8082
return null;
8183
}};
8284

83-
Class<?> returnType = methodSignature.getReturnType();
84-
if (ListenableFuture.class.isAssignableFrom(returnType)) {
85-
return ((AsyncListenableTaskExecutor) executor).submitListenable(callable);
86-
}
87-
else if (Future.class.isAssignableFrom(returnType)) {
88-
return executor.submit(callable);
89-
}
90-
else {
91-
executor.submit(callable);
92-
return null;
93-
}
85+
return doSubmit(task, executor, methodSignature.getReturnType());
9486
}
9587

9688
/**

0 commit comments

Comments
 (0)