Faz mais ou menos 1 mês que saiu o primeiro milestone do Spring 5 com o suporte a tão comentada Reactive Programming. Não vou me alongar explicando os conceitos de programação reativa, porque já o fiz em outro post, aqui no blog mesmo. A ideia desse post é mostrar as configurações necessárias para você ter o seu projeto usando o Spring 5 e também ter um exemplo funcionando e que esteja linkado com os código que fazemos no dia a dia.
A primeira coisa que você pode fazer é clonar o projeto que eu disponibilizei no github. Lá dentro você vai encontrar o pom.xml.
<properties> ... <reactor.version>3.0.0.RC1</reactor.version> <spring.version>5.0.0.M1</spring.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot.experimental</groupId> <artifactId>spring-boot-dependencies-web-reactive</artifactId> <version>0.1.0.M1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot.experimental</groupId> <artifactId>spring-boot-starter-web-reactive</artifactId> </dependency>
Perceba que usamos um novo starter do Spring Boot chamado spring-boot-starter-web-reactive. Ele contém justamente todas as dependências que precisamos, algumas delas são:
- Project Reactor
- Adapters para os servidores
Outro detalhe importante é definir que queremos a versão 5 do Spring. Isso foi feito através da propriedade spring.version.
Pronto, com o projeto configurado, podemos dar uma olhada no código em si. A nossa ideia aqui é ter um endpoint que retorna um JSON representando uma lista de autores. Um código normal para isso seria assim:
@Repository public class AutorDao { @Autowired private EntityManager manager; public List<Autor> listaTodos(){ return manager.createQuery("select a from Autor a").getResultList()) } } @RestController public class AutoresController { @Autowired private AutorDao autoresDao; @RequestMapping("/autores") public List<Autor> index() { List<Autor> result = autoresDao.listaTodos(); return result; } }
O problema desse código é que quando o Spring delega a execução para seu método, ele perde completamente o controle da situação. Você escreve a lógica que quiser aí dentro e ela vai ser executada até o fim. É o código que chamamos de imperativo.
A grande sacada do Spring 5 é trazer um pouco de programação funcional para nossas soluções do dia a dia. Antes de detalhar o código, vamos apenas dar uma olhada nele.
@Repository public class AutorDao { @Autowired private EntityManager manager; public Mono<List<Autor>> listaTodos(){ Mono<List<Autor>> query = Mono.fromSupplier(() -> manager.createQuery("select a from Autor a").getResultList()); return query; } } @RestController public class AutoresController { @Autowired private AutorDao autoresDao; @RequestMapping("/autores") public Mono<List<Autor>> index() { Mono<List<Autor>> result = autoresDao.listaTodos(); System.out.println("ainda não vai ter feito a query"); return result; } }
Agora que você já viu, espero que tenha respirado fundo também, podemos debater um pouco do que aconteceu. A primeira coisa é que em vez de retornar uma List<Autor>, você retornou uma Mono<List<Autor>>. Lembre que Mono é um evento que emite apenas um item, nesse caso uma lista de autores. Uma variação dessa solução é a que segue abaixo.
@Repository public class AutorDao { @Autowired private EntityManager manager; public Flux<Autor> listaTodos(){ Mono<Query> query = Mono.fromSupplier(() -> manager.createQuery("select a from Autor a")); Flux<Autor> list = query.flatMap((q) -> Flux.fromIterable(q.getResultList())); return list; } } @RestController public class AutoresController { @Autowired private AutorDao autoresDao; @RequestMapping("/autores") public Flux<Autor> index() { Flux<Autor> result = autoresDao.listaTodos(); System.out.println("ainda não vai ter feito a query"); return result; } }
Em vez do nosso DAO ter um evento só, nós dividimos os passos do método em criar um evento que emite uma Query e aí aplicamos uma transformação para ele gerar um evento que emite vários autores, no caso Flux<Autor>. Não estou explicando os tipos, já que isso já está explicado no outro post.
A grande mudança aqui é que você declara os comportamentos em vez de executá-los e, fazendo isso, você delega para o Spring a responsabilidade de executar os seus códigos. A ideia é que a sua aplicação vai ter a chance de usar melhor os recursos da máquina onde ela está executando. Já que o framework parece ser mais competente para isso do que nós, desenvolvedores do código de negócio.
No exemplo que eu usei, entretanto, o maior problema é que usamos o Hibernate e ele não tem nada de reativo dentro dele. O ideal era que o método que executa a query com Hibernate retornasse a Flux e por aí vai. Quando a stack de tecnologias inteira da nossa aplicação for para esse lado, aí sim, vamos realmente tirar proveito.