DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Spring Boot - Microservice- JaxRS Jersey - HATEOAS - JerseyTest - Integration
  • Java, Spring Boot, and MongoDB: Performance Analysis and Improvements
  • Build Reactive REST APIs With Spring WebFlux
  • Spring Cloud Stream: A Brief Guide

Trending

  • Rethinking Recruitment: A Journey Through Hiring Practices
  • Docker Model Runner: Streamlining AI Deployment for Developers
  • Microsoft Azure Synapse Analytics: Scaling Hurdles and Limitations
  • Create Your Own AI-Powered Virtual Tutor: An Easy Tutorial
  1. DZone
  2. Coding
  3. Frameworks
  4. Spring Webflux: Getting started

Spring Webflux: Getting started

Excited to dive into Reactive Streams? Take a look at how to use Spring Webflux and Java 9 for asynchronous processing.

By 
Gunter Rotsaert user avatar
Gunter Rotsaert
DZone Core CORE ·
Mar. 09, 18 · Tutorial
Likes (31)
Comment
Save
Tweet
Share
70.5K Views

Join the DZone community and get the full member experience.

Join For Free

This post will be about how I got started with Spring Webflux. A few weeks ago, I started to read in detail about Spring Webflux, Reactive Streams, etc. Looking back at my journey, I did things in the wrong order. In this post, I have tried to structure the information in order to give you a plan for getting started with Spring Webflux. The examples can be found on GitHub.

Reactive Manifesto

The basis for Reactive Streams can be found in the Reactive Manifesto. It is advised to read this manifesto, it is only 2 pages long, so it won't take long to read it.

Reactive Streams

Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure, as can be read on the website. This means that a source can send data to a destination without overwhelming the destination with too much data. The destination will tell the source how much data it can handle. This way, resources are used more efficiently. The Reactive Streams specification wants to set a standard also.

The standard for the JVM is available in GitHub. Summarized, the specification consists of the following items:

  • Publisher: The publisher is the source that will send the data to one or more subscribers.
  • Subscriber: A subscriber will subscribe itself to a publisher, will indicate how much data the publisher may send and will process the data.
  • Subscription: On the publisher side, a subscription will be created, which will be shared with a subscriber.
  • Processor: A processor can be used to sit between a publisher and a subscriber, this way a transformation of the data can take place.

The Reactive Streams specification is a standard and since Java 9, it is included in Java with the Flow API. Take a closer look at the Reactive Streams specification and the Java 9 Flow API. As you will notice, the interfaces for Publisher, Subscriber, Subscription, and Processor are identical.

Project Reactor

The next step to take in our journey is Project Reactor. This project provides a Reactive library based on the Reactive Streams specification. In other words, an implementation of the specification. Basically, Reactor provides two types:

  • Mono: implements Publisher and returns 0 or 1 elements;
  • Flux: implements Publisher and returns N elements.

But what is its relation with Spring Webflux? Spring Webflux is based on Project Reactor and this brings us to the main goal of this post: learn more about Spring Webflux.

Spring Webflux

Spring Webflux is introduced with Spring 5, the official Spring documentation can be found here. You can read the whole documentation, but it is quite a lot of information. I suggest to first look at an example (this article) and then refer back to the documentation. Important to know is that there are two ways to use Spring Webflux. One using annotations, which is quite similar to Spring MVC, and one using a functional way. I will use the functional way. Besides that, you must know that Spring MVC and Spring Webflux exist next to each other. Spring MVC is used for synchronous processing, while Spring Webflux for asynchronous processing.

Spring Webflux Getting Started Example

To make it easy on myself, I tried to follow the Spring Webflux getting started example: Building a Reactive RESTful Web Service. I followed the guide and used also Java 9. However, it did not run out-of-the-box. I had to make some adaptations. I will not repeat the steps to follow (you can read it in the guide), but I will document my findings when trying to build and run the application. The example can be found in package com.mydeveloperplanet.myspringwebfluxplanet.greeting.

First, I had to add the Milestones repository to the POM in order to use spring-boot-starter-parent version 2.0.0.RC2.

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.0.RC2</version>
</parent>
...
<repositories>
  <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-milestones</id>
    <name>Spring Milestones</name>
    <url>https://repo.spring.io/milestone</url>
    <snapshots>
      <enabled>false</enabled>
    </snapshots>
  </pluginRepository>
</pluginRepositories>


As of the 1st of March, the above is not necessary anymore since Spring Boot is now generally available. It is now sufficient to only refer to this GA release:

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.0.RELEASE</version>
</parent>


Also, in the dependencies, I had to use spring-boot-starter-webflux instead of spring-boot-starter-web.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>


Because I used Java 9, I had to add the following module-info :

module com.mydeveloperplanet.myspringwebfluxplanet {
  requires reactor.core;
  requires spring.web;
  requires spring.context;
  requires spring.webflux;
  requires spring.boot;
  requires spring.boot.autoconfigure;
}


Now, build and run the example with Maven target spring-boot:run.

In the console, we see that the following line is printed:

>> result = Hello, Spring!


You can also test it in the browser, using the URL http://localhost:8080/hello.

Looking at the console log, we see that the Netty Webserver is being used. This is the default for Spring Webflux applications.

Spring Webflux Basic Example

Now, let's see if we can create a basic example and understand what is exactly going on here.

We create package com.mydeveloperplanet.myspringwebfluxplanet.examples and add an ExampleRouter and an ExampleHandler, similar to the Greeting example but we try to do the minimum in order to make it accessible at URL http://localhost:8080/example.

The ExampleRouter becomes the following:

@Configuration
public class ExampleRouter {

  @Bean
  public RouterFunction<ServerResponse> route(ExampleHandler exampleHandler) {

    return RouterFunctions
        .route(RequestPredicates.GET("/example").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), exampleHandler::hello);
  }
}


The ExampleHandler becomes the following:

@Component
public class ExampleHandler {

  public Mono<ServerResponse> hello(ServerRequest request) {
    return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
         .body(BodyInserters.fromObject("Hello, Spring Webflux Example!"));
  }
}


Execute the Maven target spring-boot:run. Go to the URL http://localhost:8080/example in your browser. The following error is shown:

spring-webflux-routing-error

In the console log, we see the following:

Overriding bean definition for bean 'route' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=exampleRouter; factoryMethodName=route; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/mydeveloperplanet/myspringwebfluxplanet/examples/ExampleRouter.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=greetingRouter; factoryMethodName=route; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/mydeveloperplanet/myspringwebfluxplanet/greeting/GreetingRouter.class]]


Now change the method route in class ExampleRouter into routeExample and re-run the application. Now the browser shows the following as expected:

Hello, Spring Webflux Example!


As a minimum, we only need a Router and a Handler. We must be sure that the Router Bean has a unique name, otherwise, it may be overwritten by another Bean with the same name. The RouterFunction determines the route it matches and eventually resolves to a Handler method.

Spring Webflux 'one step further' Example

Now we know how to set up the basics, let's take it one step further and explore some extra functionality.

We create a method routeExampleOneStepFurther in class ExampleRouter and chain two routes with each other by means of the andRoute method. This way, we can chain several routes to each other and let them resolve to different handler methods. The routes are evaluated one by one, so you must make sure to define specific routes before more generic routes. The method routeExampleOneStepFurther is the following:

public RouterFunction<ServerResponse> routeExampleOneStepFurther(ExampleHandler exampleHandler) {

  return RouterFunctions
    .route(RequestPredicates.GET("/exampleFurther1").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), exampleHandler::helloFurther1)
    .andRoute(RequestPredicates.GET("/exampleFurther2").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), exampleHandler::helloFurther2);
 }


The handler methods are created in class ExampleHandler and just return a string:

public Mono<ServerResponse> helloFurther1(ServerRequest request) {
  return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
    .body(BodyInserters.fromObject("Hello, Spring Webflux Example 1!"));
}

public Mono<ServerResponse> helloFurther2(ServerRequest request) {
  return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
    .body(BodyInserters.fromObject("Hello, Spring Webflux Example 2!"));
}


Now build and run the application and invoke the URL http://localhost:8080/exampleFurther1. The result is as follows:

Hello, Spring Webflux Example 1!


The same way, URL http://localhost:8080/exampleFurther2 shows us the following result:

Hello, Spring Webflux Example 2!


We have tested manually, now let's create a unit test for it. The unit test is the following:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ExampleRouterTest {

  @Autowired
  private WebTestClient webTestClient;

  @Test
  public void testExampleOneStepFurther() {
    webTestClient
      .get().uri("/exampleFurther1")
      .accept(MediaType.TEXT_PLAIN)
      .exchange()
      .expectStatus().isOk()
      .expectBody(String.class).isEqualTo("Hello, Spring Webflux Example 1!");

    webTestClient
      .get().uri("/exampleFurther2")
      .accept(MediaType.TEXT_PLAIN)
      .exchange()
      .expectStatus().isOk()
      .expectBody(String.class).isEqualTo("Hello, Spring Webflux Example 2!");
  }

}


The test is based on the Spring Webflux example. During the test, a server will be started on a random port and will invoke the URLs one after each other. The exchange method gives us access to the response. First, the response status is checked and then we check whether the response is correct. In the Spring Webflux example, the equals method is used to verify the response, but this results always in success, even if the response is not equal to the result. This is not correct, the method isEqualTo must be used in order to have a correct test.

Spring Webflux 'last steps' Example

Finally, we will create a route which takes a parameter, add a test for it and add a test for an unsuccessful API call.

In our routeExampleOneStepFurther method, we add another route and let it resolve to the method helloFurther3 of class ExampleHandler. The route is the following:

.andRoute(RequestPredicates.GET("/exampleFurther3").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), exampleHandler::helloFurther3)


The method helloFurther2 extracts a name parameter and says hello to the given name:

public Mono<ServerResponse> helloFurther3(ServerRequest request) {
  String name = request.queryParam("name").get();
  return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
    .body(BodyInserters.fromObject("Hello, " + name + "!"));
 }


Build and run the application and go to url http://localhost:8080/exampleFurther3?name=My%20Developer%20Planet. This gives us the expected result:

Hello, My Developer Planet!


The test we add is similar to the previous tests we have written:

@Test
public void testExampleWithParameter() {
  webTestClient
    .get().uri("/exampleFurther3?name=My Developer Planet")
    .accept(MediaType.TEXT_PLAIN)
    .exchange()
    .expectStatus().isOk()
    .expectBody(String.class).isEqualTo("Hello, My Developer Planet!");
}


To conclude with, we add a test which would return a 404 http response. The expected status to an unresolved url path is tested with the isNotFound method:

@Test
public void testExampleWithError() {
  webTestClient
    .get().uri("/example/something")
    .accept(MediaType.TEXT_PLAIN)
    .exchange()
    .expectStatus().isNotFound();
}


Summary

In this post I tried to give some information on how to get started with Spring Webflux:

  • Suggested reading before getting started with Reactive programming
  • A basic Spring Webflux example provided by Spring
  • A few basic Spring Webflux examples in order to perform some first steps

It must be clear that this is only very basic information, and from this point on, there is a lot of other things to explore concerning Spring Webflux.

Spring Framework unit test Reactive Streams Stream processing application Data (computing) Spring Boot Java (programming language) POST (HTTP) Web Protocols

Published at DZone with permission of Gunter Rotsaert, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Spring Boot - Microservice- JaxRS Jersey - HATEOAS - JerseyTest - Integration
  • Java, Spring Boot, and MongoDB: Performance Analysis and Improvements
  • Build Reactive REST APIs With Spring WebFlux
  • Spring Cloud Stream: A Brief Guide

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!