前面一篇使用webflux实现了一个简单的hello world的请求和输出,这里学习基于注解控制器的webflux的crud操作。
学习来源:http://gitbook.cn/gitchat/column/5acda6f6d7966c5ae1086f2b/topic/5acda9d9d7966c5ae1087053
开始之前我们在pom.xml里面引入lombok包,如下:
org.projectlombok
lombok
1.16.20
provided
引入lombok主要是避免写过多的get和set方法,采用注解的方式在编译的时候自动生成get和set方法。需要注意IDEA需要配置lombok插件的使用,参考:https://blog.csdn.net/zhglance/article/details/54931430
1,编写实体类
package com.jack.webflux1.entity;
import lombok.Data;
/**
* create by jack 2018/4/21
*城市实体类
*/
@Data
public class City {
/**
* 城市编号
*/
private Long id;
/**
* 省份编号
*/
private Long provinceId;
/**
* 城市名称
*/
private String cityName;
/**
* 描述
*/
private String description;
}
2,编写数据访问层
package com.jack.webflux1.dao;
import com.jack.webflux1.entity.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;
/**
* create by jack 2018/4/21
* 数据访问层,负责数据的crud
*/
@Repository
public class CityRepository {
//保存数据的集合,作为伪数据库
private ConcurrentMap repository = new ConcurrentHashMap<>();
//id生成器
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 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;
}
}
3,处理器类
package com.jack.webflux1.handler;
import com.jack.webflux1.dao.CityRepository;
import com.jack.webflux1.entity.City;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* create by jack 2018/4/21
* 城市处理器
*
*
* Mono 和 Flux 适用于两个场景,即:
Mono:实现发布者,并返回 0 或 1 个元素,即单对象。
Flux:实现发布者,并返回 N 个元素,即 List 列表对象。
有人会问,这为啥不直接返回对象,比如返回 City/Long/List。
原因是,直接使用 Flux 和 Mono 是非阻塞写法,相当于回调方式。
利用函数式可以减少了回调,因此会看不到相关接口。这恰恰是 WebFlux 的好处:集合了非阻塞 + 异步
*/
@Component
public class CityHandler {
/**
* 数据操作的dao层的bean
*/
private final CityRepository cityRepository;
/**
* 通过构造器注入初始化属性cityRepository
* @param cityRepository
*/
@Autowired
public CityHandler(CityRepository cityRepository) {
this.cityRepository = cityRepository;
}
/**
* 保存城市数据的处理方法
* @param city
* @return
*/
public Mono save(City city) {
return Mono.create(cityMonoSink -> cityMonoSink.success(cityRepository.save(city)));
}
/**
* 通过城市id查询城市的处理方法
* @param id
* @return
*/
public Mono findCityById(Long id) {
return Mono.justOrEmpty(cityRepository.findCityById(id));
}
/**
* 查询所有城市数据
* @return
*/
public Flux findAllCity() {
return Flux.fromIterable(cityRepository.findAll());
}
/**
* 修改城市数据
* @param city
* @return
*/
public Mono modifyCity(City city) {
return Mono.create(cityMonoSink -> cityMonoSink.success(cityRepository.updateCity(city)));
}
/**
* 根据城市id删除城市数据
* @param id
* @return
*/
public Mono deleteCity(Long id) {
return Mono.create(cityMonoSink -> cityMonoSink.success(cityRepository.deleteCity(id)));
}
}
Mono 常用的方法有:
Mono.create():使用 MonoSink 来创建 Mono。
Mono.justOrEmpty():从一个 Optional 对象或 null 对象中创建 Mono。
Mono.error():创建一个只包含错误消息的 Mono。
Mono.never():创建一个不包含任何消息通知的 Mono。
Mono.delay():在指定的延迟时间之后,创建一个 Mono,产生数字 0 作为唯一值
如果知道 Publisher 是 0 或 1 个,则用 Mono。
Flux 最值得一提的是 fromIterable 方法,fromIterable(Iterable extends T> it) 可以发布 Iterable 类型的元素。当然,Flux 也包含了基础的操作:map、merge、concat、flatMap、take等
4,控制器
package com.jack.webflux1.controller;
import com.jack.webflux1.entity.City;
import com.jack.webflux1.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;
/**
* create by jack 2018/4/21
* webflux基于注解的控制器
*/
@RestController
@RequestMapping("/city")
public class CityWebFluxController {
@Autowired
private CityHandler cityHandler;
@GetMapping(value = "/{id}")
public Mono findCityById(@PathVariable("id") Long id) {
return cityHandler.findCityById(id);
}
@GetMapping()
public Flux findAllCity() {
return cityHandler.findAllCity();
}
@PostMapping()
public Mono saveCity(@RequestBody City city) {
return cityHandler.save(city);
}
@PutMapping()
public Mono modifyCity(@RequestBody City city) {
return cityHandler.modifyCity(city);
}
@DeleteMapping(value = "/{id}")
public Mono deleteCity(@PathVariable("id") Long id) {
return cityHandler.deleteCity(id);
}
}
上面的controller采用的是rest风格的接口。
5,运行测试
1)增加
url:http://localhost:8080/city
请求方式:post
数据格式:json
如下图:
2)根据指定id查找
如下图:
3)查找所有数据
4)修改数据
5)删除数据
上面根据rest接口演示了crud,下面详细的分析下代码的逻辑:
1)新增
新增的时候采用的是rest的post方式传递数据,数据是json格式的,格式如下:
{
"id":"",
"provinceId":75501,
"cityName":"广州",
"description":"广州有个小蛮腰"
}
新增url处理的代码如下:
@PostMapping()
public Mono saveCity(@RequestBody City city) {
return cityHandler.save(city);
}
从上面的代码可以看出,使用city对象接收参数,调用cityHandler的save方法保存city数据,处理成功以后,返回保存成功后的城市id。
cityHandler的save方法代码如下:
/** * 保存城市数据的处理方法 * @param city * @return */ public Monosave(City city) { return Mono.create(cityMonoSink -> cityMonoSink.success(cityRepository.save(city))); }
/**
* 保存城市数据的处理方法
* @param city
* @return
*/
public Mono save(City city) {
return Mono.create(cityMonoSink -> cityMonoSink.success(cityRepository.save(city)));
}
由于返回的数据只有一个所以使用的是Mono作为返回数据,使用Mono类静态create方法创建Mono对象,代码如下:
public static Mono create(Consumer> callback) {
return onAssembly(new MonoCreate(callback));
}
可以到create方法接收一个参数,参数是Consumer对象,通过callback可以看出,这里使用的是接口的回调,所以Mono.create()方法的参数需要一个实现类,实现Consumer接口,上面的代码是使用lambda的方式来实现接口的,下面看看Consumer接口的定义:
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.util.function;
import java.util.Objects;
/**
* Represents an operation that accepts a single input argument and returns no
* result. Unlike most other functional interfaces, {@code Consumer} is expected
* to operate via side-effects.
*
* This is a functional interface
* whose functional method is {@link #accept(Object)}.
*
* @param the type of the input to the operation
*
* @since 1.8
*/
@FunctionalInterface
public interface Consumer {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer andThen(Consumer super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
通过上面的代码可以看出,有两个方法,一个是默认的方法andThen,还有一个accept方法,我们的Mono.create方法的参数就是要实现这个accept方法。然后看create方法的实现:
public static Mono create(Consumer> callback) {
return onAssembly(new MonoCreate(callback));
}
在方法内部调用了onAssembly方法,参数是MonoCreate对象,然后我们看看MonoCreate类,代码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package reactor.core.publisher;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.Consumer;
import java.util.function.LongConsumer;
import reactor.core.CoreSubscriber;
import reactor.core.Disposable;
import reactor.core.Scannable.Attr;
import reactor.core.publisher.FluxCreate.SinkDisposable;
import reactor.util.annotation.Nullable;
import reactor.util.context.Context;
final class MonoCreate extends Mono {
final Consumer> callback;
MonoCreate(Consumer> callback) {
this.callback = callback;
}
public void subscribe(CoreSubscriber super T> actual) {
MonoCreate.DefaultMonoSink emitter = new MonoCreate.DefaultMonoSink(actual);
actual.onSubscribe(emitter);
try {
this.callback.accept(emitter);
} catch (Throwable var4) {
emitter.error(Operators.onOperatorError(var4, actual.currentContext()));
}
}
static final class DefaultMonoSink extends AtomicBoolean implements MonoSink, InnerProducer {
final CoreSubscriber super T> actual;
volatile Disposable disposable;
static final AtomicReferenceFieldUpdater DISPOSABLE = AtomicReferenceFieldUpdater.newUpdater(MonoCreate.DefaultMonoSink.class, Disposable.class, "disposable");
volatile int state;
static final AtomicIntegerFieldUpdater STATE = AtomicIntegerFieldUpdater.newUpdater(MonoCreate.DefaultMonoSink.class, "state");
volatile LongConsumer requestConsumer;
static final AtomicReferenceFieldUpdater REQUEST_CONSUMER = AtomicReferenceFieldUpdater.newUpdater(MonoCreate.DefaultMonoSink.class, LongConsumer.class, "requestConsumer");
T value;
static final int NO_REQUEST_HAS_VALUE = 1;
static final int HAS_REQUEST_NO_VALUE = 2;
static final int HAS_REQUEST_HAS_VALUE = 3;
DefaultMonoSink(CoreSubscriber super T> actual) {
this.actual = actual;
}
public Context currentContext() {
return this.actual.currentContext();
}
@Nullable
public Object scanUnsafe(Attr key) {
if (key != Attr.TERMINATED) {
return key == Attr.CANCELLED ? OperatorDisposables.isDisposed(this.disposable) : super.scanUnsafe(key);
} else {
return this.state == 3 || this.state == 1;
}
}
public void success() {
if (STATE.getAndSet(this, 3) != 3) {
try {
this.actual.onComplete();
} finally {
this.disposeResource(false);
}
}
}
public void success(@Nullable T value) {
if (value == null) {
this.success();
} else {
int s;
do {
s = this.state;
if (s == 3 || s == 1) {
Operators.onNextDropped(value, this.actual.currentContext());
return;
}
if (s == 2) {
if (STATE.compareAndSet(this, s, 3)) {
try {
this.actual.onNext(value);
this.actual.onComplete();
} finally {
this.disposeResource(false);
}
}
return;
}
this.value = value;
} while(!STATE.compareAndSet(this, s, 1));
}
}
public void error(Throwable e) {
if (STATE.getAndSet(this, 3) != 3) {
try {
this.actual.onError(e);
} finally {
this.disposeResource(false);
}
} else {
Operators.onOperatorError(e, this.actual.currentContext());
}
}
public MonoSink onRequest(LongConsumer consumer) {
Objects.requireNonNull(consumer, "onRequest");
if (!REQUEST_CONSUMER.compareAndSet(this, (Object)null, consumer)) {
throw new IllegalStateException("A consumer has already been assigned to consume requests");
} else {
return this;
}
}
public CoreSubscriber super T> actual() {
return this.actual;
}
public MonoSink onCancel(Disposable d) {
Objects.requireNonNull(d, "onCancel");
SinkDisposable sd = new SinkDisposable((Disposable)null, d);
if (!DISPOSABLE.compareAndSet(this, (Object)null, sd)) {
Disposable c = this.disposable;
if (c instanceof SinkDisposable) {
SinkDisposable current = (SinkDisposable)c;
if (current.onCancel == null) {
current.onCancel = d;
} else {
d.dispose();
}
}
}
return this;
}
public MonoSink onDispose(Disposable d) {
Objects.requireNonNull(d, "onDispose");
SinkDisposable sd = new SinkDisposable(d, (Disposable)null);
if (!DISPOSABLE.compareAndSet(this, (Object)null, sd)) {
Disposable c = this.disposable;
if (c instanceof SinkDisposable) {
SinkDisposable current = (SinkDisposable)c;
if (current.disposable == null) {
current.disposable = d;
} else {
d.dispose();
}
}
}
return this;
}
public void request(long n) {
if (Operators.validate(n)) {
LongConsumer consumer = this.requestConsumer;
if (consumer != null) {
consumer.accept(n);
}
int s;
do {
s = this.state;
if (s == 2 || s == 3) {
return;
}
if (s == 1) {
if (STATE.compareAndSet(this, s, 3)) {
try {
this.actual.onNext(this.value);
this.actual.onComplete();
} finally {
this.disposeResource(false);
}
}
return;
}
} while(!STATE.compareAndSet(this, s, 2));
}
}
public void cancel() {
if (STATE.getAndSet(this, 3) != 3) {
this.value = null;
this.disposeResource(true);
}
}
void disposeResource(boolean isCancel) {
Disposable d = this.disposable;
if (d != OperatorDisposables.DISPOSED) {
d = (Disposable)DISPOSABLE.getAndSet(this, OperatorDisposables.DISPOSED);
if (d != null && d != OperatorDisposables.DISPOSED) {
if (isCancel && d instanceof SinkDisposable) {
((SinkDisposable)d).cancel();
}
d.dispose();
}
}
}
}
}
上面的代码比较多,我们主要关注下面两个函数:
MonoCreate(Consumer> callback) {
this.callback = callback;
}
public void subscribe(CoreSubscriber super T> actual) {
MonoCreate.DefaultMonoSink emitter = new MonoCreate.DefaultMonoSink(actual);
actual.onSubscribe(emitter);
try {
this.callback.accept(emitter);
} catch (Throwable var4) {
emitter.error(Operators.onOperatorError(var4, actual.currentContext()));
}
}
通过上面的代码可以看出,一个是构造器,参数是Consumer,里面进行操作保存了Consumer对象,然后在subscribe方法里面有一句代码是this.callback.accept(emitter),就是在这里进行了接口的回调,回调Consumer的accept方法,这个方法是在调用Mono.create()方法的时候实现了。然后在细看subscribe方法,这里面有一个actual.onSubscribe方法,通过方法名可以知道,这里是订阅了消息。webflux是基于reactor模型,基于事件消息和异步,这里也体现了一个异步。
Mono和Flux的其他用法可以参照上面的源码流程自己看看,就不细说了。
源码地址:https://github.com/wj903829182/springcloud5/tree/master/webflux1
欢迎加群:331227121学习交流