springboot2学习2-基于注解的控制层的WebFlux CRUD

       前面一篇使用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学习交流

猜你喜欢

转载自blog.csdn.net/wj903829182/article/details/80034665