This guide walks you through the process of creating an asynchronous, event-driven system using the Reactor project.

What you’ll build

You’ll build an application that fires off events to fetch a random Spring Boot quote, and then asynchronously gathers them together.

What you’ll need

How to complete this guide

Like most Spring Getting Started guides, you can start from scratch and complete each step, or you can bypass basic setup steps that are already familiar to you. Either way, you end up with working code.

To start from scratch, move on to Build with Gradle.

To skip the basics, do the following:

  • Download and unzip the source repository for this guide, or clone it using Git: git clone https://github.com/spring-guides/{project_id}.git

  • cd into {project_id}{initial}

  • Jump ahead to [initial].

When you’re finished, you can check your results against the code in {project_id}{complete}.

Build with Gradle

Build with Gradle

Unresolved directive in https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/hide-show-gradle.adoc - include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/build_system_intro.adoc[] === Create the directory structure

In a project directory of your choosing, create the following subdirectory structure; for example, with mkdir -p src/main/java/hello on *nix systems:

└── src
    └── main
        └── java
            └── hello

Create a Gradle build file

build.gradle

Unresolved directive in https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/hide-show-gradle.adoc - include::https://raw.githubusercontent.com/spring-guides/gs-messaging-reactor/master/initial/build.gradle[]

The https://github.com/spring-projects/spring-boot/tree/master/spring-boot-tools/spring-boot-gradle-plugin[Spring Boot gradle plugin] provides many convenient features:

- It collects all the jars on the classpath and builds a single, runnable "über-jar", which makes it more convenient to execute and transport your service.
- It searches for the `public static void main()` method to flag as a runnable class.
- It provides a built-in dependency resolver that sets the version number to match https://github.com/spring-projects/spring-boot/blob/master/spring-boot-dependencies/pom.xml[Spring Boot dependencies]. You can override any version you wish, but it will default to Boot's chosen set of versions.


[[reveal-maven]]
[.reveal-maven]
== Build with Maven

[[use-maven]]
[.use-maven]
== Build with Maven


:linkattrs:

First you set up a basic build script. You can use any build system you like when building apps with Spring, but the code you need to work with https://maven.apache.org[Maven] is included here. If you're not familiar with Maven, refer to link:/guides/gs/maven[Building Java Projects with Maven].

Unresolved directive in https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/hide-show-maven.adoc - include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/create_directory_structure_hello.adoc[]
`pom.xml`
[source,xml]

<?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>org.springframework</groupId>
<artifactId>gs-messaging-reactor</artifactId>
<version>0.2.0</version>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.1.10.RELEASE</version>
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectreactor.spring</groupId>
        <artifactId>reactor-spring-context</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
<repositories>
    <repository>
        <id>spring-snapshots</id>
        <url>https://repo.spring.io/libs-snapshot</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>
<pluginRepositories>
    <pluginRepository>
        <id>spring-snapshots</id>
        <url>https://repo.spring.io/libs-snapshot</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </pluginRepository>
</pluginRepositories>

</project>

The https://github.com/spring-projects/spring-boot/tree/master/spring-boot-tools/spring-boot-maven-plugin[Spring Boot Maven plugin] provides many convenient features:

- It collects all the jars on the classpath and builds a single, runnable "über-jar", which makes it more convenient to execute and transport your service.
- It searches for the `public static void main()` method to flag as a runnable class.
- It provides a built-in dependency resolver that sets the version number to match https://github.com/spring-projects/spring-boot/blob/master/spring-boot-dependencies/pom.xml[Spring Boot dependencies]. You can override any version you wish, but it will default to Boot's chosen set of versions.


Unresolved directive in <stdin> - include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/hide-show-sts.adoc[]


[[initial]]
== Create a representation for a quote
For this event-driven example, you'll fetch quotes from http://gturnquist-quoters.cfapps.io/api[The Spring Boot quote database]. The link:/understanding/JSON[JSON] format looks like this:

[source,json]

{ "type": "success", "value": { "id": 10, "quote": "Really loving Spring Boot, makes stand alone Spring apps easy." } }

The easiest thing to do is capture the inner `value`, i.e. the quote, with one class and then wrap the whole in another class.

.`src/main/java/hello/Quote.java`
[source,java]

package hello;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown=true) public class Quote {

Long id;
String quote;
public Long getId() {
    return id;
}
public void setId(Long id) {
    this.id = id;
}
public String getQuote() {
    return quote;
}
public void setQuote(String quote) {
    this.quote = quote;
}

}

This class contains both the `id` and the `quote` text supplied from the website. `@JsonIgnoreProperties(ignoreUnknown=true)` signals that any other attributes are to be ignored.

The wrapper class looks like this:

.`src/main/java/hello/QuoteResource.java`
[source,java]

package hello;

public class QuoteResource {

String type;
Quote value;
public String getType() {
    return type;
}
public void setType(String type) {
    this.type = type;
}
public Quote getValue() {
    return value;
}
public void setValue(Quote value) {
    this.value = value;
}

}

The wrapper class has the `type` attribute along with a `Quote`. This makes it easy later to use Spring's `RestTemplate` and convert JSON to a POJO with the Jackson binding library.

== Create a receiver

An asynchronous application has publishers and receivers. To create the receiver, implement a receiver with a method to respond to events:

`src/main/java/hello/Receiver.java`
[source,java]

package hello;

import java.util.concurrent.CountDownLatch;

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate;

import reactor.event.Event; import reactor.function.Consumer;

@Service class Receiver implements Consumer<Event<Integer>> {

@Autowired
CountDownLatch latch;
RestTemplate restTemplate = new RestTemplate();
public void accept(Event<Integer> ev) {
    QuoteResource quoteResource = restTemplate.getForObject("http://gturnquist-quoters.cfapps.io/api/random", QuoteResource.class);
    System.out.println("Quote " + ev.getData() + ": " + quoteResource.getValue().getQuote());
    latch.countDown();
}

}

The `Receiver` implements the `Consumer` interface by implementing the `accept()` method. It is geared to receive `Event<Integer>`.

For this example, every time the `Receiver` receives an integer, it fetches another Spring Boot quote using Spring's `RestTemplate`. Then it signals its completion to the `CountDownLatch` to coordinate when all events have been processed.

`Receiver` has the `@Service` annotation so it will be automatically registered with the link:/understanding/application-context[application context].


== Create a publisher

The next step is to publish a handful of events to the reactor.

`src/main/java/hello/Publisher.java`
[source,java]

package hello;

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import reactor.core.Reactor; import reactor.event.Event;

import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger;

@Service public class Publisher {

@Autowired
Reactor reactor;
@Autowired
CountDownLatch latch;
public void publishQuotes(int numberOfQuotes) throws InterruptedException {
    long start = System.currentTimeMillis();
AtomicInteger counter = new AtomicInteger(1);
for (int i=0; i < numberOfQuotes; i++) {
    reactor.notify("quotes", Event.wrap(counter.getAndIncrement()));
}
latch.await();
long elapsed = System.currentTimeMillis()-start;
    System.out.println("Elapsed time: " + elapsed + "ms");
    System.out.println("Average time per quote: " + elapsed/ numberOfQuotes + "ms");
}

}

The code uses a for loop to publish a fixed number of events. An `AtomicInteger` is used to fashion a unique number, which gets turned into a Reactor event with `Event.wrap()`. The event is published to the **quotes** channel using `reactor.notify()`.

NOTE: Reactor events can contain any type of POJO. This guide uses a very simple integer, but a more detailed event can be used if more information needs to be transmitted to the receiver.

`Receiver` has the `@Service` annotation so it will be automatically registered with the application context.

NOTE: The code is a bit contrived in that it manually sends a fixed number of integers. In production, this would be replaced by some triggering input, perhaps using Reactor's `TcpServer` to respond to incoming data.

== Create an Application class

The final step in putting together your application is to register the components and then invoke them.

`src/main/java/hello/Application.java`
[source,java]

package hello;

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import reactor.core.Environment; import reactor.core.Reactor; import reactor.core.spec.Reactors;

import java.util.concurrent.CountDownLatch;

import static reactor.event.selector.Selectors.$;

@Configuration @EnableAutoConfiguration @ComponentScan public class Application implements CommandLineRunner {

private static final int NUMBER_OF_QUOTES = 10;
@Bean
Environment env() {
    return new Environment();
}
@Bean
Reactor createReactor(Environment env) {
    return Reactors.reactor()
            .env(env)
            .dispatcher(Environment.THREAD_POOL)
            .get();
}
@Autowired
private Reactor reactor;
@Autowired
private Receiver receiver;
@Autowired
private Publisher publisher;
@Bean
public CountDownLatch latch() {
    return new CountDownLatch(NUMBER_OF_QUOTES);
}
@Override
public void run(String... args) throws Exception {
    reactor.on($("quotes"), receiver);
    publisher.publishQuotes(NUMBER_OF_QUOTES);
}
public static void main(String[] args) throws InterruptedException {
    SpringApplication.run(Application.class, args);
}

}

The Reactor environment is defined with the `environment()` method. The environment gets fed into the `reactor()` method to create an asynchronous reactor. In this case, you are using the `THREAD_POOL` dispatcher.

[NOTE]
====
Reactor has four dispatchers to pick from: **synchronous**, **ring buffer**, **thread pool**, and **event loop**.

- **Synchronous** is typically used inside a consumer, especially if you use `Stream` s and `Promise` s.
- **Ring buffer** is used for large volumes of non-blocking events and is based on the http://martinfowler.com/articles/lmax.html[LMAX disruptor].
- **Thread pool** is ideal for longer running tasks that might be IO bound, and when it doesn't matter what thread they are run on.
- **Event loop** is used when you need all events on the exact same thread.
====

It also defines the number of events to send at the top with `NUMBER_OF_QUOTES` and creates a `CountDownLatch` with the `latch()` method.

The `Application` class is tagged with the `@Configuration` and `@ComponentScan` annotations. This lets it define the application context while also scanning the `hello` package for the `@Service` objects.

The `main()` method fetches the `reactor` and the `receiver`. It then registers the `receiver` to digest events on the "quotes" selector. With everything registered, it uses the `Publisher` to send out a series of quote-fetching events.

The `CountDownLatch` then waits until every thread reports that it's done before proceeding.

== Build an executable JAR

:linkattrs:

If you are using Gradle, you can run the application using `./gradlew bootRun`.

You can build a single executable JAR file that contains all the necessary dependencies, classes, and resources. This makes it easy to ship, version, and deploy the service as an application throughout the development lifecycle, across different environments, and so forth.

[subs="attributes", role="has-copy-button"]
....
./gradlew build
....

Then you can run the JAR file:

[subs="attributes", role="has-copy-button"]
....
java -jar build/libs/{project_id}-0.1.0.jar
....

If you are using Maven, you can run the application using `mvn spring-boot:run`. Or you can build the JAR file with `mvn clean package` and run the JAR by typing:

[subs="attributes", role="has-copy-button"]
....
java -jar target/{project_id}-0.1.0.jar
....

NOTE: The procedure above will create a runnable JAR. You can also opt to link:/guides/gs/convert-jar-to-war/[build a classic WAR file] instead.



You should see output similar to this:

....
Quote 5: The real benefit of Boot, however, is that it's just Spring. That means any direction the code takes, regardless of complexity, I know it's a safe bet.
Quote 8: The real benefit of Boot, however, is that it's just Spring. That means any direction the code takes, regardless of complexity, I know it's a safe bet.
Quote 7: Working with Spring Boot is like pair-programming with the Spring developers.
Quote 1: Really loving Spring Boot, makes stand alone Spring apps easy.
Quote 3: @springboot with @springframework is pure productivity! Who said in #java one has to write double the code than in other langs? #newFavLib
Quote 2: Really loving Spring Boot, makes stand alone Spring apps easy.
Quote 4: Really loving Spring Boot, makes stand alone Spring apps easy.
Quote 6: Previous to Spring Boot, I remember XML hell, confusing set up, and many hours of frustration.
Quote 10: Spring has come quite a ways in addressing developer enjoyment and ease of use since the last time I built an application using it.
Quote 9: Really loving Spring Boot, makes stand alone Spring apps easy.
Elapsed time: 206ms
Average time per quote: 20ms
....

The events were dispatched in order, one through ten. But the output shows that they were consumed asynchronously due to the results being out of order.


== Summary
Congratulations! You've just developed an asynchronous, message-driven system using the Reactor project. This is just the beginning of what you can build with it.