Detailed Guide to Using @EnableAsync and @Async for Asynchronous Bean Method Execution in Spring

This article provides a comprehensive explanation of Spring's @EnableAsync and @Async annotations, covering their purpose, usage steps, handling of return values, custom thread‑pool configuration, exception handling, thread‑pool isolation, and the underlying AOP mechanism, supplemented with complete code examples.

Full-Stack Internet Architecture
Full-Stack Internet Architecture
Full-Stack Internet Architecture
Detailed Guide to Using @EnableAsync and @Async for Asynchronous Bean Method Execution in Spring

1. Overview

This section introduces the purpose of @EnableAsync and @Async: enabling asynchronous execution of bean methods in the Spring container.

2. Purpose

By annotating a bean method with @Async (and enabling it with @EnableAsync on a configuration class), the method will be invoked asynchronously, e.g., a logService.log(msg) call can run in a separate thread.

3. Basic Usage

Two Steps

Annotate the method (or the whole class) with @Async.

Add @EnableAsync to a Spring configuration class so that the @Async annotation takes effect.

Common Scenarios

Methods without a return value.

Methods that return a value.

4. Asynchronous Method Without Return Value

Usage

If the method does not return a Future, it returns immediately and its execution cannot be awaited.

@Async
public void log(String msg) throws InterruptedException {
    System.out.println("Start logging," + System.currentTimeMillis());
    // Simulate 2‑second delay
    TimeUnit.SECONDS.sleep(2);
    System.out.println("Log finished," + System.currentTimeMillis());
}

Example

A LogService bean uses @Async to record logs asynchronously.

package com.javacode2018.async.demo1;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;

@Component
public class LogService {
    @Async
    public void log(String msg) throws InterruptedException {
        System.out.println(Thread.currentThread() + " start logging," + System.currentTimeMillis());
        TimeUnit.SECONDS.sleep(2);
        System.out.println(Thread.currentThread() + " log finished," + System.currentTimeMillis());
    }
}

Configuration class to enable async:

package com.javacode2018.async.demo1;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;

@ComponentScan
@EnableAsync
public class MainConfig1 {}

Test code demonstrates immediate return of the caller and later execution in a separate thread.

5. Obtaining Asynchronous Return Values

Usage

When a method returns a Future, the result can be retrieved via Future.get(). Use AsyncResult.forValue to create the return value.

public Future<String> getGoodsInfo(long goodsId) throws InterruptedException {
    return AsyncResult.forValue(String.format("Goods %s basic info!", goodsId));
}

Example Scenario

In an e‑commerce context, three independent methods fetch product info, description, and comments in parallel, reducing total latency from ~1.5 s to ~0.5 s.

package com.javacode2018.async.demo2;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

@Async
@Component
public class GoodsService {
    public Future<String> getGoodsInfo(long goodsId) throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(500);
        return AsyncResult.forValue(String.format("Goods %s basic info!", goodsId));
    }
    public Future<String> getGoodsDesc(long goodsId) throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(500);
        return AsyncResult.forValue(String.format("Goods %s description!", goodsId));
    }
    public Future<List<String>> getGoodsComments(long goodsId) throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(500);
        List<String> comments = Arrays.asList("Comment1", "Comment2");
        return AsyncResult.forValue(comments);
    }
}

6. Custom Asynchronous Thread Pool

By default, Spring uses a built‑in thread pool. You can define your own by providing a bean named taskExecutor or by implementing AsyncConfigurer#getAsyncExecutor.

Method 1 – Define taskExecutor Bean

@Bean
public Executor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(10);
    executor.setMaxPoolSize(100);
    executor.setThreadNamePrefix("my-thread-");
    return executor;
}

Method 2 – Implement AsyncConfigurer

@Configuration
@EnableAsync
public class MainConfig3 implements AsyncConfigurer {
    @Bean
    public Executor logExecutors() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(100);
        executor.setThreadNamePrefix("log-thread-");
        return executor;
    }
    @Override
    public Executor getAsyncExecutor() {
        return logExecutors();
    }
}

7. Custom Exception Handling

Two cases:

If the method returns Future, catch ExecutionException when calling Future.get().

If the method has no return value, provide an AsyncConfigurer that returns a custom AsyncUncaughtExceptionHandler.

Future‑Based Exception Example

try {
    Future<String> future = logService.mockException();
    System.out.println(future.get());
} catch (ExecutionException e) {
    System.out.println("Caught ExecutionException");
    e.getCause().printStackTrace();
}

Void‑Method Exception Handler

@Bean
public AsyncConfigurer asyncConfigurer() {
    return new AsyncConfigurer() {
        @Override
        public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
            return (ex, method, params) -> {
                System.out.println(String.format("Method[%s], params[%s] threw an exception:", method, Arrays.asList(params)));
                ex.printStackTrace();
            };
        }
    };
}

8. Thread‑Pool Isolation

Define separate thread‑pool beans and reference them via @Async("beanName") to prevent one business from monopolizing a shared pool.

Example Services

package com.javacode2018.async.demo5;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class RechargeService {
    @Async(MainConfig5.RECHARGE_EXECUTORS_BEAN_NAME)
    public void recharge() {
        System.out.println(Thread.currentThread() + " async recharge");
    }
}
package com.javacode2018.async.demo5;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class CashOutService {
    @Async(MainConfig5.CASHOUT_EXECUTORS_BEAN_NAME)
    public void cashOut() {
        System.out.println(Thread.currentThread() + " async cash out");
    }
}

Configuration defines two executors with distinct name prefixes ( recharge-thread- and cashOut-thread-).

9. Underlying Implementation

Spring registers AsyncAnnotationBeanPostProcessor, which creates AOP proxies for beans containing @Async. The proxy adds AsyncAnnotationAdvisor that delegates to AnnotationAsyncExecutionInterceptor, where the actual asynchronous invocation occurs.

10. Summary

The article demonstrates how to enable asynchronous method execution in Spring, how to retrieve results, customize thread pools, handle exceptions, isolate thread pools per business, and explains the AOP‑based mechanism behind @EnableAsync and @Async.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaconcurrencyspringThreadPoolAsync
Full-Stack Internet Architecture
Written by

Full-Stack Internet Architecture

Introducing full-stack Internet architecture technologies centered on Java

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.