前面一篇使用webflux实现了一个简单的hello world的请求和输出,这里学习基于注解控制器的webflux的crud操作。
学习来源:http://gitbook.cn/gitchat/column/5acda6f6d7966c5ae1086f2b/topic/5acda9d9d7966c5ae1087053
开始之前我们在pom.xml里面引入lombok包,如下:
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.20</version> <scope>provided</scope> </dependency>
引入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<Long, City> 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<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; } }
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<Long> save(City city) { return Mono.create(cityMonoSink -> cityMonoSink.success(cityRepository.save(city))); } /** * 通过城市id查询城市的处理方法 * @param id * @return */ public Mono<City> findCityById(Long id) { return Mono.justOrEmpty(cityRepository.findCityById(id)); } /** * 查询所有城市数据 * @return */ public Flux<City> findAllCity() { return Flux.fromIterable(cityRepository.findAll()); } /** * 修改城市数据 * @param city * @return */ public Mono<Long> modifyCity(City city) { return Mono.create(cityMonoSink -> cityMonoSink.success(cityRepository.updateCity(city))); } /** * 根据城市id删除城市数据 * @param id * @return */ public Mono<Long> 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<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); } }上面的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<Long> saveCity(@RequestBody City city) { return cityHandler.save(city); }
从上面的代码可以看出,使用city对象接收参数,调用cityHandler的save方法保存city数据,处理成功以后,返回保存成功后的城市id。
cityHandler的save方法代码如下:
/** * 保存城市数据的处理方法 * @param city * @return */ public Mono<Long> save(City city) { return Mono.create(cityMonoSink -> cityMonoSink.success(cityRepository.save(city))); }
/** * 保存城市数据的处理方法 * @param city * @return */ public Mono<Long> save(City city) { return Mono.create(cityMonoSink -> cityMonoSink.success(cityRepository.save(city))); }
由于返回的数据只有一个所以使用的是Mono作为返回数据,使用Mono类静态create方法创建Mono对象,代码如下:
public static <T> Mono<T> create(Consumer<MonoSink<T>> 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. * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #accept(Object)}. * * @param <T> the type of the input to the operation * * @since 1.8 */ @FunctionalInterface public interface Consumer<T> { /** * 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<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
通过上面的代码可以看出,有两个方法,一个是默认的方法andThen,还有一个accept方法,我们的Mono.create方法的参数就是要实现这个accept方法。然后看create方法的实现:
public static <T> Mono<T> create(Consumer<MonoSink<T>> 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<T> extends Mono<T> { final Consumer<MonoSink<T>> callback; MonoCreate(Consumer<MonoSink<T>> callback) { this.callback = callback; } public void subscribe(CoreSubscriber<? super T> actual) { MonoCreate.DefaultMonoSink<T> 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<T> extends AtomicBoolean implements MonoSink<T>, InnerProducer<T> { final CoreSubscriber<? super T> actual; volatile Disposable disposable; static final AtomicReferenceFieldUpdater<MonoCreate.DefaultMonoSink, Disposable> DISPOSABLE = AtomicReferenceFieldUpdater.newUpdater(MonoCreate.DefaultMonoSink.class, Disposable.class, "disposable"); volatile int state; static final AtomicIntegerFieldUpdater<MonoCreate.DefaultMonoSink> STATE = AtomicIntegerFieldUpdater.newUpdater(MonoCreate.DefaultMonoSink.class, "state"); volatile LongConsumer requestConsumer; static final AtomicReferenceFieldUpdater<MonoCreate.DefaultMonoSink, LongConsumer> 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<T> 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<T> 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<T> 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<MonoSink<T>> callback) { this.callback = callback; } public void subscribe(CoreSubscriber<? super T> actual) { MonoCreate.DefaultMonoSink<T> 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学习交流