Implementing Asynchronous Processing
1. Enabling Async Support (@EnableAsync)
Example: Enable async execution
@SpringBootApplication
@EnableAsync
public class App {}
| Attribute |
Description |
proxyTargetClass |
Force CGLIB proxies |
mode |
PROXY (default) or ASPECTJ |
annotation |
Custom marker annotation |
2. Creating Async Methods (@Async)
Example: Async email with CompletableFuture
@Service
public class NotifyService {
@Async
public void sendEmail(String to, String body) { /* slow */ }
@Async("emailExecutor")
public CompletableFuture<String> sendAndReturn(String to) {
return CompletableFuture.completedFuture("sent:" + to);
}
}
3. Returning Future and CompletableFuture
| Return Type |
Behavior |
void |
Fire-and-forget |
Future<T> |
Blocking get() |
CompletableFuture<T> |
Composable, recommended |
ListenableFuture<T> DEPRECATED |
Use CompletableFuture |
4. Configuring Thread Pool Executor (TaskExecutor)
Example: Thread pool executor for email tasks
@Bean("emailExecutor")
ThreadPoolTaskExecutor emailExecutor() {
ThreadPoolTaskExecutor ex = new ThreadPoolTaskExecutor();
ex.setCorePoolSize(4);
ex.setMaxPoolSize(16);
ex.setQueueCapacity(500);
ex.setThreadNamePrefix("email-");
ex.setRejectedExecutionHandler(new CallerRunsPolicy());
ex.initialize();
return ex;
}
Example: Async task pool configuration
spring:
task:
execution:
pool: { core-size: 4, max-size: 16, queue-capacity: 500 }
thread-name-prefix: app-task-
5. Handling Async Exceptions
Example: Handle uncaught async exceptions
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, params) ->
LoggerFactory.getLogger(method.getDeclaringClass())
.error("Async error in {}", method.getName(), ex);
}
}
| Return |
Exception Handling |
| void |
AsyncUncaughtExceptionHandler |
| Future |
Wrapped in ExecutionException on get() |
6. Using @Async with Transactions
Warning: @Async runs on a different thread → transaction does NOT propagate.
Annotate the async method itself with @Transactional.
| Pattern |
Effect |
| Caller @Transactional + callee @Async |
Async runs WITHOUT tx |
| Async method @Transactional |
Starts new tx on async thread |
7. Implementing Async REST Endpoints
Example: DeferredResult and Callable endpoint
@GetMapping("/slow")
public DeferredResult<String> slow() {
DeferredResult<String> result = new DeferredResult<>(5000L, "timeout");
CompletableFuture.supplyAsync(this::compute).thenAccept(result::setResult);
return result;
}
@GetMapping("/cb")
public Callable<String> viaCallable() { return () -> compute(); }
8. Using WebAsyncTask for Timeouts
Example: Async task with per-request timeout
@GetMapping("/report")
public WebAsyncTask<Report> report() {
return new WebAsyncTask<>(3000L, "reportExecutor", () -> service.build());
}
9. Configuring Async Request Processing
| Property |
Use |
spring.mvc.async.request-timeout |
Default async timeout (ms) |
WebMvcConfigurer.configureAsyncSupport |
Programmatic config |
10. Using ListenableFuture for Callbacks
Note: ListenableFuture is deprecated; use
CompletableFuture with thenApply, exceptionally, etc.
| CompletableFuture API |
Use |
thenApply(fn) |
Transform result |
thenCompose(fn) |
Chain async |
thenCombine(other,fn) |
Combine two futures |
exceptionally(fn) |
Recover from error |
orTimeout(d,unit) |
Fail after timeout |
11. Testing Async Methods
Example: Test async method with Awaitility
@SpringBootTest
class NotifyTests {
@Autowired NotifyService svc;
@Test void sends() throws Exception {
CompletableFuture<String> f = svc.sendAndReturn("a@b");
Awaitility.await().atMost(Duration.ofSeconds(2)).until(f::isDone);
assertEquals("sent:a@b", f.get());
}
}