Not every task belongs in the request/response cycle. Sending email, generating
reports, or syncing external systems should happen off the request thread.
Spring gives you two lightweight tools for this: @Async and @Scheduled.
Enable the machinery
Both features are opt-in. Add the enabling annotations to a configuration class and define a dedicated thread pool so async work never starves your web threads.
@Configuration
@EnableAsync
@EnableScheduling
public class AsyncConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(16);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("worker-");
executor.initialize();
return executor;
}
}Fire-and-forget with @Async
Annotate a method with @Async and Spring runs it on the executor above. Return
CompletableFuture when the caller needs the result later.
@Service
public class EmailService {
@Async("taskExecutor")
public CompletableFuture<Void> sendWelcome(String to) {
// slow SMTP call runs off the request thread
mailClient.send(to, "Welcome!");
return CompletableFuture.completedFuture(null);
}
}Note: @Async only works when the method is called through a Spring proxy.
Calling it from within the same class bypasses the proxy and runs synchronously.
Recurring jobs with @Scheduled
Use @Scheduled for cron-like work. Prefer fixedDelay for jobs that should
not overlap, and cron for wall-clock schedules.
@Component
public class ReportJob {
@Scheduled(fixedDelay = 60_000)
public void flushMetrics() {
metrics.flush();
}
@Scheduled(cron = "0 0 2 * * *", zone = "UTC")
public void nightlyReport() {
reportService.generateDaily();
}
}Takeaways
- Always define an explicit executor — the default is unbounded.
- Remember
@Asyncneeds a proxied call to take effect. - Use
fixedDelayto prevent overlapping runs,cronfor clock schedules.
For heavier workloads that must survive restarts, graduate to a real queue like Kafka or RabbitMQ instead of in-process scheduling.