Spring WebFlux é o novo módulo introduzido no Spring 5. Spring WebFlux é o primeiro passo em direção ao modelo de programação reativa no framework Spring.
Programação Reativa Spring
Se você é novo no modelo de programação reativa, então eu sugeriria fortemente que você leia os seguintes artigos para aprender sobre programação reativa.
Se você é novo no Spring 5, por favor, leia Recursos do Spring 5.
Spring WebFlux
Spring WebFlux é a alternativa ao módulo Spring MVC. Spring WebFlux é usado para criar aplicações totalmente assíncronas e não bloqueantes, construídas no modelo de execução de loop de eventos. O diagrama abaixo, da Documentação Oficial do Spring, fornece uma ótima visão sobre a comparação entre o Spring WebFlux e o Spring Web MVC. Se você está procurando desenvolver uma aplicação web ou um serviço web Rest em um modelo reativo não bloqueante, então você pode olhar para o Spring WebFlux. O Spring WebFlux é suportado no Tomcat, Jetty, contêineres Servlet 3.1+ e também em tempo de execução não Servlet, como o Netty e o Undertow. O Spring WebFlux é construído no Project Reactor. O Project Reactor é a implementação da especificação Reactive Streams. O Reactor fornece dois tipos:
- Mono: implementa Publisher e retorna 0 ou 1 elementos
- Flux: implementa Publisher e retorna N elementos.
Exemplo de “Hello World” do Spring WebFlux
Vamos construir uma aplicação simples de “Hello World” com Spring WebFlux. Vamos criar um serviço web rest simples e usar o Spring Boot para executá-lo no servidor Netty padrão. A estrutura final do nosso projeto parece com a imagem abaixo. Vamos examinar cada componente da aplicação um por um.
Dependências do Maven do Spring WebFlux
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev.spring</groupId>
<artifactId>SpringWebflux</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring WebFlux</name>
<description>Spring WebFlux Example</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<jdk.version>1.9</jdk.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
As dependências mais importantes são spring-boot-starter-webflux
e spring-boot-starter-parent
. Algumas outras dependências são para criar casos de teste JUnit.
Manipulador do Spring WebFlux
O método Manipulador do Spring WebFlux lida com a solicitação e retorna Mono
ou Flux
como resposta.
package com.journaldev.spring.component;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class HelloWorldHandler {
public Mono<ServerResponse> helloWorld(ServerRequest request) {
return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromObject("Hello World!"));
}
}
Observe que o componente reativo Mono
mantém o corpo do ServerResponse
. Também observe a cadeia de funções para definir o tipo de conteúdo de retorno, código de resposta e corpo.
Roteador do Spring WebFlux
Os métodos do roteador são usados para definir rotas para a aplicação. Esses métodos retornam o objeto RouterFunction
, que também contém o corpo de ServerResponse
.
package com.journaldev.spring.component;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
@Configuration
public class HelloWorldRouter {
@Bean
public RouterFunction<ServerResponse> routeHelloWorld(HelloWorldHandler helloWorldHandler) {
return RouterFunctions.route(RequestPredicates.GET("/helloWorld")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), helloWorldHandler::helloWorld);
}
}
Portanto, estamos expondo um método GET para /helloWorld
e a chamada do cliente deve aceitar uma resposta de texto simples.
Aplicação Spring Boot
Vamos configurar nossa simples aplicação WebFlux com o Spring Boot.
package com.journaldev.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Se você olhar para o código acima, não há nada relacionado ao Spring WebFlux. Mas o Spring Boot irá configurar nossa aplicação como Spring WebFlux, pois adicionamos a dependência do módulo spring-boot-starter-webflux
.
Suporte a Módulos Java 9
Nossa aplicação está pronta para ser executada no Java 8, mas se estiver usando o Java 9, também precisamos adicionar a classe module-info.java
.
module com.journaldev.spring {
requires reactor.core;
requires spring.web;
requires spring.beans;
requires spring.context;
requires spring.webflux;
requires spring.boot;
requires spring.boot.autoconfigure;
exports com.journaldev.spring;
}
Executando a Aplicação Spring WebFlux Spring Boot
Se você tiver suporte ao Spring no Eclipse, poderá executar a classe acima como um aplicativo Spring Boot. Se preferir usar a linha de comando, abra o terminal e execute o comando
mvn spring-boot:run
a partir do diretório de origem do projeto. Quando o aplicativo estiver em execução, observe as mensagens de log a seguir para garantir que tudo esteja correto com nosso aplicativo. Isso também é útil ao expandir este aplicativo simples, adicionando mais rotas e funcionalidades.
2018-05-07 15:01:47.893 INFO 25158 --- [ main] o.s.w.r.f.s.s.RouterFunctionMapping : Mapped ((GET && /helloWorld) && Accept: [text/plain]) -> com.journaldev.spring.component.HelloWorldRouter$$Lambda$501/704766954@6eeb5d56
2018-05-07 15:01:48.495 INFO 25158 --- [ctor-http-nio-1] r.ipc.netty.tcp.BlockingNettyContext : Started HttpServer on /0:0:0:0:0:0:0:0:8080
2018-05-07 15:01:48.495 INFO 25158 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080
2018-05-07 15:01:48.501 INFO 25158 --- [ main] com.journaldev.spring.Application : Started Application in 1.86 seconds (JVM running for 5.542)
Está claro nos logs que nosso aplicativo está sendo executado no servidor Netty na porta 8080. Vamos em frente e testar nossa aplicação.
Teste do Aplicativo Spring WebFlux
Podemos testar nosso aplicativo com vários métodos.
-
Usando o comando CURL
$ curl https://localhost:8080/helloWorld Hello World! $
-
Iniciar URL no Navegador
-
Usando WebTestClient do Spring 5 Aqui está um programa de teste JUnit para testar nosso serviço da web Rest usando
WebTestClient
do Spring 5 web reativa.package com.journaldev.spring; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class SpringWebFluxTest { @Autowired private WebTestClient webTestClient; @Test public void testHelloWorld() { webTestClient .get().uri("/helloWorld") // método GET e URI .accept(MediaType.TEXT_PLAIN) // definindo ACCEPT-Content .exchange() // dá acesso à resposta .expectStatus().isOk() // verificando se a resposta está OK .expectBody(String.class).isEqualTo("Hello World!"); // verificando o tipo de resposta e a mensagem } }
Execute como um caso de teste JUnit e deverá passar com cores brilhantes.
-
Usando WebClient do Spring Web Reactive Também podemos usar
WebClient
para chamar o serviço web REST.pacote com.journaldev.spring.client; import org.springframework.http.MediaType; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; public class HelloWorldWebClient { public static void main(String args[]) { WebClient client = WebClient.create("https://localhost:8080"); Mono<ClientResponse> result = client.get() .uri("/helloWorld") .accept(MediaType.TEXT_PLAIN) .exchange(); System.out.println("Resultado = " + result.flatMap(res -> res.bodyToMono(String.class)).block()); } }
Basta executá-lo como uma aplicação Java simples e você deverá ver a saída adequada com muitas mensagens de depuração.
Resumo
Neste post, aprendemos sobre o Spring WebFlux e como construir um serviço web reativo de exemplo. É bom ver que frameworks populares como o Spring estão apoiando o modelo de programação reativa. Mas temos muito a cobrir, porque se todas as suas dependências não forem reativas e não bloqueadoras, então sua aplicação também não será verdadeiramente reativa. Por exemplo, os fornecedores de banco de dados relacionais não têm drivers reativos porque dependem do JDBC, que não é reativo. Portanto, a API do Hibernate também não é reativa. Então, se você estiver usando bancos de dados relacionais, não poderá construir uma aplicação verdadeiramente reativa ainda. Estou esperançoso de que isso mudará mais cedo do que tarde.
Você pode baixar o código do projeto do meu Repositório GitHub.
Referência: Documentação Oficial
Source:
https://www.digitalocean.com/community/tutorials/spring-webflux-reactive-programming