Introduction: Mr. Tu Zipei, known as “China’s first Big data”, mentioned in his famous book Top of Data that Moore’s Law, social media and data mining are the three causes of big data. According to the IBM study, 90 percent of all data obtained by the entire human civilization has been generated in the past two years. In this context, a number of new technologies have emerged, including NoSQL, Hadoop, Spark, Storm, and Kylin. Among them, Reactive programming technology represented by RxJava and Reactor is aimed at Velocity in the classic definition of big data 4V (Volume, Variety, Velocity, Value), namely, high concurrency problem. In the upcoming Release of Spring 5, Responsive programming support has also been introduced. Over the next few weeks, I’ll be sharing some of my lessons on responsive programming in three installments. This is the third article to take a look at the upcoming release of Spring 5 at the end of next month with a simple Spring 5 sample application.

Previously:

  • 【Spring 5】 Responsive Web Framework foresight
  • An overview of responsive programming

1 review

With the introduction of the first two articles, you have gained a basic understanding of responsive programming and Spring 5. I will use a simple Spring 5 application as an example to show how to quickly build a responsive Web application (hereinafter referred to as RP application) using Spring 5.

2 of actual combat

2.1 Environment Preparation

First, download my sample application from GitHub at github.com/emac/spring… .

Then, download the latest version of MongoDB from the MongoDB website, and run Mongod & from the command line to start the service.

For now, try running the test cases that come with the project.

./gradlew clean build

2.2 Dependency Introduction

Next, take a look at the dependencies related to reactive programming in the sample application.

compile('org.springframework.boot:spring-boot-starter-webflux')
compile('org.springframework.boot:spring-boot-starter-data-mongodb-reactive')
testCompile('io.projectreactor.addons:reactor-test')Copy the code
  • spring-boot-starter-webflux: Enabling RP (Reactive Programming) support in Spring 5 is a requirement for developing RP applications using Spring 5, similar to spring-boot-starter-Web for traditional Spring MVC applications.
  • Spring-boot-starter-data-mongodb – Reactive: The reactive data extension library for mongodb introduced in Spring 5 allows unified RP-style apis to operate mongodb.
  • IO. Projectreactor. Addons: reactor – test: reactor (Spring framework of default to the RP 5) provide the official test tool library.

2.3 Sample Code

If you remember from the first article in this series, Spring 5 provides Spring MVC annotations and Router Functions for writing RP applications. In this article, I’ll show you how to write a simple RP Controller using the most familiar MVC annotations.

@RestController
public class RestaurantController {

    /** * Extends the ReactiveCrudRepository interface to provide basic CRUD operations */
    private final RestaurantRepository restaurantRepository;

    /** * Spring-boot-starter-data-mongodb -reactive */
    private final ReactiveMongoTemplate reactiveMongoTemplate;

    public RestaurantController(RestaurantRepository restaurantRepository, ReactiveMongoTemplate reactiveMongoTemplate) {
        this.restaurantRepository = restaurantRepository;
        this.reactiveMongoTemplate = reactiveMongoTemplate;
    }

    @GetMapping("/reactive/restaurants")
    public Flux<Restaurant> findAll(a) {
        return restaurantRepository.findAll();
    }

    @GetMapping("/reactive/restaurants/{id}")
    public Mono<Restaurant> get(@PathVariable String id) {
        return restaurantRepository.findById(id);
    }

    @PostMapping("/reactive/restaurants")
    public Flux<Restaurant> create(@RequestBody Flux<Restaurant> restaurants) {
        return restaurants
                .buffer(10000)
                .flatMap(rs -> reactiveMongoTemplate.insert(rs, Restaurant.class));
    }

    @DeleteMapping("/reactive/restaurants/{id}")
    public Mono<Void> delete(@PathVariable String id) {
        returnrestaurantRepository.deleteById(id); }}Copy the code

As you can see, implementing an RP Controller is very similar to an ordinary Controller. The core difference is that the two most basic data types in RP, Flux (corresponding to multiple values) and Mono (single value), are prefered, especially for method parameters and return values. Even empty return values should be wrapped as Mono

. The purpose of this is to enable the application to process data in a unified manner in accordance with the RP specification, ideally from the bottom database (or other external system calls) to the top Controller layer, all data does not touch the ground, through various Flux and Mono laid “pipe”, straight to the calling end. Just like the famous advertising slogan of Nongfu Spring, we don’t produce water, we are only the porters of nature.

2.4 Unit Tests

In contrast to unit testing for non-RP applications, unit testing for RP applications mainly uses a new testing tool class introduced in Spring 5, WebTestClient, which is designed to test RP applications.

@RunWith(SpringRunner.class)
@SpringBootTest
public class RestaurantControllerTests {

    @Test
    public void testNormal(a) throws InterruptedException {
        // start from scratch
        restaurantRepository.deleteAll().block();

        // prepare
        WebTestClient webClient = WebTestClient.bindToController(new RestaurantController(restaurantRepository, reactiveMongoTemplate)).build();
        Restaurant[] restaurants = IntStream.range(0.100)
                .mapToObj(String::valueOf)
                .map(s -> new Restaurant(s, s, s))
                .toArray(Restaurant[]::new);

        // create
        webClient.post().uri("/reactive/restaurants")
                .accept(MediaType.APPLICATION_JSON_UTF8)
                .syncBody(restaurants)
                .exchange()
                .expectStatus().isOk()
                .expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
                .expectBodyList(Restaurant.class)
                .hasSize(100)
                .consumeWith(rs -> Flux.fromIterable(rs)
                        .log()
                        .subscribe(r1 -> {
                            // get
                            webClient.get()
                                    .uri("/reactive/restaurants/{id}". r1.getId()) .accept(MediaType.APPLICATION_JSON_UTF8) .exchange() .expectStatus().isOk() .expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8) .expectBody(Restaurant.class) .consumeWith(r2 -> Assert.assertEquals(r1, r2)); })); }}Copy the code

When you create a WebTestClient instance, you first bind the RP Controller to be tested. As you can see, writing unit tests for RP applications is the same as writing business classes in a data-neutral streaming style.

You can find more unit tests in the sample application.

3 summary

This is the first and most common way to write RP applications in Spring 5. For reasons of length, this article will stop here. I’ll cover the second approach, Router Functions, in detail in the next article. Welcome you to my message board to share, and we have a move.

4 reference

  • Spring Framework Reference – WebFlux framework
  • spring-framework Reactive Tests