header

Getting Started with Spring Webflux

Spring Boot now embraces reactive programming which is a non-blocking asynchronous applications and event-driven. Spring Framework uses Reactor internally for its own reactive support. Reactor is a Reactive Streams implementation that further extends the basic Publisher contract with the Flux and Mono. NOTE: If you need to know what tools you need to have installed in your computer in order to create a Spring Boot basic project, please refer my previous post: Spring Boot. Let’s start creating a new Spring Boot project with Webflux, Mongo Reactive and Lombok as dependencies:

spring init --dependencies=webflux,data-mongodb-reactive,lombok --build=gradle --language=java reactive-webflux-workshop

Here is the complete build.gradle file generated:

buildscript {
  ext {
    springBootVersion = '2.1.0.RELEASE'
  }
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
  }
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.jos.dem.webflux'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
  mavenCentral()
}

dependencies {
  implementation('org.springframework.boot:spring-boot-starter-webflux')
  implementation('org.projectlombok:lombok:1.18.2')
  implementation('org.springframework.boot:spring-boot-starter-data-mongodb-reactive')
  testImplementation('org.springframework.boot:spring-boot-starter-test')
  testImplementation('io.projectreactor:reactor-test')
}

Now let’s create a simple POJO to store and retrieve information from our MongoDB.

package com.jos.dem.webflux.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import lombok.Data;
import lombok.AllArgsConstructor;

@Data
@Document
@AllArgsConstructor
public class Person {

  @Id
  private String uuid;
  private String nickname;
  private String email;

}

The @Data annotation generates setters and getters, toString(), equals(), hashcode() and a constructor for every required field. Lombok is a great tool to avoid boilerplate code, for knowing more please go here. Spring Data now is supporting a full reactive experience with MongoDB, Couchbase, Redis and Casandra, in this case let’s create a PersonRepository using ReactiveMongoRepository as we going to be reactive.

package com.jos.dem.webflux.repository;

import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import reactor.core.publisher.Flux;
import com.jos.dem.webflux.model.Person;

public interface PersonRepository extends ReactiveMongoRepository<Person, String> {}

Next, we are going to use CommandLineRunner to start our workflow. The CommandLineRunner is a call back interface in Spring Boot, when Spring Boot starts will call it and pass in args through a run() internal method.

package com.jos.dem.webflux;

import org.springframework.context.annotation.Bean;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class PersonApplication {

  public static void main(String[] args) {
    SpringApplication.run(PersonApplication.class, args);
  }

  @Bean
  CommandLineRunner start(){
    return args -> {
      System.out.println("Hello World!");
    };
  }

}

It is time to create a list of persons and store them in our MongoDB

package com.jos.dem.webflux;

import org.springframework.context.annotation.Bean;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import reactor.core.publisher.Flux;

import java.util.UUID;
import java.util.stream.Stream;

import com.jos.dem.webflux.model.Person;
import com.jos.dem.webflux.repository.PersonRepository;

@SpringBootApplication
public class PersonApplication {

  public static void main(String[] args) {
    SpringApplication.run(PersonApplication.class, args);
  }

  @Bean
  CommandLineRunner start(PersonRepository personRepository){
    return args -> {
      Stream.of("josdem", "tgrip", "edzero", "skuarch", "siedrix")
      .map(nickname -> new Person(UUID.randomUUID().toString(), nickname, nickname + "@email.com"))
      .forEach(person -> personRepository.save(person).subscribe());
    };
  }

}

IMPORTANT: The logic implemented in the operators is only executed when data starts to flow, and that does not happen until you use subscribe() method. That’s it, now we are storing Person objects to our MongoDB, let’s add some code to clean our database, insert and show persons.

package com.jos.dem.webflux;

import org.springframework.context.annotation.Bean;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import reactor.core.publisher.Flux;

import java.util.UUID;
import java.util.stream.Stream;

import com.jos.dem.webflux.model.Person;
import com.jos.dem.webflux.repository.PersonRepository;

@SpringBootApplication
public class PersonApplication {

  public static void main(String[] args) {
    SpringApplication.run(PersonApplication.class, args);
  }

  @Bean
  CommandLineRunner start(PersonRepository personRepository){
    return args -> {
      personRepository.deleteAll().subscribe();

      Stream.of("josdem", "tgrip", "edzero", "skuarch", "siedrix")
      .map(nickname -> new Person(UUID.randomUUID().toString(), nickname, nickname + "@email.com"))
      .forEach(person -> personRepository.save(person).subscribe());

      personRepository.findAll().log().subscribe(System.out::println);
    };
  }

}

In order to run this example you need to create a database in MongoDB with authorization: "enabled". Also do not forget to add your MongoDB credentials information to your application.properties file:

spring.data.mongodb.database=reactive_webflux
spring.data.mongodb.host=localhost
spring.data.mongodb.username=username
spring.data.mongodb.password=password

To run the project:

gradle bootRun

Using Maven

You can do the same using Maven, the only difference is that you need to specify --build=maven parameter in the spring init command line:

spring init --dependencies=webflux,data-mongodb-reactive,lombok --build=maven --language=java reactive-webflux-workshop

And when you run your project use this command:

mvn spring-boot:run

This is the pom.xml file generated:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.example</groupId>
	<artifactId>reactive-webflux-workshop</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>demo</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webflux</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</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>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

To browse the project go here, to download the project:

git clone https://github.com/josdem/reactive-webflux-workshop.git
git fetch
git checkout feature/setup

Return to the main article

comments powered by Disqus