Reactive programming is about non-blocking applications. And Spring Framework 5 includes a new spring-webflux
module, supports Reactive Streams for communicating backpressure across async components and libraries. So in the tutorial, JavaSampleApproach will guide you through the steps for creating a SpringBoot WebFlux Annotation-based restful APIs.
Related posts:
– SpringBoot WebFlux Functional RestAPIs
– Reactor – Simple Ways to create Flux/Mono
– Spring WebClient with Spring Webflux | SpringBoot 2
– SpringBoot WebFlux Test
Contents
I. Technologies
– Java: 1.8
– Maven: 3.3.9
– Spring Tool Suite: Version 3.9.0.RELEASE
– Spring Boot: 2.0.0.M4
– Spring Boot Starter Webflux
II. Spring WebFlux
Spring Framework 5.0 supports WebFlux with fully asynchronous and non-blocking and does NOT require the Servlet API(Unlike Spring MVC).
Spring WebFlux supports 2 distinct programming models:
– Annotation-based with @Controller
– Functional with Java 8 lambda style
In the tutorial, we will introduce WebFlux with Annotation-based.
For starting with WebFlux, SpringBoot supports a collection dependency: spring-boot-starter-webflux
.
Sample code:
@RestController @RequestMapping(value="/api/customer") public class RestControllerAPIs { @GetMapping("/") public FluxgetAll() { ... } @GetMapping("/{id}") public Mono getCustomer(@PathVariable Long id) { ... } }
– reactor.core.publisher.Flux
: is a standard Publisher
representing a reactive sequence of 0..N items, optionally terminated by either a success signal or an error.
– reactor.core.publisher.Mono
: Mono
is a specialized Publisher
that emits at most single-valued-or-empty result.
III. Practice
In the tutorial, We create a SpringBoot project as below:
Step to do:
– Create SpringBoot project
– Create data model
– Implement Spring WebFlux APIs
– Run and check results
1. Create SpringBoot project
Using SpringToolSuite, create a SpringBoot project with Reactive Web
dependency:
Check pom.xml
after creating:
org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-test test io.projectreactor reactor-test test org.springframework.boot spring-boot-maven-plugin spring-snapshots Spring Snapshots https://repo.spring.io/snapshot true spring-milestones Spring Milestones https://repo.spring.io/milestone false spring-snapshots Spring Snapshots https://repo.spring.io/snapshot true spring-milestones Spring Milestones https://repo.spring.io/milestone false
2. Create data model
– Create a Customer model class:
package com.javasampleapproach.webflux.model; public class Customer { private long custId; private String firstname; private String lastname; private int age; public Customer(){} public Customer(long custId, String firstname, String lastname, int age){ this.custId = custId; this.firstname = firstname; this.lastname = lastname; this.age = age; } public long getCustId() { return custId; } public void setCustId(Long custId) { this.custId = custId; } 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 int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { String info = String.format("custId = %d, firstname = %s, lastname = %s, age = %d", custId, firstname, lastname, age); return info; } }
3. Implement Spring WebFlux APIs
Implement WebFlux APIs:
package com.javasampleapproach.webflux.controller; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; import javax.annotation.PostConstruct; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; 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.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.javasampleapproach.webflux.model.Customer; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @RestController @RequestMapping(value="/api/customer") public class RestControllerAPIs { MapcustStores = new HashMap (); @PostConstruct public void initIt() throws Exception { custStores.put(Long.valueOf(1), new Customer(1, "Jack", "Smith", 20)); custStores.put(Long.valueOf(2), new Customer(2, "Peter", "Johnson", 25)); } @GetMapping("/") public Flux getAll() { return Flux.fromIterable(custStores.entrySet().stream() .map(entry -> entry.getValue()) .collect(Collectors.toList())); } @GetMapping("/{id}") public Mono getCustomer(@PathVariable Long id) { return Mono.justOrEmpty(custStores.get(id)); } @PostMapping("/post") public Mono > postCustomer(@RequestBody Customer customer){ // do post custStores.put(customer.getCustId(), customer); // log on console System.out.println("########### POST:" + customer); return Mono.just(new ResponseEntity<>("Post Successfully!", HttpStatus.CREATED)); } @PutMapping("/put/{id}") public Mono > putCustomer(@PathVariable Long id, @RequestBody Customer customer){ // reset customer.Id customer.setCustId(id); custStores.put(id, customer); // log on console System.out.println("########### PUT:" + customer); return Mono.just(new ResponseEntity<>(customer, HttpStatus.CREATED)); } @DeleteMapping("/delete/{id}") public Mono > deleteMethod(@PathVariable Long id) { // delete processing custStores.remove(id); return Mono.just(new ResponseEntity<>("Delete Succesfully!", HttpStatus.ACCEPTED)); } }
4. Run and check results
Build and run the SpringBoot project with commandlines: {mvn clean install
, mvn spring-boot:run
}
– See console’ logs:
Mapped "{[/api/customer/],methods=[GET]}" onto public reactor.core.publisher.Fluxcom.javasampleapproach.webflux.controller.RestControllerAPIs.getAll() Mapped "{[/api/customer/{id}],methods=[GET]}" onto public reactor.core.publisher.Mono com.javasampleapproach.webflux.controller.RestControllerAPIs.getCustomer(java.lang.Long) Mapped "{[/api/customer/post],methods=[POST]}" onto public reactor.core.publisher.Mono > com.javasampleapproach.webflux.controller.RestControllerAPIs.postCustomer(com.javasampleapproach.webflux.model.Customer) Mapped "{[/api/customer/delete/{id}],methods=[DELETE]}" onto public reactor.core.publisher.Mono > com.javasampleapproach.webflux.controller.RestControllerAPIs.deleteMethod(java.lang.Long) Mapped "{[/api/customer/put/{id}],methods=[PUT]}" onto public reactor.core.publisher.Mono > com.javasampleapproach.webflux.controller.RestControllerAPIs.putCustomer(java.lang.Long,com.javasampleapproach.webflux.model.Customer) Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.reactive.resource.ResourceWebHandler] Looking for @ControllerAdvice: org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext@42dce8e7: startup date [Wed Sep 20 18:02:45 ICT 2017]; root of context hierarchy Started HttpServer on /0:0:0:0:0:0:0:0:8080 Netty started on port(s): 8080
– Make a GET all customer request: http://localhost:8080/api/customer/
– Make a GET customer request: http://localhost:8080/api/customer/1/
– Make a POST request: http://localhost:8080/api/customer/post
– Make a PUT request: http://localhost:8080/api/customer/put/3
– Make a DELETE request: http://localhost:8080/api/customer/delete/1
– Make a GET all customers request: http://localhost:8080/api/customer/
IV. Sourcecode
SpringBootWebFluxAnnotationBased
How do I convert CompletableFuture to a Flux or Mono?
I would love to design my services layer to depend on CompletableFutures and convert it to Flux and Mono on a web layer.