📜 ⬆️ ⬇️

Reactive programming with Spring Boot 2. Part 2


In the first part, we learned what reactivity is and how to work with it at a basic level. If you want to continue learning reactive programming with the new framework from Spring, then welcome!

Now we will analyze what to do with the knowledge gained from the previous article, and also build a small web service that will work in asynchronous mode.

For this we need:


All settings and the final project can be viewed on github .
')
Let's get started

If you have not connected all the dependencies, you can do it right now. In the gradle they will look like this:

compile('org.springframework.boot:spring-boot-starter-data-mongodb-reactive') compile('org.springframework.boot:spring-boot-starter-webflux') compileOnly('org.projectlombok:lombok') testCompile('org.springframework.boot:spring-boot-starter-test') testCompile('io.projectreactor:reactor-test') 


So, we have everything ready. First, create a user of our application:
 @Data @AllArgsConstructor @NoArgsConstructor @Document public class User { @Id private String id; private String firstName; private String lastName; } 

I remind you that in order to successfully compile and work in IntelliJ Idea, you must tick the enable annotation processing box or write all the setters, heaters and constructors yourself.

Next, create a repository with our users:

 import org.faoxis.habrreactivemongo.domain.User; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; public interface UserRepository extends ReactiveMongoRepository<User, String> { } 

It should be noted here that we inherit from a special interface for working in jet mode. If you look at the ReactiveMongoRepository interface, you can see that objects are returned to us, wrapped in the already familiar classes Mono and Flux . This means that with any access to the database, we do not immediately get the result. Instead, we get a stream of data from which we can get data as soon as it is ready.

Currently, multi-layered architecture is the most common solution when working with microservice architecture. It is really very convenient. Let's create a service layer. To do this, we make the appropriate interface:

 public interface UserService { Flux<User> get(); Mono<User> save(User user); } 

Our service is quite simple, so we immediately create several useful methods. And then we implement them:

 @Service @AllArgsConstructor public class UserServiceImpl implements UserService { private UserRepository userRepository; @Override public Flux<User> get() { return userRepository.findAll(); } @Override public Mono<User> save(User user) { return userRepository.save(user); } } 

Here it is worth noting that the implementation of the UserRepository dependency occurs through the constructor using the AllArgsConstructor annotation. Just in case, let me remind you that with some version of Spring 4 you can automatically implement dependency injection through the designer without the Autowire annotation.

And finally, let's make the controller:

 import lombok.AllArgsConstructor; import org.faoxis.habrreactivemongo.domain.User; import org.faoxis.habrreactivemongo.service.UserService; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @RestController @RequestMapping("/users") @AllArgsConstructor public class UserController { private UserService userService; @PostMapping public Mono<User> post(@RequestBody User user) { return userService.save(user); } @GetMapping public Flux<User> get() { return userService.get(); } } 

Run our application. Everything should work. Now we will make a POST request to localhost : 8080 / users with the following contents:

 { "firstName": "Peter", "lastName": "Griffin" } 

In response, we will get the same object, but with the id assigned to it:
 { "id": "5a0bf0fdc48fd53478638c9e", "firstName": "Peter", "lastName": "Griffin" } 

Fine! Let's save a couple more users and try to see what we already have in the database. I have such a result GET request on localhost : 8080 / users:

 [ { "id": "5a0bf0fdc48fd53478638c9e", "firstName": "Peter", "lastName": "Griffin" }, { "id": "5a0bf192c48fd53478638c9f", "firstName": "Lois", "lastName": "Griffin" }, { "id": "5a0bf19ac48fd53478638ca0", "firstName": "Mag", "lastName": "Griffin" } ] 

Fine! We have a whole service that works in asynchronous mode! But there is one more thing to keep in mind. Working with asynchronous repositories can greatly change the type of service that receives data from it.

To demonstrate this, create another method URL handler in our controller:

  @GetMapping("{lastName}") public Flux<User> getByLastName(@PathVariable(name = "lastName") String lastName) { return userService.getByLastName(lastName); } 

Everything is simple here. We get the user's last name and print all people with that last name.

Of course, this logic can be put on the database, but here I would like to pay attention to how it will look in the service:

  @Override public Flux<User> getByLastName(final String lastName) { return userRepository .findAll() .filter(user -> user.getLastName().equals(lastName)); } 

Note that there is a difference in working with data and data streams. Usually we perform some actions on the data directly. Here the situation is different. We say what should be done in the data stream.

Let's try to make a GET request at the URL localhost : 8080 / users / Griffin. I have the following result:

 [ { "id": "5a0bf0fdc48fd53478638c9e", "firstName": "Peter", "lastName": "Griffin" }, { "id": "5a0bf192c48fd53478638c9f", "firstName": "Lois", "lastName": "Griffin" }, { "id": "5a0bf19ac48fd53478638ca0", "firstName": "Mag", "lastName": "Griffin" } ] 

In this article, we looked at how to build an asynchronous service with the new WebFlux framework and Netty server (it comes out of the box by default). We also saw how easy it is to achieve with Spring Boot 2 . If you have a microservice architecture on a project, then most likely you can easily transfer your applications to WebFlux with the release of Spring Boot 2 if you wish (if, of course, there is a need for this).

PS There are several ideas for follow-up. If you are interested in the material, and you are not against my submission, then in the following sections we can consider, for example, asynchronous interaction between applications. Thanks for attention!

Source: https://habr.com/ru/post/342506/


All Articles