03: WebFlux Web CRUD Practice

preface

The last article implemented Hello by creating a simple service based on a functional endpoint. This article uses Spring Boot WebFlux annotation control layer technology to create a CRUD WebFlux application to make development easier. We are not going to access the database store here because we will get to it later, and this is mainly a complete WebFlux CRUD.

structure

This project will manage the City and realize CRUD operation. After the project was created and written, the following structure was obtained, and its directory structure was as follows:

├ ─ ─ pom. XML ├ ─ ─ the SRC │ └ ─ ─ the main │ ├ ─ ─ Java │ │ └ ─ ─ org │ │ └ ─ ─ spring │ │ └ ─ ─ springboot │ │ ├ ─ ─ Application. Java │ │ ├ ─ ─ The dao │ │ │ └ ─ ─ CityRepository. Java │ │ ├ ─ ─ domain │ │ │ └ ─ ─ City. Java │ │ ├ ─ ─ handler │ │ │ └ ─ ─ CityHandler. Java │ │ └ ─ ─ Webflux │ │ └ ─ ─ controller │ │ └ ─ ─ CityWebFluxController. Java │ └ ─ ─ resources │ └ ─ ─ application. The properties └ ─ ─ targetCopy the code

For a directory structure, we need to write things in order:

  • object
  • Data access layer class Repository
  • Handler class
  • Controller class Controller

object

New package org. Spring. Springboot domain, as writing urban entity object classes. Create a new City object, City, with the following code:

/** * public class City {/** * public class City */ private Long id; /** * private Long provinceId; /** * cityName */ private String cityName; /** * description */ private String description; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getProvinceId() { return provinceId; } public void setProvinceId(Long provinceId) { this.provinceId = provinceId; } public String getCityName() { return cityName; } public void setCityName(String cityName) { this.cityName = cityName; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; }}Copy the code

A city contains a city number, a province number, a city name, and a description. In specific development, Lombok tools are used to eliminate verbose Java code, especially getter/setter methods for POJOs. Check out Lombok’s official website at projectlombok.org.

Data access layer CityRepository

New package org. Spring. Springboot dao, as writing city class Repository data access layer. Create CityRepository with the following code:

import org.spring.springboot.domain.City; import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicLong; @Repository public class CityRepository { private ConcurrentMap<Long, City> repository = new ConcurrentHashMap<>(); private static final AtomicLong idGenerator = new AtomicLong(0); public Long save(City city) { Long id = idGenerator.incrementAndGet(); city.setId(id); repository.put(id, city); return id; } public Collection<City> findAll() { return repository.values(); } public City findCityById(Long id) { return repository.get(id); } public Long updateCity(City city) { repository.put(city.getId(), city); return city.getId(); } public Long deleteCity(Long id) { repository.remove(id); return id; }}Copy the code

@Repository is used to annotate data access components, known as DAO components. The implementation code uses a Map object named Repository as an in-memory data store and implements specific business logic for the object. CityRepository is responsible for organizing the Book persistence layer (data operations) to add, query, and delete data.

Data storage will not be covered here, but will be explained later.

Handler class

New package org. Spring. Springboot handler, as writing city CityHandler processor class. Create CityHandler with the following code:

import org.spring.springboot.dao.CityRepository; import org.spring.springboot.domain.City; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @Component public class CityHandler { private final CityRepository cityRepository; @Autowired public CityHandler(CityRepository cityRepository) { this.cityRepository = cityRepository; } public Mono<Long> save(City city) { return Mono.create(cityMonoSink -> cityMonoSink.success(cityRepository.save(city))); } public Mono<City> findCityById(Long id) { return Mono.justOrEmpty(cityRepository.findCityById(id)); } public Flux<City> findAllCity() { return Flux.fromIterable(cityRepository.findAll()); } public Mono<Long> modifyCity(City city) { return Mono.create(cityMonoSink -> cityMonoSink.success(cityRepository.updateCity(city))); } public Mono<Long> deleteCity(Long id) { return Mono.create(cityMonoSink -> cityMonoSink.success(cityRepository.deleteCity(id))); }}Copy the code

@Component refers to a Component. Use this annotation when a Component is not easily categorized. Then inject the CityRepository Bean into the constructor with the final and @autoWired annotations as follows:

    private final CityRepository cityRepository;

    @Autowired
    public CityHandler(CityRepository cityRepository) {
        this.cityRepository = cityRepository;
    }Copy the code

As can be seen from the return value, Mono and Flux apply to two scenarios, namely:

  • Mono: Implements the publisher and returns 0 or 1 element, a single object
  • Flux: Implements the publisher and returns N elements, the List List object

One wonders why this doesn’t just return an object, like City/Long/List. The reason is that using Flux and Mono directly is non-blocking, equivalent to a callback. The use of functional expressions reduces callbacks so that the interface is not visible. This is exactly the benefit of WebFlux: a combination of non-blocking + asynchronous.

Mono

What is Mono? The official description is as follows: A Reactive Streams Publisher with basic rx operators that completes successfully by emitting an element, or with an error.

Mono is the response flow Publisher with the base RX operator. Elements or errors can be published successfully. As shown in the figure:

The common methods used by Mono are:

  • Mono.create() : Use MonoSink to create Mono
  • Mono.justorempty () : Creates Mono from an Optional or null object.
  • Mono.error() : Creates a Mono containing only error messages
  • Mono.never() : Creates a Mono that does not contain any message notifications
  • Mono.delay() : After the specified delay time, creates a Mono that produces the number 0 as a unique value

Flux

What is Flux? The official description is as follows: A Reactive Streams Publisher with rx operators that emits 0 to N elements, and then completes (successfully or with an error).

Flux is a response flow, Publisher with a base RX operator. You can successfully publish 0 to N elements or errors. Flux is actually a complement to Mono. As shown in the figure:

So be careful: If you know that Publisher is 0 or 1, use Mono.

Flux is most notable for the fromIterable method. FromIterable (Iterable It) can publish elements of the Iterable type. Of course, Flux also contains basic operations: map, Merge, concat, flatMap, and take, which will not be covered here.

Controller class Controller

This configuration is not required during Spring Boot WebFlux development. Spring Boot WebFlux can be developed using an auto-configuration annotated driver mode.

New package directory org. Spring. Springboot. Webflux. Controller, and created in the directory called CityWebFluxController HTTP Restful to deal with different business request. The code is as follows:

import org.spring.springboot.domain.City; import org.spring.springboot.handler.CityHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @RestController @RequestMapping(value = "/city") public class CityWebFluxController { @Autowired private CityHandler cityHandler; @GetMapping(value = "/{id}") public Mono<City> findCityById(@PathVariable("id") Long id) { return cityHandler.findCityById(id); } @GetMapping() public Flux<City> findAllCity() { return cityHandler.findAllCity(); } @PostMapping() public Mono<Long> saveCity(@RequestBody City city) { return cityHandler.save(city); } @PutMapping() public Mono<Long> modifyCity(@RequestBody City city) { return cityHandler.modifyCity(city); } @DeleteMapping(value = "/{id}") public Mono<Long> deleteCity(@PathVariable("id") Long id) { return cityHandler.deleteCity(id); }}Copy the code

The interface is implemented in REST style. So what exactly is REST?

REST is an architectural style that belongs to the WEB itself and is implemented under the HTTP 1.1 specification. Representational State Transfer is translated as a layer State Transfer. Resource: indicates resources. Such as the newsfeed; Representational: representation, such as JSON or rich text; State Transfer: State change. Through HTTP actions.

There are five key elements to understanding REST:

  • Resources
  • Representation of resources
  • State Transfer
  • Uniform Interface
  • Hypertext Driven

Six key features:

  • Resource Oriented
  • Addressability
  • Connectedness
  • Statelessness
  • Uniform Interface
  • Hypertext Driven

Specific not begin one, see http://www.infoq.com/cn/articles/understanding-restful-style.

The knowledge of request entry parameters, Filters, redirection, Conversion, formatting, and so on will be the same as the previous MVC knowledge. See the documentation for details: https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html

Run the project

A CRUD Spring Boot Webflux project is developed, the following run project verification. On the toolbar on the right side of IDEA, click the Maven Project Tab, and then click install to use the Maven plugin. Or, from the project root directory, execute Maven cleanup and installation commands:

cd springboot-webflux-2-restful
mvn clean installCopy the code

See successful output in the console:

. Omit [INFO] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 01:30 min [INFO] Finished at: 2017-10-15T10:00:54+08:00 [INFO] Final Memory: 31M/174M [INFO] ------------------------------------------------------------------------Copy the code

Perform Application class startup in IDEA, either in normal mode or Debug mode. You can see the output of a successful run on the console:

. Omit the 08:43:39 2018-04-10. 2052-932 the INFO [ctor - HTTP - nio - 1] r.ipc.net ty. TCP. BlockingNettyContext: Started HttpServer on / 0:0:0:0:0:0:0:0:0:0:0:8080 2018-04-10 08:43:39.935 INFO 2052 -- [main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080 the 2018-04-10 08:43:39. 2052-960 the INFO [main] org. Spring. Springboot. Application: Started Application in 6.547 seconds (JVM running for 9.851)Copy the code

Open the POST MAN tool, development requirements. Do the following:

New city information POST at http://127.0.0.1:8080/city

GET http://127.0.0.1:8080/city for urban information list

The other interfaces will not be demonstrated.

conclusion

Here, we explore some of Spring WebFlux’s capabilities to build a basic CRUD project without the underlying database. To better demonstrate how to create Flux flows and how to manipulate them. The following shows how to manipulate the data store.

This article is published by OpenWrite, a blogging tool platform