Quantcast
Channel: Tech Tutorials
Viewing all articles
Browse latest Browse all 895

Spring Web Reactive - Spring WebFlux Example Using Annotation-Based Programming

$
0
0

This post Spring Web Reactive Framework - Spring WebFlux Tutorial gives an overview of Spring web reactive. Building on that knowledge in this post we’ll see a Spring web reactive example using Spring WebFlux annotation-based programming model where @Controller and @RestController components use annotations to express request mappings, request input, exception handling, and more. The application built here is a RESTful web service with Spring Webflux and also includes a WebClient consumer of that service. Application uses Spring Boot and run on the default Netty server.

Maven dependency for Spring WebFlux


<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.netjs</groupId>
<artifactId>reactive</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>reactive</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdk.version>1.10</jdk.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

The dependency you need to add is spring-boot-starter-webflux which takes care of getting all the jar dependencies like reactor-core, reactive-streams, spring-webflux along with the Netty server. Spring Boot 2 uses Netty by default with WebFlux because Netty is more widely used in the async, non-blocking space and also provides both client and server that can share resources.

Spring reactive WebFlux example – annotation-based programming

For Spring WebFlux annotation-based programming model you need to provide the following components-

  1. Controller class- You can define controller beans using a standard Spring bean definition. The @Controller stereotype allows for auto-detection, aligned with Spring general support for detecting @Component classes in the classpath and auto-registering bean definitions for them.
  2. @RequestMapping- The @RequestMapping annotation is used to map requests to controllers methods. It has various attributes to match by URL, HTTP method, request parameters, headers, and media types.

    There are also HTTP method specific shortcut variants of @RequestMapping:

    • @GetMapping
    • @PostMapping
    • @PutMapping
    • @DeleteMapping
    • @PatchMapping

In the example here we also have a model bean User and a reactive repository that is exposed in the handler class for the data operations. To keep the application simple we’ll use a Map to store data rather than going to an actual reactive repository like reactive Mongo.

Spring Reactive WebFlux application - Model bean

The model bean used is User.


public class User {
private long id;
private String firstName;
private String lastName;
private String email;

public User() {

}
public User(long id, String firstName, String lastName, String email) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

Spring Reactive WebFlux application – Repository


public interface UserRepository {
Mono<User> getUserById(int id);

Flux<User> getAllUsers();

Mono<Void> saveUser(Mono<User> user);
}

As you can see there are three methods-

  1. getUserById fetches a single User by Id. This method returns a Mono.
  2. getAllUsers fetches all the Users. This method returns a Flux.
  3. saveUser method saves the passed User object. This method returns Mono<Void> which is an empty Mono that emits a completion signal when the user has been read from the request and stored.

UserRepositoryImpl.java


import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.netjs.model.User;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Repository
public class UserRepositoryImpl implements UserRepository {

Map<Integer, User> userMap = new ConcurrentHashMap<Integer, User>();
public UserRepositoryImpl(){
userMap.put(1, new User(1, "Robert", "Ludlum", "rl@rl.com"));
userMap.put(2, new User(2, "John", "Grisham", "jg@jg.com"));
userMap.put(3, new User(3, "James", "Patterson", "jp@jp.com"));
}

@Override
public Mono<User> getUserById(int id) {
return Mono.justOrEmpty(userMap.get(id));
}

@Override
public Flux<User> getAllUsers() {
// get as stream
return Flux.fromStream(userMap.values().stream());
}

@Override
public Mono<Void> saveUser(Mono<User> user) {
Mono<User> userMono = user.doOnNext(value->{
userMap.put((userMap.keySet().size() +1), value);
} );
return userMono.then();
}
}

Spring Reactive WebFlux application – Controller Class

Controller end points are grouped together in a UserController class. UserController class is annotated with @RestController annotation.


import org.netjs.model.User;
import org.netjs.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
public class UserController {
@Autowired
private UserRepository userRepository;

@GetMapping("/user")
public Flux<User> listUser() {
Flux<User> user = userRepository.getAllUsers();
return user;
}

@GetMapping("/user/{id}")
Mono<User> findById(@PathVariable int id) {
return this.userRepository.getUserById(id);
}

@PostMapping("/user")
Mono<Void> create(@RequestBody Mono<User> userStream) {
return this.userRepository.saveUser(userStream).then();
}
}
  • listUser controller end point returns all User objects found in the repository as Flux.
  • createUser is a handler function that stores a new User contained in the request body. UserRepository.saveUser(User) returns Mono<Void>.
  • getUser is a handler function that returns a single user, identified via the path variable id. That user is retrieved from the repository using that id.

Create a WebClient

For reactive applications, Spring framework offers the WebClient class, which is non-blocking. We’ll use a WebClient implementation to consume our RESTful service.


import java.util.List;

import org.netjs.model.User;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class UserWebClient {
private WebClient client = WebClient.create("http://localhost:8080");
// For getting all users
private Mono<ClientResponse> result = client.get()
.uri("/user")
.accept(MediaType.APPLICATION_JSON_UTF8)
.exchange();

/* // Getting user by ID
private Mono<User> singleUser = client.get()
.uri("/user/{1}")
.accept(MediaType.APPLICATION_JSON_UTF8)
.exchange()
.flatMap(res -> res.bodyToMono(User.class));*/


public List<User> getResult() {
Flux<User> userList = result.flatMapMany(res -> res.bodyToFlux(User.class));
return userList.collectList().block();
}
}

Making the Application Executable

We’ll configure the Spring WebFlux application as a standalone application using the @SpringBootApplication convenience annotation.


@SpringBootApplication
@ComponentScan(basePackages={"org.netjs.reactive", "org.netjs.repository"})
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
UserWebClient uwc = new UserWebClient();
System.out.println(uwc.getResult());
}
}

The main() method uses Spring Boot’s SpringApplication.run() method to launch an application.

Running the Spring WebFlux REST application

Now we have the WebClient to consume the Restful service and the Spring boot configuration to launch the application.

You can run the above class as stand alone Java application.

You can also run the application from the command line with Gradle or Maven. You can also build a single executable JAR file that contains all the necessary dependencies, classes, and resources, and run that.

With Maven, you can run the application using mvn spring-boot:run command from terminal. Or you can build the JAR file with mvn clean package. Then you can run the JAR file- java -jar target/reactive-0.0.1-SNAPSHOT.jar

When using mvn spring-boot:run command from your project directory to launch the application, on the successful start of the application you should see on the console that the netty server is started and listening on default port 8080.


2018-11-09 12:10:47.325 INFO 9492 --- [ main] s.w.r.r.m.a.RequestMappingHandlerMapping : Mapped "{[/user],methods=[POST]}" onto reactor.core.publisher.Mono<java.lang.Void> org.netjs.reactive.UserController.create(reactor.core.publisher.Mono<org.netjs.model.User>)
2018-11-09 12:10:47.327 INFO 9492 --- [ main] s.w.r.r.m.a.RequestMappingHandlerMapping : Mapped "{[/user],methods=[GET]}" onto public reactor.core.publisher.Flux<org.netjs.model.User> org.netjs.reactive.UserController.listUser()
2018-11-09 12:10:47.328 INFO 9492 --- [ main] s.w.r.r.m.a.RequestMappingHandlerMapping : Mapped "{[/user/{id}],methods=[GET]}" onto reactor.core.publisher.Mono<org.netjs.model.User> org.netjs.reactive.UserController.findById(int)

2018-11-09 12:10:47.429 INFO 9492 --- [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.reactive.resource.ResourceWebHandler]
2018-11-09 12:10:47.430 INFO 9492 --- [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.reactive.resource.ResourceWebHandler]
2018-11-09 12:10:47.696 INFO 9492 --- [ main] o.s.w.r.r.m.a.ControllerMethodResolver : Looking for @ControllerAdvice: org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext@72c8e7b: startup date [Fri Nov 09 12:10:45 IST 2018]; root of context hierarchy
2018-11-09 12:10:48.533 INFO 9492 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-11-09 12:10:48.945 INFO 9492 --- [ctor-http-nio-1] r.ipc.netty.tcp.BlockingNettyContext : Started HttpServer on /0:0:0:0:0:0:0:0:8080

Since Spring boot uses Netty Server by default so application is deployed to the server automatically and you can access it by giving the following URL in browser to fetch all the Users -

http://localhost:8080/user
Spring WebFlux annotation based example

To get user by ID

http://localhost:8080/user/2
Spring reactive webflux annotation based example

To save a user

To save a user you can use the following curl command.


curl localhost:8080/user/create -H "Content-Type: application/json" -X POST -d "{\"id\":4, \"firstName\":\"Dean\",\"lastName\":\"Koontz\", \"email\":\"dk@dk.com\"}"
Spring WebFlux annotation based example

That's all for this topic Spring Web Reactive - Spring WebFlux Example Using Annotation-Based Programming. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Spring Transaction Management JDBC Example Using @Transactional Annotation
  2. Spring MVC Configuring Multiple View Resolvers Example
  3. Select Query Using JDBCTemplate in Spring Framework
  4. Sending Email Using Spring Framework Example
  5. Autodiscovery of Bean Using componenent-scan in Spring

You may also like -

>>>Go to Spring Tutorial Page


Viewing all articles
Browse latest Browse all 895

Trending Articles