java 39 lines · 7 steps

Async report generation with Spring @Async

A Spring service renders PDF reports off the request thread and fans out across accounts using CompletableFuture.

Explained by highlit
1@Service
2public class ReportGenerationService {
3 
4 private final ReportRepository reportRepository;
5 private final PdfRenderer pdfRenderer;
6 private final BlobStorageClient blobStorage;
7 
8 public ReportGenerationService(ReportRepository reportRepository,
9 PdfRenderer pdfRenderer,
10 BlobStorageClient blobStorage) {
11 this.reportRepository = reportRepository;
12 this.pdfRenderer = pdfRenderer;
13 this.blobStorage = blobStorage;
14 }
15 
16 @Async("reportExecutor")
17 public CompletableFuture<ReportResult> generateMonthlyReport(long accountId, YearMonth period) {
18 var transactions = reportRepository.findByAccountAndPeriod(accountId, period);
19 
20 var document = pdfRenderer.render(new ReportModel(accountId, period, transactions));
21 var key = "reports/%d/%s.pdf".formatted(accountId, period);
22 var url = blobStorage.upload(key, document, "application/pdf");
23 
24 var result = new ReportResult(accountId, period, url, transactions.size());
25 return CompletableFuture.completedFuture(result);
26 }
27 
28 @Async("reportExecutor")
29 public CompletableFuture<List<ReportResult>> generateForAccounts(List<Long> accountIds, YearMonth period) {
30 var futures = accountIds.stream()
31 .map(id -> generateMonthlyReport(id, period))
32 .toList();
33 
34 return CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new))
35 .thenApply(ignored -> futures.stream()
36 .map(CompletableFuture::join)
37 .toList());
38 }
39}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Returning CompletableFuture from an @Async method lets Spring run the work on a dedicated executor while callers keep a handle to the result.
  2. 2CompletableFuture.allOf plus join is the standard pattern for waiting on a batch of parallel tasks and collecting their results.
  3. 3Naming the executor in @Async keeps report work on its own thread pool, isolating it from other async tasks.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Async report generation with Spring @Async — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code