diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java index 83230f7461..254377e2f3 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java @@ -18,9 +18,13 @@ package org.springframework.aop.interceptor; import java.lang.reflect.Method; import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.Future; +import java.util.function.Supplier; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -31,9 +35,12 @@ import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.core.task.AsyncListenableTaskExecutor; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.core.task.support.TaskExecutorAdapter; +import org.springframework.lang.UsesJava8; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; +import org.springframework.util.concurrent.ListenableFuture; /** * Base class for asynchronous method execution aspects, such as @@ -52,6 +59,11 @@ import org.springframework.util.StringUtils; */ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { + // Java 8's CompletableFuture type present? + private static final boolean completableFuturePresent = ClassUtils.isPresent( + "java.util.concurrent.CompletableFuture", AsyncExecutionInterceptor.class.getClassLoader()); + + protected final Log logger = LogFactory.getLog(getClass()); private final Map executors = new ConcurrentHashMap(16); @@ -152,6 +164,32 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { */ protected abstract String getExecutorQualifier(Method method); + /** + * Delegate for actually executing the given task with the chosen executor. + * @param task the task to execute + * @param executor the chosen executor + * @param returnType the declared return type (potentially a {@link Future} variant) + * @return the execution result (potentially a corresponding {@link Future} handle) + */ + protected Object doSubmit(Callable task, AsyncTaskExecutor executor, Class returnType) { + if (completableFuturePresent) { + Future result = CompletableFutureDelegate.processCompletableFuture(returnType, task, executor); + if (result != null) { + return result; + } + } + if (ListenableFuture.class.isAssignableFrom(returnType)) { + return ((AsyncListenableTaskExecutor) executor).submitListenable(task); + } + else if (Future.class.isAssignableFrom(returnType)) { + return executor.submit(task); + } + else { + executor.submit(task); + return null; + } + } + /** * Handles a fatal error thrown while asynchronously invoking the specified * {@link Method}. @@ -180,4 +218,29 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { } } + + /** + * Inner class to avoid a hard dependency on Java 8. + */ + @UsesJava8 + private static class CompletableFutureDelegate { + + public static Future processCompletableFuture(Class returnType, final Callable task, Executor executor) { + if (!CompletableFuture.class.isAssignableFrom(returnType)) { + return null; + } + return CompletableFuture.supplyAsync(new Supplier() { + @Override + public T get() { + try { + return task.call(); + } + catch (Throwable ex) { + throw new CompletionException(ex); + } + } + }, executor); + } + } + } diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java index d5026cb434..8aa0c020ac 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java @@ -18,12 +18,9 @@ package org.springframework.aop.interceptor; import java.lang.reflect.Method; import java.util.concurrent.Callable; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Future; -import java.util.function.Supplier; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; @@ -31,11 +28,8 @@ import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.support.AopUtils; import org.springframework.core.BridgeMethodResolver; import org.springframework.core.Ordered; -import org.springframework.core.task.AsyncListenableTaskExecutor; import org.springframework.core.task.AsyncTaskExecutor; -import org.springframework.lang.UsesJava8; import org.springframework.util.ClassUtils; -import org.springframework.util.concurrent.ListenableFuture; /** * AOP Alliance {@code MethodInterceptor} that processes method invocations @@ -69,13 +63,7 @@ import org.springframework.util.concurrent.ListenableFuture; * @see org.springframework.scheduling.annotation.AsyncAnnotationAdvisor * @see org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor */ -public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport - implements MethodInterceptor, Ordered { - - // Java 8's CompletableFuture type present? - private static final boolean completableFuturePresent = ClassUtils.isPresent( - "java.util.concurrent.CompletableFuture", AsyncExecutionInterceptor.class.getClassLoader()); - +public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered { /** * Create a new {@code AsyncExecutionInterceptor}. @@ -132,23 +120,7 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport } }; - Class returnType = invocation.getMethod().getReturnType(); - if (completableFuturePresent) { - Future result = CompletableFutureDelegate.processCompletableFuture(returnType, task, executor); - if (result != null) { - return result; - } - } - if (ListenableFuture.class.isAssignableFrom(returnType)) { - return ((AsyncListenableTaskExecutor) executor).submitListenable(task); - } - else if (Future.class.isAssignableFrom(returnType)) { - return executor.submit(task); - } - else { - executor.submit(task); - return null; - } + return doSubmit(task, executor, invocation.getMethod().getReturnType()); } /** @@ -169,29 +141,4 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport return Ordered.HIGHEST_PRECEDENCE; } - - /** - * Inner class to avoid a hard dependency on Java 8. - */ - @UsesJava8 - private static class CompletableFutureDelegate { - - public static Future processCompletableFuture(Class returnType, final Callable task, Executor executor) { - if (!CompletableFuture.class.isAssignableFrom(returnType)) { - return null; - } - return CompletableFuture.supplyAsync(new Supplier() { - @Override - public T get() { - try { - return task.call(); - } - catch (Throwable ex) { - throw new CompletionException(ex); - } - } - }, executor); - } - } - } diff --git a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj index 7b929bf85c..684eaf9e1c 100644 --- a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj @@ -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. @@ -62,11 +62,13 @@ public abstract aspect AbstractAsyncExecutionAspect extends AsyncExecutionAspect @SuppressAjWarnings("adviceDidNotMatch") Object around() : asyncMethod() { final MethodSignature methodSignature = (MethodSignature) thisJoinPointStaticPart.getSignature(); + AsyncTaskExecutor executor = determineAsyncExecutor(methodSignature.getMethod()); if (executor == null) { return proceed(); } - Callable callable = new Callable() { + + Callable task = new Callable() { public Object call() throws Exception { try { Object result = proceed(); @@ -80,17 +82,7 @@ public abstract aspect AbstractAsyncExecutionAspect extends AsyncExecutionAspect return null; }}; - Class returnType = methodSignature.getReturnType(); - if (ListenableFuture.class.isAssignableFrom(returnType)) { - return ((AsyncListenableTaskExecutor) executor).submitListenable(callable); - } - else if (Future.class.isAssignableFrom(returnType)) { - return executor.submit(callable); - } - else { - executor.submit(callable); - return null; - } + return doSubmit(task, executor, methodSignature.getReturnType()); } /**