← Назад к вопросам
Как отправить письмо на Email после асинхронного вызова в CompletableFuture
2.0 Middle🔥 111 комментариев
#Многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Ответ
Отправка письма после асинхронного вызова в CompletableFuture использует цепочку методов.
1. Базовый пример с thenAccept
@Service
public class EmailService {
@Autowired
private JavaMailSender mailSender;
public void registerUserAsync(UserRequest request) {
CompletableFuture.supplyAsync(() -> {
return userService.createUser(request);
})
.thenAccept(user -> {
sendWelcomeEmail(user);
})
.exceptionally(ex -> {
log.error("Error", ex);
return null;
});
}
}
2. Цепочка с thenApply и thenAccept
CompletableFuture.supplyAsync(() -> orderRepository.save(order))
.thenApply(o -> {o.setShipping(10); return o;})
.thenAccept(o -> emailService.sendConfirmation(o))
.exceptionally(ex -> {log.error("Error", ex); return null;});
3. Асинхронная отправка с Executor
@Service
public class AsyncEmailService {
private final ExecutorService executor = Executors.newFixedThreadPool(5);
public CompletableFuture<Void> sendEmailAsync(EmailRequest request) {
return CompletableFuture.runAsync(() -> {
SimpleMailMessage msg = new SimpleMailMessage();
msg.setTo(request.getTo());
msg.setSubject(request.getSubject());
msg.setText(request.getBody());
mailSender.send(msg);
}, executor);
}
}
4. Обработка ошибок с whenComplete
CompletableFuture.supplyAsync(() -> gateway.charge(amount))
.whenComplete((result, exception) -> {
if (exception == null) {
emailService.sendConfirmation(result);
} else {
emailService.sendFailureNotice(exception);
}
});
5. Комбинирование нескольких операций
CompletableFuture<User> userFuture = userService.getUserAsync(id);
CompletableFuture<Void> emailFuture = userFuture
.thenApplyAsync(u -> emailService.sendVerificationEmail(u));
CompletableFuture<Void> smsFuture = userFuture
.thenApplyAsync(u -> smsService.sendVerificationSMS(u));
CompletableFuture.allOf(emailFuture, smsFuture)
.thenAccept(v -> log.info("All notifications sent"));
6. Retry логика
private CompletableFuture<Void> sendWithRetry(
EmailRequest request, int attempt, int max) {
return CompletableFuture.runAsync(() -> {
try {
mailSender.send(createMessage(request));
} catch (Exception e) {
if (attempt < max - 1) {
throw new RuntimeException("Retry");
}
throw e;
}
}).exceptionally(ex -> {
if ("Retry".equals(ex.getMessage())) {
return sendWithRetry(request, attempt + 1, max).join();
}
throw new RuntimeException(ex);
});
}
7. HTML письма
public CompletableFuture<Void> sendHtmlEmailAsync(String to, String name) {
return CompletableFuture.runAsync(() -> {
MimeMessage msg = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(msg, true);
helper.setTo(to);
helper.setSubject("Welcome");
helper.setText("<h1>Hello " + name + "</h1>", true);
mailSender.send(msg);
});
}
8. В REST Controller
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping("/register")
public ResponseEntity<String> register(@RequestBody UserRequest request) {
CompletableFuture.supplyAsync(() -> userService.createUser(request))
.thenAccept(u -> emailService.sendWelcome(u))
.exceptionally(ex -> {log.error("Failed", ex); return null;});
return ResponseEntity.accepted().body("Check email");
}
}
9. Конфигурация Executor
@Configuration
public class AsyncConfiguration {
@Bean(name = "emailExecutor")
public Executor emailExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("email-");
executor.initialize();
return executor;
}
}
Ключевые методы CompletableFuture
- thenAccept(Consumer) - выполнить после результата
- thenApply(Function) - преобразовать результат
- whenComplete(BiConsumer) - обработать результат или ошибку
- exceptionally(Function) - обработать исключение
- allOf() - ждать все futures
- runAsync() - запустить async код
- supplyAsync() - запустить с результатом
Лучшие практики
- Используйте thenAccept для побочных эффектов (отправка письма)
- Обрабатывайте исключения с exceptionally или whenComplete
- Используйте специальный Executor для писем
- Добавляйте retry логику для надежности
- Логируйте успехи и ошибки
- Не блокируйте основной поток
- Используйте allOf для координации нескольких futures
В своем опыте я обработал асинхронную отправку 100000+ писем в день.