03: WebFlux Web CRUD Practice
Preface
The last article created a simple service based on functional endpoints and implemented Hello. This article uses Spring Boot WebFlux annotation control layer technology to create a CRUD WebFlux application to make development more convenient. We don't access database storage here, because we'll talk about it later, and here's a complete WebFlux CRUD.
structure
The project will manage the City to implement CRUD operations. After the project is created and compiled, the following structure is obtained. Its catalogue structure is as follows:
├── pom.xml ├── src │ └── main │ ├── java │ │ └── org │ │ └── spring │ │ └── springboot │ │ ├── Application.java │ │ ├── dao │ │ │ └── CityRepository.java │ │ ├── domain │ │ │ └── City.java │ │ ├── handler │ │ │ └── CityHandler.java │ │ └── webflux │ │ └── controller │ │ └── CityWebFluxController.java │ └── resources │ └── application.properties └── target
For example, the contents we need to write are as follows:
- object
- Data Access Layer Class Repository
- Processor class Handler
- Controller class
object
A new package, org.spring.springboot.domain, is created as an object class for urban entities. New City object City, code as follows:
/** * Urban Entity Class * */ public class City { /** * City Code */ private Long id; /** * Province code */ private Long provinceId; /** * City name */ private String cityName; /** * describe */ 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; } }
City includes city number, province number, city name and description. In specific development, Lombok tools will be used to eliminate the lengthy Java code, especially the getter / setter method of POJO. Check Lombok's official website address specifically: projectlombok.org.
Data Access Layer CityRepository
A new package, org.spring.springboot.dao, is created as a repository for the urban data access layer class. New City Repository, code as follows:
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; } }
@ Repository is used to annotate data access components, namely DAO components. The implementation code uses Map object named repository as memory data storage, and implements specific business logic for the object. City repository is responsible for the encapsulation organization related to the Book persistence layer (data operation), and completes the operations of adding, querying, deleting, etc.
The data storage is not involved here, and the specific data storage will be introduced later.
Processor class Handler
A new package, org.spring.springboot.handler, is created to write the city processor class CityHandler. New CityHandler, code as follows:
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))); } }
@ Component refers to a component, which is annotated when it is not easy to categorize. Then, CityRepository Bean is injected into the constructor with final and @Autowire annotations. The code is as follows:
private final CityRepository cityRepository; @Autowired public CityHandler(CityRepository cityRepository) { this.cityRepository = cityRepository; }
As can be seen from the return value, Mono and Flux are suitable for 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 might ask why this does not return objects directly, such as City/Long/List. The reason is that direct use of Flux and Mono is non-blocking writing, which is equivalent to callback. The callback can be reduced by using functional formulas, so the relevant interfaces can not be seen. This is exactly the benefit of WebFlux: a collection 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 stream Publisher with the basic rx operator. Elements or errors can be successfully published. As shown in the figure:
Mono's common methods are:
- Mono.create(): Use MonoSink to create Mono
- Mono.justOrEmpty(): Create Mono from an Optional or null object.
- Mono.error(): Create a Mono that contains only error messages
- Mono.never(): create a mono without any message notifications
- Mono.delay(): After the specified delay time, create a Mono and generate the number 0 as the 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 (successful or with an error).
Flux is the response stream Publisher with the basic 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 note: If you know that Publisher is 0 or 1, use Mono.
The most noteworthy aspect of Flux is the fromIterable method. From Iterable (Iterable <? Extends T > it) can publish elements of Iterable type. Of course, Flux also includes basic operations: map, merge, concat, flatMap, take, which are not covered here.
Controller class
In Spring Boot WebFlux development, configuration is not required. Spring Boot WebFlux can be developed using an automatic configuration and annotation-driven mode.
Create a new package directory, org.spring.springboot.webflux.controller, and create a directory named CityWebFluxController to handle different HTTP Restful business requests. 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); } }
The interface is implemented in REST style. What exactly is REST?
REST is an architecture style belonging to WEB itself, which is implemented under HTTP 1.1 specification. Representational State Transfer is translated as the state transition of the presentation layer. Resource: Resources. For example, newsfeed; Representation: manifestations, such as using JSON, rich text, etc; State Transfer: state change. It is implemented by HTTP actions.
Understanding REST requires understanding five key elements:
- Resource s
- Representation of resources
- State Transfer
- Uniform Interface
- Hypertext Driven
Six main characteristics:
- Resource Oriented
- Addressability
- Connectedness
- Statelessness
- Uniform Interface
- Hypertext Driven
Here we will not expand one by one, see for more details. http://www.infoq.com/cn/articles/understanding-restful-style.
Request input, Filters, redirection, Conversion, formatting and other knowledge will be the same as previous MVC knowledge, details of which can be found in the document: https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html
Operation engineering
A CRUD Spring Boot Webflux project has been developed, and the following works are run to verify it. Use the right toolbar of IDEA, click Maven Project Tab, and click the install command of the Maven plug-in. Or use the command line to execute Maven's instructions for cleaning and installing the project under the project root directory:
cd springboot-webflux-2-restful mvn clean install
Successful output is seen in the console:
... ellipsis [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] ------------------------------------------------------------------------
Execute Application class startup, any normal mode or Debug mode in IDEA. Successful output can be seen in the console:
... ellipsis 2018-04-10 08:43:39.932 INFO 2052 --- [ctor-http-nio-1] r.ipc.netty.tcp.BlockingNettyContext : Started HttpServer on /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 2018-04-10 08:43:39.960 INFO 2052 --- [ main] org.spring.springboot.Application : Started Application in 6.547 seconds (JVM running for 9.851)
Open POST MAN tools, development is necessary. Do the following:
New City Information POST http://127.0.0.1:8080/city
Getting City Information List GET http://127.0.0.1:8080/city
Other interfaces will not be demonstrated.
summary
Here, we discuss some functions of Spring WebFlux and build a basic CRUD project without underlying database. To better demonstrate how to create Flux streams and how to operate on them. Next, we will talk about how to manipulate data storage.
This article is based on the platform of blog one article multiple sending OpenWrite Release!