Spring Boot Reactive Tutorial
1. Overview
Spring 5, which will release later this year, will support building asynchronous and Reactive applications.
This is a simple tutorial showing the new features in Spring and how to create a web application. The application will connect to a database, have basic authentication, and be Reactive.
2. Reactive Programming
Reactive programming is about building asynchronous, non-blocking, and event-driven applications that can easily scale.
Each event is published to subscribers while ensuring that the subscribers are never overwhelmed.
Mono
and Flux
are implementations of the Publisher
interface. A Flux
will observe 0 to N items and eventually terminate successfully or not. A Mono
will observe 0 or 1 item, with Mono<Void>
hinting at most 0 items.
To learn more about Reactive Programming, you can refer to this article.
3. Dependencies
We'll use Gradle to build our project. I recommend using Spring Initializr for bootstrapping your project.
We'll use:
- Spring Boot 2
- Spring Webflux
- Spring Reactive Data MongoDB
- Spring Security Reactive Webflux
- Lombok
Not all the Spring libraries have a stable release yet.
Lombok is used to reduce boilerplate code for models and POJOs. It can generate setters/getters, default constructors, toString, etc. methods automatically.
buildscript {
ext {
springBootVersion = '2.0.0.M2'
}
...
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-mongodb-reactive')
compile('org.springframework.boot:spring-boot-starter-webflux')
compile('org.springframework.security:spring-security-core')
compile('org.springframework.security:spring-security-config')
compile('org.springframework.security:spring-security-webflux')
compileOnly('org.projectlombok:lombok')
...
}
4. Auto-Configuration
We'll leave Spring Boot to automatically configure our application based on the dependencies added.
@SpringBootApplication
@EnableReactiveMongoRepositories
@EnableWebFluxSecurity
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
For using non-default values in our application configuration, we can specify them as properties and Spring Boot will automatically use them to create beans.
spring.data.mongodb.database=demo
All beans necessary for MongoDB, Web, and Security will be automatically created.
5. Database
We'll be using MongoDB in our example and a simple POJO. A PersonRepository
bean will be created automatically.
@Data
@NoArgsConstructor
@Document
public class Person {
@Id
private String id;
private String name;
}
public interface PersonRespository extends ReactiveMongoRepository<Person, String> {
Flux<Person> findByName(String name);
}
6. Web API
We'll create REST endpoints for Person
.
Spring 5 added support for creating routes functionally while still supporting the traditional, annotation-based way of creating them.
Let's look at both of them with the help of examples.
6.1. Annotation-Based
This is the traditional way of creating endpoints.
@RestController
@RequestMapping("/person")
public class PersonController {
@Autowired
private PersonRespository personRespository;
@GetMapping
public Flux<Person> index() {
return personRespository.findAll();
}
}
This will create a REST endpoint, which will return all the Person
records reactively.
6.2. Router Functions
This is a new and concise way of creating endpoints.
@Bean
RouterFunction<?> routes(PersonRespository personRespository) {
return nest(path("/person"),
route(RequestPredicates.GET("/{id}"),
request -> ok().body(personRespository.findById(request.pathVariable("id")), Person.class))
.andRoute(method(HttpMethod.POST),
request -> {
personRespository.insert(request.bodyToMono(Person.class)).subscribe();
return ok().build();
})
);
}
The nest
method is used to create nested routes, where a group of routes share a common path (prefix), header, or other RequestPredicate
.
So, in our case, all the corresponding routes have the common prefix /person.
In the first route, we have exposed a GET API /person/{id}, which will retrieve the corresponding record and return it.
In the second route, we have exposed a POST API /person, which will receive a Person
object and save it in the DB.
The cURL commands for the same:
curl http://localhost:8080/person -v -u tom:password
curl http://localhost:8080/person/{id} -v -u tom:password
curl http://localhost:8080/person -X POST -d '{"name":"John Doe","age":20}' -H "Content-Type: application/json" -v -u tom:password
We should define the routes in a Spring configuration file.
7. Security
We'll be using a very simple basic authentication mechanism in our example.
@Bean
UserDetailsRepository userDetailsRepository() {
UserDetails tom = withUsername("tom").password("password").roles("USER").build();
UserDetails harry = withUsername("harry").password("password").roles("USER", "ADMIN").build();
return new MapUserDetailsRepository(tom, harry);
}
We have added some users for our application and assigned different roles to them.
8. Conclusion
I have tried explaining, with a simple example, how to build a simple Reactive web application using Spring Boot.
You can read more about:
Spring Web Reactive
Spring Data Reactive
Spring Functional Web Framework
You can find the complete example on GitHub.