É comum na construção de aplicações utilizando Spring, utilizarmos a anotação de @EnableAsync para habilitar execuções assíncronas e com o auxílio da @Async sobre os métodos facilmente torná-los assíncronos.
A @Async possui basicamente duas regras de utilização:
o método anotado deve ser público
o invocador do método não pode ser da mesma classe
No exemplo abaixo, não haverá problemas de compilação, porém o método (apesar de anotado com @Async) não terá a execução como o desejado.
@Service
@RequiredArgsConstructor
public class HelloService {
public String get() {
log.info(“Chegou!”);
print();
return “Ola!”;
}
@Async
@SneakyThrows
public void print() {
Thread.sleep(Duration.ofSeconds(5));
log.info(“Burlado!”);
}
}
E é muito comum desejarmos que, por ser responsabilidade da classe, o bloco de código que deve ser executado assíncronamente se mantenha nela. Como resolver?
Simples!
Basta criarmos outra classe que auxilie, por exemplo:
public class AsyncService {
@Async
public void run(final Runnable runnable) {
runnable.run();
}
@Async
public <O> O run(final Supplier<O> supplier) {
return supplier.get();
}
}
Fazermos a injeção de dependência dessa bean onde é desejável a execução assíncrona e ainda por cima, poder tornar o método privado.
@Service
@RequiredArgsConstructor
public class HelloService {
private final AsyncService asyncService;
public String get() {
log.info(“Chegou!”);
asyncService.run(this::print);
return “Ola!”;
}
@SneakyThrows
private void print() {
Thread.sleep(Duration.ofSeconds(5));
log.info(“Burlado!”);
}
}
Esse pequeno exemplo, demonstra a aplicação de vários conceitos e recursos: Inversão de Controle, Injeção de Dependência, SOLID, Design Pattern, Interfaces Funcionais.