001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.aop.interceptor; 018 019import java.lang.reflect.Method; 020import java.util.concurrent.Callable; 021import java.util.concurrent.ExecutionException; 022import java.util.concurrent.Executor; 023import java.util.concurrent.Future; 024 025import org.aopalliance.intercept.MethodInterceptor; 026import org.aopalliance.intercept.MethodInvocation; 027 028import org.springframework.aop.support.AopUtils; 029import org.springframework.beans.factory.BeanFactory; 030import org.springframework.core.BridgeMethodResolver; 031import org.springframework.core.Ordered; 032import org.springframework.core.task.AsyncTaskExecutor; 033import org.springframework.core.task.SimpleAsyncTaskExecutor; 034import org.springframework.lang.Nullable; 035import org.springframework.util.ClassUtils; 036 037/** 038 * AOP Alliance {@code MethodInterceptor} that processes method invocations 039 * asynchronously, using a given {@link org.springframework.core.task.AsyncTaskExecutor}. 040 * Typically used with the {@link org.springframework.scheduling.annotation.Async} annotation. 041 * 042 * <p>In terms of target method signatures, any parameter types are supported. 043 * However, the return type is constrained to either {@code void} or 044 * {@code java.util.concurrent.Future}. In the latter case, the Future handle 045 * returned from the proxy will be an actual asynchronous Future that can be used 046 * to track the result of the asynchronous method execution. However, since the 047 * target method needs to implement the same signature, it will have to return 048 * a temporary Future handle that just passes the return value through 049 * (like Spring's {@link org.springframework.scheduling.annotation.AsyncResult} 050 * or EJB 3.1's {@code javax.ejb.AsyncResult}). 051 * 052 * <p>When the return type is {@code java.util.concurrent.Future}, any exception thrown 053 * during the execution can be accessed and managed by the caller. With {@code void} 054 * return type however, such exceptions cannot be transmitted back. In that case an 055 * {@link AsyncUncaughtExceptionHandler} can be registered to process such exceptions. 056 * 057 * <p>As of Spring 3.1.2 the {@code AnnotationAsyncExecutionInterceptor} subclass is 058 * preferred for use due to its support for executor qualification in conjunction with 059 * Spring's {@code @Async} annotation. 060 * 061 * @author Juergen Hoeller 062 * @author Chris Beams 063 * @author Stephane Nicoll 064 * @since 3.0 065 * @see org.springframework.scheduling.annotation.Async 066 * @see org.springframework.scheduling.annotation.AsyncAnnotationAdvisor 067 * @see org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor 068 */ 069public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered { 070 071 /** 072 * Create a new instance with a default {@link AsyncUncaughtExceptionHandler}. 073 * @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor} 074 * or {@link java.util.concurrent.ExecutorService}) to delegate to; 075 * as of 4.2.6, a local executor for this interceptor will be built otherwise 076 */ 077 public AsyncExecutionInterceptor(@Nullable Executor defaultExecutor) { 078 super(defaultExecutor); 079 } 080 081 /** 082 * Create a new {@code AsyncExecutionInterceptor}. 083 * @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor} 084 * or {@link java.util.concurrent.ExecutorService}) to delegate to; 085 * as of 4.2.6, a local executor for this interceptor will be built otherwise 086 * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use 087 */ 088 public AsyncExecutionInterceptor(@Nullable Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) { 089 super(defaultExecutor, exceptionHandler); 090 } 091 092 093 /** 094 * Intercept the given method invocation, submit the actual calling of the method to 095 * the correct task executor and return immediately to the caller. 096 * @param invocation the method to intercept and make asynchronous 097 * @return {@link Future} if the original method returns {@code Future}; {@code null} 098 * otherwise. 099 */ 100 @Override 101 @Nullable 102 public Object invoke(final MethodInvocation invocation) throws Throwable { 103 Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); 104 Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass); 105 final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); 106 107 AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod); 108 if (executor == null) { 109 throw new IllegalStateException( 110 "No executor specified and no default executor set on AsyncExecutionInterceptor either"); 111 } 112 113 Callable<Object> task = () -> { 114 try { 115 Object result = invocation.proceed(); 116 if (result instanceof Future) { 117 return ((Future<?>) result).get(); 118 } 119 } 120 catch (ExecutionException ex) { 121 handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments()); 122 } 123 catch (Throwable ex) { 124 handleError(ex, userDeclaredMethod, invocation.getArguments()); 125 } 126 return null; 127 }; 128 129 return doSubmit(task, executor, invocation.getMethod().getReturnType()); 130 } 131 132 /** 133 * This implementation is a no-op for compatibility in Spring 3.1.2. 134 * Subclasses may override to provide support for extracting qualifier information, 135 * e.g. via an annotation on the given method. 136 * @return always {@code null} 137 * @since 3.1.2 138 * @see #determineAsyncExecutor(Method) 139 */ 140 @Override 141 @Nullable 142 protected String getExecutorQualifier(Method method) { 143 return null; 144 } 145 146 /** 147 * This implementation searches for a unique {@link org.springframework.core.task.TaskExecutor} 148 * bean in the context, or for an {@link Executor} bean named "taskExecutor" otherwise. 149 * If neither of the two is resolvable (e.g. if no {@code BeanFactory} was configured at all), 150 * this implementation falls back to a newly created {@link SimpleAsyncTaskExecutor} instance 151 * for local use if no default could be found. 152 * @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME 153 */ 154 @Override 155 @Nullable 156 protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) { 157 Executor defaultExecutor = super.getDefaultExecutor(beanFactory); 158 return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor()); 159 } 160 161 @Override 162 public int getOrder() { 163 return Ordered.HIGHEST_PRECEDENCE; 164 } 165 166}