BadPractice18 - SpotBugsExtensionForSpringFrameWork/CS5098 GitHub Wiki

Bad Practice - Should use AsyncResult<V> return type to handle @Async exception instead of Future<V> return type

Description

For those methods with return type void, we should avoid using try-catch block because the process that try to catch exception has already disconnected with the invocation method.

For those methods with return value, developers used to use Future<V> as the return type. Future<V> is the interface that Java standard library provided to represent the result of an asunchronous computation. However, it's not a good practice to use Future<V> to handle @Async exception, developers should treat AsyncResult<V> as a good practice.

@Async
public Future<Integer> asyncMethodWithReturnType() {
    System.out.println("Execute method asynchronously - " 
      + Thread.currentThread().getName());
  	int a = 1;
  	int b = 0;
    int c = 0;
  
  	c = a/b;

  	return new Future<Integer>(c);

}
public void testAsyncAnnotationForMethodsWithReturnType() {
    System.out.println("Invoking an asynchronous method. " 
      + Thread.currentThread().getName());
  	try {
    	Future<String> future = asyncAnnotationExample.asyncMethodWithReturnType();
    } catch (ArithmeticException e) {
      logger.error("Handling exception", e);
    }
}

Theory

When the return type of the asynchonous method is Future<V>, the exception thrown during the execution can be accessed by the caller. That's because there is a method Future.get() that throws ExecutionException which means developers can use the get() method to obtain any exception thrown during the execution.

public interface Future<V> {
	...

  V get() throws InterruptedException, ExecutionException;

  ...
}

Although Future<V> has the ability to catch exception, Spring provides a way to handle exception of invoked method. AsyncResult<V> is a Spring provided class implements interface ListenableFuture<V> which can track the result of asynchronous method execution. Also, since Spring 4.2, it provides a method to retrieve thrown exception during the execution of the asynchonous method.

public class AsyncResult<V> implements ListenableFuture<V> {
  
  ...
    
	@Override
	@Nullable
	public V get() throws ExecutionException {
		if (this.executionException != null) {
			throw (this.executionException instanceof ExecutionException ?
					(ExecutionException) this.executionException :
					new ExecutionException(this.executionException));
		}
		return this.value;
	}
  
  ...
}

Solution

First, we can simply change the Future<V> return type to AsyncResult<V>. Second, we just need to call the future.get() method to let the main process to handle the thrown exception.

@Service
public class AsyncService {
 
    @Async
    public AsyncResult<Integer> asyncMethodWithResult() {
        int a = 1;
  			int b = 0;
    		int c = 0;
  
  			c = a/b;

  			return new Future<Integer>(c);
    }
}
public class Test {

  private Logger logger = LoggerFactory.getLogger(getClass());

  @Autowired
  AsyncService asyncService;

  public void test() {
    try {
      Future future = asyncService.asyncMethodWithResult();
      future.get();
    } catch (ArithmeticException e) {
      logger.error("exception occurs", e);
    } 
  }

}

Link

https://www.programmersought.com/article/34932801814/

Supporting Exception Handling for Futures in Java

⚠️ **GitHub.com Fallback** ⚠️