├── .gitignore ├── LICENSE ├── README.md ├── 第 一 章 ├── info.md ├── java9-reactive-demo1 │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── dockerx │ │ └── reactive │ │ ├── jdkaction │ │ ├── DockerXDemoApplication.java │ │ ├── DockerXDemoPublisher.java │ │ └── DockerXDemoSubscriber.java │ │ └── submissionPublisher │ │ └── ConsumeSubmissionPublisher.java └── java9-reactive-stock │ ├── pom.xml │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── dockerx │ │ │ └── reactive │ │ │ └── orderstock │ │ │ ├── OrderstockApp.java │ │ │ ├── product │ │ │ ├── Order.java │ │ │ ├── OrderItem.java │ │ │ ├── Product.java │ │ │ └── ProductIsOutOfStock.java │ │ │ └── stock │ │ │ ├── Stock.java │ │ │ ├── StockItem.java │ │ │ └── StockMaintain.java │ └── resources │ │ └── log4j.properties │ └── test │ └── java │ └── com │ └── dockerx │ └── reactive │ └── orderstock │ └── TestStockMaintain.java ├── 第 二 三 六 七 章 ├── 第二三六七章 Rxjava源码解读相关示例 │ └── java9-reactive-rxjava │ │ ├── pom.xml │ │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── dockerx │ │ │ └── reactive │ │ │ └── rxjava │ │ │ ├── GenerateNumbersIterator.java │ │ │ └── ValueT.java │ │ └── test │ │ └── java │ │ └── com │ │ └── dockerx │ │ └── reactive │ │ └── rxjava │ │ ├── City.java │ │ ├── ConcurrencyTest.java │ │ ├── Flight.java │ │ ├── FlowableSelect.java │ │ ├── FlowableTest.java │ │ ├── Hotel.java │ │ ├── ObservableTest.java │ │ ├── OperatorsTest.java │ │ ├── Update.java │ │ ├── Vacation.java │ │ ├── Weather.java │ │ └── WeatherStation.java └── 第二章 Demo1 │ └── java9-reactive-rxjava │ ├── pom.xml │ └── src │ └── main │ └── java │ └── com │ └── dockerx │ └── reactive │ └── rxjava │ └── GenerateNumbersIterator.java ├── 第 五 章 ├── rxjava-web-spring-boot-starter │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── dockerx │ │ │ │ └── spring │ │ │ │ └── boot │ │ │ │ └── rxjava │ │ │ │ ├── asyncresult │ │ │ │ ├── ObservableDeferredResult.java │ │ │ │ ├── ObservableSseEmitter.java │ │ │ │ └── SingleDeferredResult.java │ │ │ │ ├── config │ │ │ │ ├── RxJavaMvcAutoConfiguration.java │ │ │ │ └── RxMVC.java │ │ │ │ └── valuehandler │ │ │ │ ├── ObservableReturnValueHandler.java │ │ │ │ └── SingleReturnValueHandler.java │ │ └── resources │ │ │ └── META-INF │ │ │ └── spring.factories │ │ └── test │ │ └── java │ │ └── dockerx │ │ └── spring │ │ └── boot │ │ └── rxjava │ │ ├── asyncresult │ │ ├── ObservableDeferredResultTest.java │ │ ├── ObservableSseEmitterTest.java │ │ └── SingleDeferredResultTest.java │ │ └── dto │ │ └── EventDto.java ├── version 1-student │ └── rxjava-web │ │ ├── .gitignore │ │ ├── .mvn │ │ └── wrapper │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ │ ├── mvnw │ │ ├── mvnw.cmd │ │ ├── pom.xml │ │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── dockerx │ │ │ │ └── rxjavaweb │ │ │ │ ├── RxjavaWebApplication.java │ │ │ │ ├── config │ │ │ │ ├── SwaggerConfig.java │ │ │ │ └── Webconfig.java │ │ │ │ ├── controller │ │ │ │ ├── CurrencyController.java │ │ │ │ └── StudentController.java │ │ │ │ ├── domain │ │ │ │ └── dto │ │ │ │ │ ├── BaseStudentDTO.java │ │ │ │ │ ├── CurrencyRatesDTO.java │ │ │ │ │ └── StudentDTO.java │ │ │ │ ├── repository │ │ │ │ ├── StudentDao.java │ │ │ │ └── StudentRepository.java │ │ │ │ ├── returnhandler │ │ │ │ └── ObservableReturnValueHandler.java │ │ │ │ ├── service │ │ │ │ ├── CurrencyConverterService.java │ │ │ │ └── CurrencyConverterServiceImpl.java │ │ │ │ └── transformer │ │ │ │ ├── DocumentToStudentTransformer.java │ │ │ │ └── Transformer.java │ │ └── resources │ │ │ └── application.yml │ │ └── test │ │ └── java │ │ └── com │ │ └── dockerx │ │ └── rxjavaweb │ │ └── RxjavaWebApplicationTests.java └── version 2-currency │ └── rxjava-web │ ├── .gitignore │ ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── dockerx │ │ │ └── rxjavaweb │ │ │ ├── RxjavaWebApplication.java │ │ │ ├── config │ │ │ ├── SwaggerConfig.java │ │ │ └── Webconfig.java │ │ │ ├── controller │ │ │ ├── CurrencyController.java │ │ │ └── StudentController.java │ │ │ ├── domain │ │ │ └── dto │ │ │ │ ├── BaseStudentDTO.java │ │ │ │ ├── CurrencyRatesDTO.java │ │ │ │ └── StudentDTO.java │ │ │ ├── repository │ │ │ ├── StudentDao.java │ │ │ └── StudentRepository.java │ │ │ ├── returnhandler │ │ │ └── ObservableReturnValueHandler.java │ │ │ ├── service │ │ │ ├── CurrencyConverterService.java │ │ │ └── CurrencyConverterServiceImpl.java │ │ │ └── transformer │ │ │ ├── DocumentToStudentTransformer.java │ │ │ └── Transformer.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── dockerx │ └── rxjavaweb │ └── RxjavaWebApplicationTests.java └── 第 八 章 ├── info.md └── 结合Spring Web应用使用Flowable └── rx-react ├── .gitignore ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── dockerx │ │ └── rxreact │ │ ├── RxReactApplication.java │ │ ├── config │ │ ├── SwaggerConfig.java │ │ └── Webconfig.java │ │ ├── controller │ │ └── ReposController.java │ │ ├── domain │ │ ├── Author.java │ │ ├── Commit.java │ │ ├── CommitContent.java │ │ ├── CommittedFile.java │ │ ├── Committer.java │ │ ├── Repository.java │ │ └── SingleCommit.java │ │ ├── repository │ │ └── GitHbubRepos.java │ │ ├── returnhandler │ │ └── FlowableReturnValueHandler.java │ │ ├── services │ │ ├── GitHubService.java │ │ └── GitHubServiceImpl.java │ │ └── utils │ │ └── LanguageAnalysis.java └── resources │ ├── application.yml │ └── languages.yml └── test └── java └── com └── dockerx └── rxreact ├── RxReactApplicationTests.java ├── repository └── GitHbubReposTest.java └── services └── GitHubServiceImplTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *#*# 2 | *.#* 3 | *.iml 4 | *.ipr 5 | *.iws 6 | *.pyc 7 | *.pyo 8 | *.swp 9 | *~ 10 | .DS_Store 11 | .cache 12 | .classpath 13 | .ensime 14 | .ensime_cache/ 15 | .ensime_lucene 16 | .generated-mima* 17 | .idea/ 18 | .idea_modules/ 19 | .project 20 | .pydevproject 21 | .scala_dependencies 22 | .settings 23 | /lib/ 24 | R-unit-tests.log 25 | R/unit-tests.out 26 | R/cran-check.out 27 | R/pkg/vignettes/sparkr-vignettes.html 28 | R/pkg/tests/fulltests/Rplots.pdf 29 | build/*.jar 30 | build/apache-maven* 31 | build/scala* 32 | build/zinc* 33 | cache 34 | checkpoint 35 | conf/*.cmd 36 | conf/*.conf 37 | conf/*.properties 38 | conf/*.sh 39 | conf/*.xml 40 | conf/java-opts 41 | conf/slaves 42 | dependency-reduced-pom.xml 43 | derby.log 44 | dev/create-release/*final 45 | dev/create-release/*txt 46 | dev/pr-deps/ 47 | dist/ 48 | docs/_site 49 | docs/api 50 | sql/docs 51 | sql/site 52 | lib_managed/ 53 | lint-r-report.log 54 | log/ 55 | logs/ 56 | out/ 57 | project/boot/ 58 | project/build/target/ 59 | project/plugins/lib_managed/ 60 | project/plugins/project/build.properties 61 | project/plugins/src_managed/ 62 | project/plugins/target/ 63 | python/lib/pyspark.zip 64 | python/deps 65 | python/pyspark/python 66 | reports/ 67 | scalastyle-on-compile.generated.xml 68 | scalastyle-output.xml 69 | scalastyle.txt 70 | spark-*-bin-*.tgz 71 | spark-tests.log 72 | src_managed/ 73 | streaming-tests.log 74 | target/ 75 | unit-tests.log 76 | work/ 77 | 78 | # For Hive 79 | TempStatsStore/ 80 | metastore/ 81 | metastore_db/ 82 | sql/hive-thriftserver/test_warehouses 83 | warehouse/ 84 | spark-warehouse/ 85 | 86 | # For R session data 87 | .RData 88 | .RHistory 89 | .Rhistory 90 | *.Rproj 91 | *.Rproj.* 92 | 93 | .Rproj.user 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 本书配套视频及其他响应式系列配套视频如下: 2 | 3 | 4 | 5 | #### Java编程方法论-响应式篇-RxJava 分享视频 已完结 6 | 7 | bilibili:https://www.bilibili.com/video/av34537840 8 | 9 | 油管: 10 | 11 | #### Java编程方法论-响应式篇-Reactor 分享视频 已完结 12 | 13 | B站: 14 | 15 | 油管:https://www.youtube.com/playlist?list=PL95Ey4rht7980EH8yr7SLBvj9XSE1ggdy 16 | 17 | #### Java编程方法论-响应式篇-Reactor-Netty 分享视频 在分享 18 | 19 | 相关博文:https://juejin.im/user/59c7640851882578e00ddf90/posts 20 | 21 | 视频分享: 22 | 23 | B站:https://www.bilibili.com/video/av45556406/ 24 | 25 | 油管:https://www.youtube.com/watch?v=6qLh2L75KdM&list=PL95Ey4rht79-ISlb_Yr9ToaEI0K8ARmH6 26 | 27 | #### Java编程方法论-JDK 篇 之 NIO 分享视频 在分享 28 | 29 | 相关博文:https://juejin.im/user/59c7640851882578e00ddf90/posts 30 | 31 | B站:https://www.bilibili.com/video/av43230997 32 | 33 | 油管:https://www.youtube.com/watch?v=ZZnCI8xaTRo&list=PL95Ey4rht799NVLgQiSV9skTqY6VuspIk 34 | 35 | ## Java编程方法论-响应式 之 Rxjava篇 36 | 37 | 38 | 01 响应式入门:https://www.bilibili.com/video/av34537840/?p=1 39 | 40 | 02 Java9中的响应式编程:https://www.bilibili.com/video/av34537840/?p=2 41 | 42 | 03 Rxjava开篇:https://www.bilibili.com/video/av34537840/?p=3 43 | 44 | 04 Rxjava中create方法的设计思想:https://www.bilibili.com/video/av34537840/?p=4 45 | 46 | 05 Observables和Observable.cache():https://www.bilibili.com/video/av34537840/?p=5 47 | 48 | 06 无休止数据流与定时控制:https://www.bilibili.com/video/av34537840/?p=6 49 | 50 | 07 Demo的设计初衷:https://www.bilibili.com/video/av34537840/?p=7 51 | 52 | 08 Observable.cache()源码解读:https://www.bilibili.com/video/av34537840/?p=8 53 | 54 | 09 ConnectableObservable与publish().refCount()解读:https://www.bilibili.com/video/av34537840/?p=9 55 | 56 | 10 SubmissionPublisher 中订阅者状态的管理:https://www.bilibili.com/video/av34537840/?p=10 57 | 58 | 11 RxJava中Subject解读:https://www.bilibili.com/video/av34537840/?p=11 59 | 60 | 12 filter() map()深入解读与flatMap()初解:https://www.bilibili.com/video/av34537840/?p=12 61 | 62 | 13 flatMap()与scan()深入解读:https://www.bilibili.com/video/av34537840/?p=13 63 | 64 | 14 groupBy()进行分组:https://www.bilibili.com/video/av34537840/?p=14 65 | 66 | 15 merge()的源码解读 上:https://www.bilibili.com/video/av34537840/?p=15 67 | 68 | 16 merge()的源码解读 下:https://www.bilibili.com/video/av34537840/?p=16 69 | 70 | 17 zip()的源码解读:https://www.bilibili.com/video/av34537840/?p=17 71 | 72 | 18 combineLatest()的源码解读:https://www.bilibili.com/video/av34537840/?p=18 73 | 74 | 19 withLatestFrom() 源码解读:https://www.bilibili.com/video/av34537840/?p=19 75 | 76 | 20 amb() 操作源码解读:https://www.bilibili.com/video/av34537840/?p=20 77 | 78 | 21 scan()操作的2次深入:https://www.bilibili.com/video/av34537840/?p=21 79 | 80 | 22 reduce()源码解读:https://www.bilibili.com/video/av34537840/?p=22 81 | 82 | 23 collect() 源码解读:https://www.bilibili.com/video/av34537840/?p=23 83 | 84 | 24 distinct() distinctUntilChanged() compose() lift()及其他操作源码解读:https://www.bilibili.com/video/av34537840/?p=24 85 | 86 | 25 Observable实战之Spring MVC返回值的响应式化改造:https://www.bilibili.com/video/av34537840/?p=25 87 | 88 | 26 汇率查询的小服务及对于返回值处理抽取的前置知识讲解:https://www.bilibili.com/video/av34537840/?p=26 89 | 90 | 27 写一个SpringMVC的响应式返回值处理组件springboot-starter:https://www.bilibili.com/video/av34537840/?p=27 91 | 92 | 28 RxJava2中的多线程操作中调度器的引入:https://www.bilibili.com/video/av34537840/?p=28 93 | 94 | 29 subscribeOn() observeOn() unsubscribeOn()操作源码解读:https://www.bilibili.com/video/av34537840/?p=29 95 | 96 | 30 调度器Scheduler源码设计思路解读:https://www.bilibili.com/video/av34537840/?p=30 97 | 98 | 31 调度器Scheduler源码解读补充1:https://www.bilibili.com/video/av34537840/?p=31 99 | 100 | 32 调度器Scheduler源码解读补充2:https://www.bilibili.com/video/av34537840/?p=32 101 | 102 | 33 调度器Scheduler源码解读补充3:https://www.bilibili.com/video/av34537840/?p=33 103 | 104 | 34 背压回顾以及一些探究:https://www.bilibili.com/video/av34537840/?p=34 105 | 106 | 35 rxjava中SpscLinkedArrayQueue无界队列的实现解读:https://www.bilibili.com/video/av34537840/?p=35 107 | 108 | 36 从Observable到 Flowable 的设计思路 及Flowable.create() 中背压设计的解读:https://www.bilibili.com/video/av34537840/?p=36 109 | 110 | 37 onBackpressureXXX()操作与Flowable.generate()解读:https://www.bilibili.com/video/av34537840/?p=37 111 | 112 | 38 关于Rxjava解读简短的结束语:https://www.bilibili.com/video/av34537840/?p=38 113 | 114 | -------------------------------------------------------------------------------- /第 一 章/info.md: -------------------------------------------------------------------------------- 1 | # Java-9-Spring-Webflux 2 | 本章博文 : process的讲解 3 | 4 | ​ Reactive Streams 中背压的实现 -------------------------------------------------------------------------------- /第 一 章/java9-reactive-demo1/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.dockerx.reactive 8 | java9-reactive-demo1 9 | 1.0-SNAPSHOT 10 | 11 | 1.9 12 | UTF-8 13 | 14 | 15 | 16 | 17 | org.apache.maven.plugins 18 | maven-compiler-plugin 19 | 3.6.1 20 | 21 | 9 22 | 9 23 | true 24 | true 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-demo1/src/main/java/com/dockerx/reactive/jdkaction/DockerXDemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.jdkaction; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.ExecutorService; 5 | import java.util.concurrent.ForkJoinPool; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.stream.IntStream; 8 | 9 | /** 10 | * @author Author 知秋 11 | * @email fei6751803@163.com 12 | * @time Created by Auser on 2017/12/21 22:54. 13 | */ 14 | public class DockerXDemoApplication { 15 | 16 | public static void main(String[] args) { 17 | Flow_customsubmissionPublisher(); 18 | } 19 | 20 | private static void demoSubscribe(DockerXDemoPublisher publisher, String subscriberName){ 21 | DockerXDemoSubscriber subscriber = new DockerXDemoSubscriber<>(4L,subscriberName); 22 | 23 | publisher.subscribe(subscriber); 24 | } 25 | 26 | private static void Flow_customsubmissionPublisher() { 27 | ExecutorService execService = ForkJoinPool.commonPool();//Executors.newFixedThreadPool(3); 28 | try (DockerXDemoPublisher publisher = new DockerXDemoPublisher<>(execService)){ 29 | demoSubscribe(publisher, "One"); 30 | demoSubscribe(publisher, "Two"); 31 | demoSubscribe(publisher, "Three"); 32 | IntStream.range(1, 5).forEach(publisher::submit); 33 | try { 34 | Thread.sleep(5000); 35 | } catch (InterruptedException e) { 36 | e.printStackTrace(); 37 | } 38 | 39 | } finally { 40 | try { 41 | execService.shutdown(); 42 | int shutdownDelaySec = 1; 43 | System.out.println("………………等待 " + shutdownDelaySec + " 秒后结束服务……………… "); 44 | execService.awaitTermination(shutdownDelaySec, TimeUnit.SECONDS); 45 | } catch (Exception ex) { 46 | System.out.println("捕获到 execService.awaitTermination()方法的异常: " + ex.getClass().getName()); 47 | } finally { 48 | System.out.println("调用 execService.shutdownNow()结束服务..."); 49 | List l = execService.shutdownNow(); 50 | System.out.println("还剩 "+l.size() + " 个任务等待被执行,服务已关闭 "); 51 | } 52 | 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-demo1/src/main/java/com/dockerx/reactive/jdkaction/DockerXDemoPublisher.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.jdkaction; 2 | 3 | import java.lang.invoke.MethodHandles; 4 | import java.lang.invoke.VarHandle; 5 | import java.util.ArrayDeque; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Queue; 9 | import java.util.concurrent.*; 10 | import java.util.concurrent.locks.AbstractQueuedSynchronizer; 11 | import java.util.concurrent.locks.LockSupport; 12 | 13 | /** 14 | * @author Author 知秋 15 | * @email fei6751803@163.com 16 | * @time Created by Auser on 2017/12/24 23:28. 17 | */ 18 | public class DockerXDemoPublisher implements Flow.Publisher, AutoCloseable { 19 | private final ExecutorService executor; // daemon-based 20 | private CopyOnWriteArrayList list = new CopyOnWriteArrayList(); 21 | //private volatile int wip =0; 22 | //private static final VarHandle WIP; 23 | // static { 24 | // try { 25 | // MethodHandles.Lookup l = MethodHandles.lookup(); 26 | // WIP = l.findVarHandle(DockerXDemoPublisher.class, "wip", int.class); 27 | // } catch (ReflectiveOperationException e) { 28 | // throw new Error(e); 29 | // } 30 | // 31 | //} 32 | 33 | 34 | public void submit(T item) { 35 | System.out.println("***************** 开始发布元素 item: "+item+" *****************"); 36 | 37 | list.forEach(e -> { 38 | e.future=executor.submit(() -> { 39 | e.subscriber.onNext(item); 40 | e.cancel(); 41 | e.request(1); 42 | 43 | 44 | }); 45 | 46 | }); 47 | } 48 | 49 | public DockerXDemoPublisher(ExecutorService executor) { 50 | this.executor = executor; 51 | } 52 | 53 | public void close() { 54 | list.forEach(e -> { 55 | e.future=executor.submit(() -> { e.subscriber.onComplete();}); 56 | }); 57 | } 58 | 59 | @Override 60 | public void subscribe(Flow.Subscriber subscriber) { 61 | subscriber.onSubscribe(new DockerXDemoSubscription(subscriber, executor)); 62 | list.add(new DockerXDemoSubscription(subscriber, executor)); 63 | 64 | } 65 | 66 | static class DockerXDemoSubscription implements Flow.Subscription { 67 | private final Flow.Subscriber subscriber; 68 | private final ExecutorService executor; 69 | private Future future; 70 | private T item; 71 | private boolean completed; 72 | 73 | 74 | public DockerXDemoSubscription(Flow.Subscriber subscriber, ExecutorService executor) { 75 | this.subscriber = subscriber; 76 | this.executor = executor; 77 | } 78 | 79 | 80 | @Override 81 | public void request(long n) { 82 | if (n != 0 && !completed) { 83 | if (n < 0) { 84 | IllegalArgumentException ex = new IllegalArgumentException(); 85 | executor.execute(() -> subscriber.onError(ex)); 86 | } else { 87 | future = executor.submit(() -> { 88 | subscriber.onNext(item); 89 | }); 90 | } 91 | } else { 92 | subscriber.onComplete(); 93 | } 94 | } 95 | 96 | @Override 97 | public void cancel() { 98 | completed = true; 99 | if (future != null && !future.isCancelled()) { 100 | this.future.cancel(true); 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-demo1/src/main/java/com/dockerx/reactive/jdkaction/DockerXDemoSubscriber.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.jdkaction; 2 | 3 | import java.util.concurrent.Flow; 4 | 5 | /** 6 | * @author Author 知秋 7 | * @email fei6751803@163.com 8 | * @time Created by Auser on 2017/12/21 22:02. 9 | */ 10 | public class DockerXDemoSubscriber implements Flow.Subscriber{ 11 | private String name; 12 | private Flow.Subscription subscription; 13 | final long bufferSize; 14 | boolean done; 15 | long count; 16 | 17 | public String getName() { 18 | return name; 19 | } 20 | 21 | public Flow.Subscription getSubscription() { 22 | return subscription; 23 | } 24 | 25 | public DockerXDemoSubscriber(long bufferSize, String name) { 26 | this.bufferSize = bufferSize; 27 | this.name = name; 28 | } 29 | 30 | public void onSubscribe(Flow.Subscription subscription) { 31 | //count = bufferSize - bufferSize / 2;// 当消费一半的时候重新请求 32 | (this.subscription = subscription).request(bufferSize); 33 | System.out.println("开始onSubscribe订阅"); 34 | try { 35 | Thread.sleep(100); 36 | } catch (InterruptedException e) { 37 | e.printStackTrace(); 38 | } 39 | } 40 | 41 | public void onNext(T item) { 42 | if (done) { 43 | System.out.println(name +" Completed " + item); 44 | return; 45 | } 46 | //if (--count <= 0) subscription.request(count = bufferSize - bufferSize / 2); 47 | System.out.println(" ############# " + Thread.currentThread().getName()+" name: "+name+" item: "+(item==null?"NNNNN":item)+ " ############# "); 48 | System.out.println(name + " received: " + (item==null?"NNNNN":item)); 49 | try { 50 | Thread.sleep(10); 51 | } catch (InterruptedException e) { 52 | e.printStackTrace(); 53 | } 54 | 55 | } 56 | 57 | public void onError(Throwable throwable) { 58 | throwable.printStackTrace(); 59 | } 60 | 61 | public void onComplete() { 62 | this.done=true; 63 | System.out.println("Completed"); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-demo1/src/main/java/com/dockerx/reactive/submissionPublisher/ConsumeSubmissionPublisher.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.submissionPublisher; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | import java.util.concurrent.ExecutionException; 5 | import java.util.concurrent.SubmissionPublisher; 6 | import java.util.stream.LongStream; 7 | 8 | /** 9 | * @author Author 知秋 10 | * @email fei6751803@163.com 11 | * @time Created by Auser on 2017/12/27 0:33. 12 | */ 13 | public class ConsumeSubmissionPublisher { 14 | public static void main(String[] args) throws InterruptedException, ExecutionException { 15 | publish(); 16 | } 17 | public static void publish()throws InterruptedException, ExecutionException 18 | { 19 | CompletableFuture future = null; 20 | try (SubmissionPublisher publisher = new SubmissionPublisher()) { 21 | System.out.println("Subscriber Buffer Size: " + publisher.getMaxBufferCapacity()); 22 | future=publisher.consume(System.out::println); 23 | LongStream.range(1, 10).forEach(publisher::submit); 24 | } finally { 25 | future.get(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-stock/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.dockerx.reactive 8 | java9-reactive-stock 9 | 1.0-SNAPSHOT 10 | 11 | 2.0.0.BUILD-SNAPSHOT 12 | 1.9 13 | UTF-8 14 | com.dockerx.reactive.orderstock.OrderstockApp 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-autoconfigure 20 | ${spring-boot.version} 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | ${spring-boot.version} 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-logging 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-log4j 37 | LATEST 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-test 42 | RELEASE 43 | 44 | 45 | 46 | 47 | 48 | spring-snapshots 49 | Spring Snapshots 50 | https://repo.spring.io/libs-snapshot 51 | 52 | true 53 | 54 | 55 | 56 | spring-milestones 57 | Spring Milestones 58 | https://repo.spring.io/milestone 59 | 60 | false 61 | 62 | 63 | 64 | 65 | 66 | 67 | spring-snapshots 68 | Spring Snapshots 69 | https://repo.spring.io/snapshot 70 | 71 | true 72 | 73 | 74 | 75 | spring-milestones 76 | Spring Milestones 77 | https://repo.spring.io/milestone 78 | 79 | false 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.apache.maven.plugins 87 | maven-compiler-plugin 88 | 3.6.1 89 | 90 | ${java.version} 91 | ${java.version} 92 | true 93 | true 94 | 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-maven-plugin 99 | 2.0.0.BUILD-SNAPSHOT 100 | 101 | 102 | 103 | repackage 104 | 105 | 106 | exec 107 | ${start-class} 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-stock/src/main/java/com/dockerx/reactive/orderstock/OrderstockApp.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.orderstock; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * @author Author 知秋 8 | * @email fei6751803@163.com 9 | * @time Created by Auser on 2017/12/22 22:22. 10 | */ 11 | @SpringBootApplication 12 | public class OrderstockApp { 13 | public static void main(String[] args) { 14 | SpringApplication.run(OrderstockApp.class,args); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-stock/src/main/java/com/dockerx/reactive/orderstock/product/Order.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.orderstock.product; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author Author 知秋 7 | * @email fei6751803@163.com 8 | * @time Created by Auser on 2017/12/23 1:39. 9 | */ 10 | public class Order { 11 | private String orderId; 12 | private List items; 13 | private String customerId; 14 | 15 | public String getOrderId() { 16 | return orderId; 17 | } 18 | 19 | public Order setOrderId(String orderId) { 20 | this.orderId = orderId; 21 | return this; 22 | } 23 | 24 | public List getItems() { 25 | return items; 26 | } 27 | 28 | public Order setItems(List items) { 29 | this.items = items; 30 | return this; 31 | } 32 | 33 | public String getCustomerId() { 34 | return customerId; 35 | } 36 | 37 | public Order setCustomerId(String customerId) { 38 | this.customerId = customerId; 39 | return this; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-stock/src/main/java/com/dockerx/reactive/orderstock/product/OrderItem.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.orderstock.product; 2 | 3 | /** 4 | * @author Author 知秋 5 | * @email fei6751803@163.com 6 | * @time Created by Auser on 2017/12/23 1:54. 7 | */ 8 | public class OrderItem { 9 | private long amount; 10 | private Product product; 11 | 12 | public void setAmount(long amount) { 13 | this.amount = amount; 14 | } 15 | 16 | public void setProduct(Product product) { 17 | this.product = product; 18 | } 19 | 20 | public long getAmount() { 21 | return amount; 22 | } 23 | 24 | public Product getProduct() { 25 | return product; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-stock/src/main/java/com/dockerx/reactive/orderstock/product/Product.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.orderstock.product; 2 | 3 | /** 4 | * @author Author 知秋 5 | * @email fei6751803@163.com 6 | * @time Created by Auser on 2017/12/22 22:25. 7 | */ 8 | public class Product { 9 | } 10 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-stock/src/main/java/com/dockerx/reactive/orderstock/product/ProductIsOutOfStock.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.orderstock.product; 2 | 3 | /** 4 | * @author Author 知秋 5 | * @email fei6751803@163.com 6 | * @time Created by Auser on 2017/12/22 22:34. 7 | */ 8 | public class ProductIsOutOfStock extends Exception { 9 | public ProductIsOutOfStock(Product product) { 10 | super(product.toString()); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-stock/src/main/java/com/dockerx/reactive/orderstock/stock/Stock.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.orderstock.stock; 2 | 3 | import com.dockerx.reactive.orderstock.product.Product; 4 | import com.dockerx.reactive.orderstock.product.ProductIsOutOfStock; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.util.Map; 8 | import java.util.concurrent.ConcurrentHashMap; 9 | 10 | /** 11 | * 库存操作类 12 | * @author Author 知秋 13 | * @email fei6751803@163.com 14 | * @time Created by Auser on 2017/12/22 22:12. 15 | */ 16 | @Component 17 | public class Stock { 18 | private final Map stockItemMap = new ConcurrentHashMap<>(); 19 | 20 | private StockItem getItem(Product product){ 21 | //如果没有此商品,添加一个这个key,返回给null就是 22 | stockItemMap.putIfAbsent(product,new StockItem()); 23 | return stockItemMap.get(product); 24 | } 25 | 26 | public void store(Product product,long amount){ 27 | getItem(product).store(amount); 28 | } 29 | 30 | public void remove(Product product,long amount) throws ProductIsOutOfStock { 31 | if (getItem(product).remove(amount) != amount) 32 | throw new ProductIsOutOfStock(product); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-stock/src/main/java/com/dockerx/reactive/orderstock/stock/StockItem.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.orderstock.stock; 2 | 3 | import java.util.concurrent.atomic.AtomicLong; 4 | 5 | /** 6 | * 库存商品数量操作类 7 | * @author Author 知秋 8 | * @email fei6751803@163.com 9 | * @time Created by Auser on 2017/12/22 21:36. 10 | */ 11 | public class StockItem { 12 | private final AtomicLong amountItemStock = 13 | new AtomicLong(0); 14 | 15 | public void store(long n) { 16 | amountItemStock.accumulateAndGet(n, (pre, mount) -> pre + mount); 17 | } 18 | //下单时,所需商品数量没超过库存数量的话,就用库存数量减去所需商品数量, 19 | //返回此次从库存移除商品的具体数量;超过的话,库存不做任何操作,返回的就是库存移除商品的数量,即为0。 20 | public long remove(long n) { 21 | class RemoveData { 22 | long remove; 23 | } 24 | RemoveData removeData = new RemoveData(); 25 | amountItemStock.accumulateAndGet(n, 26 | (pre, mount) -> pre >= n ? pre - (removeData.remove = mount) : pre - (removeData.remove = 0L) 27 | ); 28 | return removeData.remove; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-stock/src/main/java/com/dockerx/reactive/orderstock/stock/StockMaintain.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.orderstock.stock; 2 | 3 | 4 | import com.dockerx.reactive.orderstock.product.Order; 5 | import com.dockerx.reactive.orderstock.product.ProductIsOutOfStock; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Flow; 12 | import java.util.concurrent.ForkJoinPool; 13 | 14 | /** 15 | * @author Author 知秋 16 | * @email fei6751803@163.com 17 | * @time Created by Auser on 2017/12/23 1:38. 18 | */ 19 | public class StockMaintain implements Flow.Subscriber { 20 | 21 | private static final Logger log = LoggerFactory.getLogger(StockMaintain.class); 22 | 23 | private Stock stock; 24 | private Flow.Subscription subscription = null; 25 | private ExecutorService execService = ForkJoinPool.commonPool(); 26 | 27 | public StockMaintain(@Autowired Stock stock) { 28 | this.stock = stock; 29 | } 30 | 31 | @Override 32 | public void onSubscribe(Flow.Subscription subscription) { 33 | log.info("******调用 onSubscribe******"); 34 | subscription.request(3); 35 | this.subscription = subscription; 36 | } 37 | 38 | @Override 39 | public void onNext(Order order) { 40 | execService.submit(() -> { 41 | log.info("Thread {}", Thread.currentThread().getName()); 42 | order.getItems().forEach(item -> { 43 | try { 44 | stock.remove(item.getProduct(),item.getAmount()); 45 | log.info("有 {} 件产品从库存消耗 ",item.getAmount()); 46 | } catch (ProductIsOutOfStock productIsOutOfStock) { 47 | log.error("产品库存不足"); 48 | } 49 | }); 50 | subscription.request(1); 51 | }); 52 | } 53 | 54 | @Override 55 | public void onError(Throwable throwable) { 56 | log.info(" 因为 {} 调用 onError ", throwable); 57 | } 58 | 59 | @Override 60 | public void onComplete() { 61 | log.info(" 调用 onComplete "); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-stock/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # LOG4J 2 | log4j.rootCategory=INFO, stdout,file 3 | # print console 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%d{yy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L - %m%n 7 | # print file 8 | log4j.appender.file=org.apache.log4j.DailyRollingFileAppender 9 | log4j.appender.file.DatePattern='-'yyyy-MM-dd'.log' 10 | log4j.appender.file.File=./logs/serial 11 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 12 | log4j.appender.file.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} %p [%c]: %m%n 13 | -------------------------------------------------------------------------------- /第 一 章/java9-reactive-stock/src/test/java/com/dockerx/reactive/orderstock/TestStockMaintain.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.orderstock; 2 | 3 | import com.dockerx.reactive.orderstock.product.Order; 4 | import com.dockerx.reactive.orderstock.product.OrderItem; 5 | import com.dockerx.reactive.orderstock.product.Product; 6 | import com.dockerx.reactive.orderstock.stock.Stock; 7 | import com.dockerx.reactive.orderstock.stock.StockMaintain; 8 | import org.junit.Test; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.util.LinkedList; 13 | import java.util.List; 14 | import java.util.concurrent.SubmissionPublisher; 15 | 16 | /** 17 | * @author Author 知秋 18 | * @email fei6751803@163.com 19 | * @time Created by Auser on 2017/12/23 2:10. 20 | */ 21 | public class TestStockMaintain { 22 | private static final Logger log = LoggerFactory.getLogger(TestStockMaintain.class); 23 | @Test 24 | public void teststockRemoval() throws InterruptedException { 25 | Stock stock = new Stock(); 26 | SubmissionPublisher p = new SubmissionPublisher<>(); 27 | p.subscribe(new StockMaintain(stock)); 28 | Product product = new Product(); 29 | stock.store(product, 40); 30 | OrderItem item = new OrderItem(); 31 | item.setProduct(product); 32 | item.setAmount(10); 33 | Order order = new Order(); 34 | List items = new LinkedList<>(); 35 | items.add(item); 36 | order.setItems(items); 37 | for (int i = 0; i < 10; i++) 38 | p.submit(order); 39 | log.info("所有订单已经提交完毕"); 40 | for (int j = 0; j < 10; j++) { 41 | log.info("Sleeping a bit..."); 42 | Thread.sleep(50); 43 | } 44 | p.close(); 45 | log.info("Publisher已关闭"); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /第 二 三 六 七 章/第二三六七章 Rxjava源码解读相关示例/java9-reactive-rxjava/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.dockerx.reactive 8 | java9-reactive-rxjava 9 | 1.0-SNAPSHOT 10 | 11 | 1.9 12 | UTF-8 13 | 14 | 15 | 16 | io.reactivex.rxjava2 17 | rxjava 18 | 2.1.7 19 | 20 | 21 | org.junit.jupiter 22 | junit-jupiter-api 23 | RELEASE 24 | 25 | 26 | 27 | com.google.guava 28 | guava 29 | 24.1-jre 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-compiler-plugin 38 | 3.6.1 39 | 40 | ${java.version} 41 | ${java.version} 42 | true 43 | true 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /第 二 三 六 七 章/第二三六七章 Rxjava源码解读相关示例/java9-reactive-rxjava/src/main/java/com/dockerx/reactive/rxjava/GenerateNumbersIterator.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.rxjava; 2 | 3 | import java.math.BigInteger; 4 | import java.util.Iterator; 5 | 6 | /** 7 | * @author Author 知秋 8 | * @email fei6751803@163.com 9 | * @time Created by Auser on 2017/12/28 0:34. 10 | */ 11 | public class GenerateNumbersIterator implements Iterator{ 12 | private BigInteger current = BigInteger.ZERO; 13 | private BigInteger num; 14 | 15 | public GenerateNumbersIterator(BigInteger num) { 16 | this.num = num; 17 | } 18 | 19 | @Override 20 | public boolean hasNext() { 21 | return current.compareTo(num)<0; 22 | } 23 | 24 | @Override 25 | public Object next() { 26 | current = current.add(BigInteger.ONE); 27 | return current; 28 | } 29 | 30 | public static void main(String[] args) { 31 | GenerateNumbersIterator generateNumbersIterator=new GenerateNumbersIterator(BigInteger.valueOf(10L)); 32 | while (generateNumbersIterator.hasNext()){ 33 | System.out.println(generateNumbersIterator.next()); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /第 二 三 六 七 章/第二三六七章 Rxjava源码解读相关示例/java9-reactive-rxjava/src/main/java/com/dockerx/reactive/rxjava/ValueT.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.rxjava; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.function.Consumer; 6 | 7 | /** 8 | * @author Author 知秋 9 | * @email fei6751803@163.com 10 | * @time Created by Auser on 2018/3/20 0:48. 11 | */ 12 | public class ValueT { 13 | private final List list; 14 | 15 | public ValueT(List list) { 16 | this.list = list; 17 | } 18 | 19 | public List getList() { 20 | return list; 21 | } 22 | 23 | public static ValueT valuetest(List o, Consumer consumer){ 24 | ValueT operatorsTest = new ValueT<>(o); 25 | consumer.accept(o); 26 | o.clear(); 27 | consumer.accept(operatorsTest.getList()); 28 | return operatorsTest; 29 | } 30 | 31 | public static void main(String[] args) { 32 | List list1=new ArrayList(); 33 | list1.add(1); 34 | System.out.println(list1.hashCode()); 35 | ValueT valuetest = valuetest(list1, o -> System.out.println(o.hashCode())); 36 | System.out.println(valuetest.hashCode()); 37 | list1.add(2); 38 | System.out.println(list1.hashCode()); 39 | System.out.println(valuetest.hashCode()); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /第 二 三 六 七 章/第二三六七章 Rxjava源码解读相关示例/java9-reactive-rxjava/src/test/java/com/dockerx/reactive/rxjava/City.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.rxjava; 2 | 3 | /** 4 | * @author Author 知秋 5 | * @email fei6751803@163.com 6 | * @time Created by Auser on 2018/2/20 15:57. 7 | */ 8 | public enum City { 9 | ChengDu, QingDao, DaLi, YiLi 10 | } 11 | -------------------------------------------------------------------------------- /第 二 三 六 七 章/第二三六七章 Rxjava源码解读相关示例/java9-reactive-rxjava/src/test/java/com/dockerx/reactive/rxjava/ConcurrencyTest.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.rxjava; 2 | 3 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 4 | import io.reactivex.Observable; 5 | import io.reactivex.Scheduler; 6 | import io.reactivex.disposables.Disposable; 7 | import io.reactivex.schedulers.Schedulers; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import java.util.concurrent.*; 11 | 12 | /** 13 | * @author Author 知秋 14 | * @email fei6751803@163.com 15 | * @time Created by Auser on 2018/4/7 6:29. 16 | */ 17 | public class ConcurrencyTest { 18 | private static void log(Object msg) { 19 | System.out.println( 20 | Thread.currentThread().getName() + 21 | ": " + msg); 22 | } 23 | 24 | public static void sleep(int millis) { 25 | try { 26 | Thread.sleep(millis); 27 | } catch (InterruptedException e) { 28 | e.printStackTrace(); 29 | } 30 | } 31 | private static void sleep(int timeout, TimeUnit unit) { 32 | try { 33 | unit.sleep(timeout); 34 | } catch (InterruptedException e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | public static T delayCalculation(T value) { 39 | sleep(ThreadLocalRandom.current().nextInt(1000)); 40 | System.out.println( 41 | Thread.currentThread().getName() + 42 | ": " + value); 43 | return value; 44 | } 45 | @Test 46 | void demo1() { 47 | Observable.interval(1, TimeUnit.SECONDS) 48 | .map(i -> i + " 次") 49 | .subscribe(System.out::println); 50 | sleep(5,TimeUnit.SECONDS); 51 | } 52 | @Test 53 | void demo2() { 54 | Observable.interval(1, TimeUnit.SECONDS) 55 | .map(ConcurrencyTest::delayCalculation) 56 | .subscribe(System.out::println); 57 | 58 | Observable.interval(1, TimeUnit.SECONDS) 59 | .map(ConcurrencyTest::delayCalculation) 60 | .subscribe(ConcurrencyTest::log); 61 | sleep(4,TimeUnit.SECONDS); 62 | } 63 | @Test 64 | void demo3() { 65 | Observable.intervalRange(1,2,1, 1,TimeUnit.SECONDS) 66 | .map(ConcurrencyTest::delayCalculation) 67 | .blockingSubscribe(System.out::println); 68 | 69 | Observable.intervalRange(1,2,1, 1,TimeUnit.SECONDS) 70 | .map(i -> { 71 | log(i); 72 | return i; 73 | }) 74 | .blockingSubscribe(ConcurrencyTest::log); 75 | 76 | } 77 | 78 | @Test 79 | void blockingSubscribe_test() { 80 | Observable.just("Apple", "Orange", "Appla", "Eatla", "HOHO", 81 | "Meta") 82 | .subscribeOn(Schedulers.computation()) 83 | .map(ConcurrencyTest::delayCalculation) 84 | .map(String::toUpperCase) 85 | .blockingSubscribe(ConcurrencyTest::log); 86 | } 87 | @Test 88 | void blockingSubscribe_test3() { 89 | Observable.just("Apple", "Orange", "Appla", "Eatla", "HOHO", 90 | "Meta") 91 | .subscribeOn(Schedulers.computation()) 92 | .map(ConcurrencyTest::delayCalculation) 93 | .subscribeOn(Schedulers.io()) 94 | .map(String::length) 95 | .subscribe(ConcurrencyTest::log); 96 | sleep(10000); 97 | } 98 | @Test 99 | void blockingSubscribe_test2() { 100 | Observable.just("Apple", "Orange", "Appla", "Eatla", "HOHO", 101 | "Meta") 102 | .subscribeOn(Schedulers.computation()) 103 | .map(ConcurrencyTest::delayCalculation) 104 | .subscribeOn(Schedulers.computation()) 105 | .subscribeOn(Schedulers.computation()) 106 | .map(String::length) 107 | .subscribe(ConcurrencyTest::log); 108 | sleep(10000); 109 | } 110 | 111 | @Test 112 | void interval_test() { 113 | Observable.interval(1, TimeUnit.SECONDS, Schedulers.newThread()) 114 | .subscribe(i -> System.out.println( i + 115 | " 来自线程: " + Thread.currentThread().getName())); 116 | sleep(5000); 117 | } 118 | @Test 119 | void observeOn_test() { 120 | Observable.just("Apple", "Orange", "Appla", "Eatla", "HOHO", 121 | "Meta") 122 | .subscribeOn(Schedulers.computation()) 123 | .map(ConcurrencyTest::delayCalculation) 124 | .observeOn(Schedulers.computation()) 125 | .map(String::length) 126 | .subscribe(ConcurrencyTest::log); 127 | sleep(10000); 128 | } 129 | @Test 130 | void observeOn_test2() { 131 | Observable.just("Apple", "Orange") 132 | .subscribeOn(Schedulers.computation()) 133 | .map(ConcurrencyTest::delayCalculation) 134 | .observeOn(Schedulers.newThread()) 135 | .map(ConcurrencyTest::delayCalculation) 136 | .map(String::length) 137 | .observeOn(Schedulers.io()) 138 | .map(Object::toString) 139 | .subscribe(ConcurrencyTest::log); 140 | sleep(10000); 141 | } 142 | 143 | @Test 144 | void unsubscribeOn_test() { 145 | Disposable disposable = 146 | Observable.interval(1, TimeUnit.SECONDS) 147 | .map(i -> "receive " +i) 148 | .doOnDispose(()-> System.out.println("Disposing on thread " 149 | + Thread.currentThread().getName())) 150 | .unsubscribeOn(Schedulers.io()) 151 | .doOnDispose(()-> System.out.println("Disposing1 on thread " 152 | + Thread.currentThread().getName())) 153 | .subscribe(ConcurrencyTest::log); 154 | sleep(4,TimeUnit.SECONDS); 155 | disposable.dispose(); 156 | sleep(4,TimeUnit.SECONDS); 157 | } 158 | @Test 159 | void unsubscribeOn_test1() { 160 | Disposable disposable = 161 | Observable.interval(1, TimeUnit.SECONDS) 162 | .map(i -> "receive " +i) 163 | .doOnDispose(()-> System.out.println("Disposing on thread " 164 | + Thread.currentThread().getName())) 165 | .subscribe(ConcurrencyTest::log); 166 | sleep(4,TimeUnit.SECONDS); 167 | disposable.dispose(); 168 | sleep(4,TimeUnit.SECONDS); 169 | } 170 | 171 | 172 | 173 | @Test 174 | void custom_Scheduler_test() { 175 | Scheduler customScheduler = custom_Scheduler(); 176 | Observable.just("Apple", "Orange", "Appla") 177 | .subscribeOn(customScheduler) 178 | .map(ConcurrencyTest::delayCalculation) 179 | .observeOn(customScheduler) 180 | .map(String::length) 181 | .subscribe(ConcurrencyTest::log); 182 | sleep(10000); 183 | } 184 | @Test 185 | void custom_Scheduler_test2() { 186 | Scheduler scheduler = Schedulers.from(Executors.newFixedThreadPool(10)); 187 | Observable.just("Apple", "Orange", "Appla") 188 | .subscribeOn(scheduler) 189 | .map(ConcurrencyTest::delayCalculation) 190 | .observeOn(scheduler) 191 | .map(String::length) 192 | .subscribe(ConcurrencyTest::log); 193 | sleep(10000); 194 | } 195 | 196 | public static Scheduler custom_Scheduler() { 197 | ThreadFactory threadFactory = new ThreadFactoryBuilder() 198 | .setNameFormat("自定义线程池-%d") 199 | .build(); 200 | Executor executor = new ThreadPoolExecutor( 201 | 10, //corePoolSize 202 | 10, //maximumPoolSize 203 | 0L, TimeUnit.MILLISECONDS, //keepAliveTime, unit 204 | new LinkedBlockingQueue<>(1000), //workQueue 205 | threadFactory 206 | ); 207 | return Schedulers.from(executor); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /第 二 三 六 七 章/第二三六七章 Rxjava源码解读相关示例/java9-reactive-rxjava/src/test/java/com/dockerx/reactive/rxjava/Flight.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.rxjava; 2 | 3 | /** 4 | * @author Author 知秋 5 | * @email fei6751803@163.com 6 | * @time Created by Auser on 2018/2/20 16:02. 7 | */ 8 | public class Flight { 9 | private int discount; 10 | 11 | public Flight(int discount) { 12 | this.discount = discount; 13 | } 14 | 15 | public int getDiscount() { 16 | return discount; 17 | } 18 | 19 | public Flight setDiscount(int discount) { 20 | this.discount = discount; 21 | return this; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /第 二 三 六 七 章/第二三六七章 Rxjava源码解读相关示例/java9-reactive-rxjava/src/test/java/com/dockerx/reactive/rxjava/FlowableSelect.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.rxjava; 2 | 3 | import io.reactivex.Emitter; 4 | import io.reactivex.Flowable; 5 | import io.reactivex.functions.BiConsumer; 6 | import io.reactivex.functions.Consumer; 7 | import io.reactivex.functions.Function; 8 | 9 | import java.sql.*; 10 | import java.util.List; 11 | import java.util.concurrent.Callable; 12 | 13 | /** 14 | * @author Author 知秋 15 | * @email fei6751803@163.com 16 | * @time Created by Auser on 2018/4/21 23:13. 17 | */ 18 | public class FlowableSelect { 19 | 20 | public static Flowable create(Callable connectionFactory, List parameters, String sql, 21 | Function mapper) { 22 | Callable initialState = () -> { 23 | Connection con = connectionFactory.call(); 24 | PreparedStatement ps = con.prepareStatement(sql); 25 | // TODO set parameters 26 | ResultSet rs = ps.executeQuery(); 27 | return rs; 28 | }; 29 | BiConsumer> generator = (rs, emitter) -> { 30 | if (rs.next()) { 31 | emitter.onNext(mapper.apply(rs)); 32 | } else { 33 | emitter.onComplete(); 34 | } 35 | }; 36 | Consumer disposeState = FlowableSelect::closeSilently; 37 | return Flowable.generate(initialState, generator, disposeState); 38 | } 39 | 40 | public static void closeSilently(ResultSet rs) { 41 | Statement stmt = null; 42 | try { 43 | stmt = rs.getStatement(); 44 | } catch (SQLException e) { 45 | // ignore 46 | } 47 | try { 48 | rs.close(); 49 | } catch (SQLException e) { 50 | // ignore 51 | } 52 | if (stmt != null) { 53 | try { 54 | stmt.close(); 55 | } catch (SQLException e) { 56 | // ignore 57 | } 58 | Connection con = null; 59 | try { 60 | con = stmt.getConnection(); 61 | } catch (SQLException e1) { 62 | // ignore 63 | } 64 | if (con != null) { 65 | try { 66 | con.close(); 67 | } catch (SQLException e) { 68 | // ignore 69 | } 70 | } 71 | } 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /第 二 三 六 七 章/第二三六七章 Rxjava源码解读相关示例/java9-reactive-rxjava/src/test/java/com/dockerx/reactive/rxjava/Hotel.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.rxjava; 2 | 3 | /** 4 | * @author Author 知秋 5 | * @email fei6751803@163.com 6 | * @time Created by Auser on 2018/2/20 16:03. 7 | */ 8 | public class Hotel { 9 | } 10 | -------------------------------------------------------------------------------- /第 二 三 六 七 章/第二三六七章 Rxjava源码解读相关示例/java9-reactive-rxjava/src/test/java/com/dockerx/reactive/rxjava/Update.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.rxjava; 2 | 3 | import io.reactivex.Emitter; 4 | import io.reactivex.Flowable; 5 | import io.reactivex.Single; 6 | import io.reactivex.functions.BiConsumer; 7 | import io.reactivex.functions.Consumer; 8 | import io.reactivex.functions.Function; 9 | 10 | import java.sql.*; 11 | import java.util.List; 12 | import java.util.concurrent.Callable; 13 | 14 | /** 15 | * @author Author 知秋 16 | * @email fei6751803@163.com 17 | * @time Created by Auser on 2018/4/21 23:31. 18 | */ 19 | public class Update { 20 | public static Single create(Callable connectionFactory, List parameters, String sql) { 21 | Callable resourceFactory = () -> { 22 | Connection con = connectionFactory.call(); 23 | // TODO set parameters 24 | return con.prepareStatement(sql); 25 | }; 26 | Function> singleFactory = ps -> Single.just(ps.executeUpdate()); 27 | Consumer disposer = Update::closeAll; 28 | return Single.using(resourceFactory, singleFactory, disposer); 29 | } 30 | 31 | public static Flowable create(Callable connectionFactory, List parameters, String sql, 32 | Function mapper) { 33 | Callable resourceFactory = () -> { 34 | Connection con = connectionFactory.call(); 35 | // TODO set parameters 36 | return con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); 37 | }; 38 | Function> singleFactory = ps -> create(ps, mapper); 39 | Consumer disposer = Update::closeAll; 40 | return Flowable.using(resourceFactory, singleFactory, disposer); 41 | } 42 | 43 | private static Flowable create(PreparedStatement ps, Function mapper) { 44 | Callable initialState = () -> { 45 | ps.execute(); 46 | return ps.getGeneratedKeys(); 47 | }; 48 | BiConsumer> generator = (rs, emitter) -> { 49 | if (rs.next()) { 50 | emitter.onNext(mapper.apply(rs)); 51 | } else { 52 | emitter.onComplete(); 53 | } 54 | }; 55 | Consumer disposer = Update::closeAll; 56 | return Flowable.generate(initialState, generator, disposer); 57 | } 58 | 59 | 60 | public static void closeAll(ResultSet rs) { 61 | Statement stmt = null; 62 | try { 63 | stmt = rs.getStatement(); 64 | } catch (SQLException e) { 65 | // ignore 66 | } 67 | try { 68 | rs.close(); 69 | } catch (SQLException e) { 70 | // ignore 71 | } 72 | if (stmt != null) { 73 | closeAll(stmt); 74 | } 75 | 76 | } 77 | 78 | public static void closeAll(Statement stmt) { 79 | try { 80 | stmt.close(); 81 | } catch (SQLException e) { 82 | // ignore 83 | } 84 | Connection con = null; 85 | try { 86 | con = stmt.getConnection(); 87 | } catch (SQLException e1) { 88 | // ignore 89 | } 90 | if (con != null) { 91 | try { 92 | con.close(); 93 | } catch (SQLException e) { 94 | // ignore 95 | } 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /第 二 三 六 七 章/第二三六七章 Rxjava源码解读相关示例/java9-reactive-rxjava/src/test/java/com/dockerx/reactive/rxjava/Vacation.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.rxjava; 2 | 3 | import io.reactivex.Observable; 4 | 5 | import java.time.LocalDate; 6 | import java.util.Random; 7 | 8 | /** 9 | * @author Author 知秋 10 | * @email fei6751803@163.com 11 | * @time Created by Auser on 2018/2/20 16:05. 12 | */ 13 | public class Vacation { 14 | private final City where; 15 | private final LocalDate when; 16 | 17 | private Flight flight; 18 | 19 | private Hotel hotel; 20 | private Weather weather; 21 | 22 | public LocalDate getWhen() { 23 | return when; 24 | } 25 | 26 | public Weather getWeather() { 27 | return weather; 28 | } 29 | 30 | public Vacation setWeather(Weather weather) { 31 | this.weather = weather; 32 | return this; 33 | } 34 | 35 | Vacation(City where, LocalDate when) { 36 | this.where = where; 37 | this.when = when; 38 | } 39 | 40 | 41 | public City getWhere() { 42 | return where; 43 | } 44 | 45 | public Flight getFlight() { 46 | return flight; 47 | } 48 | 49 | public Vacation setFlight(Flight flight) { 50 | this.flight = flight; 51 | return this; 52 | } 53 | 54 | public Hotel getHotel() { 55 | return hotel; 56 | } 57 | 58 | public Vacation setHotel(Hotel hotel) { 59 | this.hotel = hotel; 60 | return this; 61 | } 62 | 63 | public Observable cheapFlightFrom(City from) { 64 | //性价比最高的航班 65 | return Observable.just(new Flight(new Random().nextInt(5)+1)); 66 | } 67 | 68 | public Observable cheapHotel() { 69 | //打折的酒店 70 | return Observable.just(new Hotel()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /第 二 三 六 七 章/第二三六七章 Rxjava源码解读相关示例/java9-reactive-rxjava/src/test/java/com/dockerx/reactive/rxjava/Weather.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.rxjava; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * @author Author 知秋 7 | * @email fei6751803@163.com 8 | * @time Created by Auser on 2018/2/17 1:26. 9 | */ 10 | public class Weather { 11 | private final Temperature temperature; 12 | 13 | private final Wind wind; 14 | 15 | private boolean sunny; 16 | 17 | public Weather setSunny(boolean sunny) { 18 | this.sunny = sunny; 19 | return this; 20 | } 21 | 22 | public boolean isSunny() { 23 | return sunny; 24 | } 25 | 26 | public Weather(Temperature temperature, Wind wind) { 27 | this.temperature = temperature; 28 | this.wind = wind; 29 | } 30 | public boolean computeSunny() { 31 | Random random = new Random(); 32 | return random.nextInt(10)%2==0; 33 | } 34 | 35 | public Temperature getTemperature() { 36 | return temperature; 37 | } 38 | 39 | public Wind getWind() { 40 | return wind; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /第 二 三 六 七 章/第二三六七章 Rxjava源码解读相关示例/java9-reactive-rxjava/src/test/java/com/dockerx/reactive/rxjava/WeatherStation.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.rxjava; 2 | 3 | import io.reactivex.Observable; 4 | 5 | /** 6 | * @author Author 知秋 7 | * @email fei6751803@163.com 8 | * @time Created by Auser on 2018/2/17 1:23. 9 | */ 10 | public interface WeatherStation { 11 | Observable temperature(); 12 | Observable wind(); 13 | } 14 | 15 | class BasicWeatherStation implements WeatherStation { 16 | 17 | @Override 18 | public Observable temperature() { 19 | return Observable.just(new Temperature()); 20 | } 21 | 22 | @Override 23 | public Observable wind() { 24 | return Observable.just(new Wind()); 25 | } 26 | } 27 | 28 | class Temperature {} 29 | 30 | class Wind {} 31 | -------------------------------------------------------------------------------- /第 二 三 六 七 章/第二章 Demo1/java9-reactive-rxjava/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.dockerx.reactive 8 | java9-reactive-rxjava 9 | 1.0-SNAPSHOT 10 | 11 | 1.9 12 | UTF-8 13 | 14 | 15 | 16 | io.reactivex.rxjava2 17 | rxjava 18 | 2.1.7 19 | 20 | 21 | 22 | 23 | 24 | org.apache.maven.plugins 25 | maven-compiler-plugin 26 | 3.6.1 27 | 28 | ${java.version} 29 | ${java.version} 30 | true 31 | true 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /第 二 三 六 七 章/第二章 Demo1/java9-reactive-rxjava/src/main/java/com/dockerx/reactive/rxjava/GenerateNumbersIterator.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.reactive.rxjava; 2 | 3 | import java.math.BigInteger; 4 | import java.util.Iterator; 5 | 6 | /** 7 | * @author Author 知秋 8 | * @email fei6751803@163.com 9 | * @time Created by Auser on 2017/12/28 0:34. 10 | */ 11 | public class GenerateNumbersIterator implements Iterator{ 12 | private BigInteger current = BigInteger.ZERO; 13 | private BigInteger num; 14 | 15 | public GenerateNumbersIterator(BigInteger num) { 16 | this.num = num; 17 | } 18 | 19 | @Override 20 | public boolean hasNext() { 21 | return current.compareTo(num)<0; 22 | } 23 | 24 | @Override 25 | public Object next() { 26 | current = current.add(BigInteger.ONE); 27 | return current; 28 | } 29 | 30 | public static void main(String[] args) { 31 | GenerateNumbersIterator generateNumbersIterator=new GenerateNumbersIterator(BigInteger.valueOf(10L)); 32 | while (generateNumbersIterator.hasNext()){ 33 | System.out.println(generateNumbersIterator.next()); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /第 五 章/rxjava-web-spring-boot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.dockerx 8 | rxjava-web-spring-boot-starter 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | UTF-8 14 | 1.9 15 | 1.9 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-autoconfigure 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-test 29 | 30 | 31 | 32 | io.reactivex.rxjava2 33 | rxjava 34 | 2.1.12 35 | 36 | 37 | repository.junit 38 | junit 39 | 4.11 40 | test 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-dependencies 51 | 2.0.0.RELEASE 52 | pom 53 | import 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /第 五 章/rxjava-web-spring-boot-starter/src/main/java/dockerx/spring/boot/rxjava/asyncresult/ObservableDeferredResult.java: -------------------------------------------------------------------------------- 1 | package dockerx.spring.boot.rxjava.asyncresult; 2 | 3 | import io.reactivex.Observable; 4 | import org.springframework.util.Assert; 5 | import org.springframework.web.context.request.async.DeferredResult; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author Author 知秋 11 | * @email fei6751803@163.com 12 | * @time Created by Auser on 2018/3/27 1:37. 13 | */ 14 | public class ObservableDeferredResult extends DeferredResult> { 15 | 16 | 17 | public ObservableDeferredResult(Observable observable) { 18 | this(null, observable); 19 | } 20 | 21 | 22 | public ObservableDeferredResult(Long timeout, Observable observable) { 23 | super(timeout); 24 | Assert.notNull(observable, "observable can not be null"); 25 | observable.toList().toObservable().subscribe(new SingleDeferredResult.DeferredResultObserver>(this)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /第 五 章/rxjava-web-spring-boot-starter/src/main/java/dockerx/spring/boot/rxjava/asyncresult/ObservableSseEmitter.java: -------------------------------------------------------------------------------- 1 | package dockerx.spring.boot.rxjava.asyncresult; 2 | 3 | import io.reactivex.Observable; 4 | import io.reactivex.observers.DisposableObserver; 5 | import org.springframework.http.MediaType; 6 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; 7 | import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * @author Author 知秋 13 | * @email fei6751803@163.com 14 | * @time Created by Auser on 2018/3/27 2:21. 15 | */ 16 | public class ObservableSseEmitter extends SseEmitter { 17 | 18 | public ObservableSseEmitter(Observable observable) { 19 | this(null, observable); 20 | } 21 | 22 | public ObservableSseEmitter(MediaType mediaType, Observable observable) { 23 | this(null, mediaType, observable); 24 | } 25 | 26 | public ObservableSseEmitter(Long timeout, MediaType mediaType, Observable observable) { 27 | super(timeout); 28 | observable.subscribe(new ResponseBodyEmitterObserver<>(mediaType, this)); 29 | } 30 | 31 | static final class ResponseBodyEmitterObserver extends DisposableObserver implements Runnable { 32 | 33 | private final MediaType mediaType; 34 | 35 | private final ResponseBodyEmitter responseBodyEmitter; 36 | 37 | private boolean completed; 38 | 39 | public ResponseBodyEmitterObserver(MediaType mediaType, ResponseBodyEmitter responseBodyEmitter) { 40 | 41 | this.mediaType = mediaType; 42 | this.responseBodyEmitter = responseBodyEmitter; 43 | this.responseBodyEmitter.onTimeout(this); 44 | this.responseBodyEmitter.onCompletion(this); 45 | } 46 | 47 | public void onNext(T value) { 48 | 49 | try { 50 | if(!completed) { 51 | responseBodyEmitter.send(value, mediaType); 52 | } 53 | } catch (IOException e) { 54 | throw new RuntimeException(e.getMessage(), e); 55 | } 56 | } 57 | 58 | public void onError(Throwable e) { 59 | responseBodyEmitter.completeWithError(e); 60 | } 61 | 62 | public void onComplete() { 63 | if(!completed) { 64 | completed = true; 65 | responseBodyEmitter.complete(); 66 | } 67 | } 68 | 69 | public void run() { 70 | this.dispose(); 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /第 五 章/rxjava-web-spring-boot-starter/src/main/java/dockerx/spring/boot/rxjava/asyncresult/SingleDeferredResult.java: -------------------------------------------------------------------------------- 1 | package dockerx.spring.boot.rxjava.asyncresult; 2 | 3 | import io.reactivex.Single; 4 | import io.reactivex.observers.DisposableObserver; 5 | import org.springframework.util.Assert; 6 | import org.springframework.web.context.request.async.DeferredResult; 7 | 8 | /** 9 | * @author Author 知秋 10 | * @email fei6751803@163.com 11 | * @time Created by Auser on 2018/3/27 1:03. 12 | */ 13 | public class SingleDeferredResult extends DeferredResult { 14 | 15 | 16 | public SingleDeferredResult(Single single) { 17 | this(null, single); 18 | } 19 | 20 | 21 | public SingleDeferredResult(Long timeout, Single single) { 22 | super(timeout); 23 | Assert.notNull(single, "single can not be null"); 24 | single.toObservable().subscribe(new DeferredResultObserver<>(this)); 25 | } 26 | 27 | static final class DeferredResultObserver extends DisposableObserver implements Runnable { 28 | 29 | private final DeferredResult deferredResult; 30 | 31 | public DeferredResultObserver( DeferredResult deferredResult) { 32 | this.deferredResult = deferredResult; 33 | this.deferredResult.onTimeout(this); 34 | this.deferredResult.onCompletion(this); 35 | } 36 | 37 | 38 | public void onNext(T t) { 39 | deferredResult.setResult(t); 40 | } 41 | 42 | public void onError(Throwable throwable) { 43 | deferredResult.setErrorResult(throwable); 44 | } 45 | 46 | public void onComplete() { 47 | 48 | } 49 | 50 | public void run() { 51 | this.dispose(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /第 五 章/rxjava-web-spring-boot-starter/src/main/java/dockerx/spring/boot/rxjava/config/RxJavaMvcAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package dockerx.spring.boot.rxjava.config; 2 | 3 | import dockerx.spring.boot.rxjava.valuehandler.ObservableReturnValueHandler; 4 | import dockerx.spring.boot.rxjava.valuehandler.SingleReturnValueHandler; 5 | import io.reactivex.Observable; 6 | import io.reactivex.Single; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 10 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 11 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; 15 | import org.springframework.web.method.support.HandlerMethodReturnValueHandler; 16 | import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration; 17 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; 18 | 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | /** 24 | * @author Author 知秋 25 | * @email fei6751803@163.com 26 | * @time Created by Auser on 2018/3/27 21:33. 27 | */ 28 | @Configuration 29 | @ConditionalOnProperty(value = "rxjava.mvc.enabled", matchIfMissing = true) 30 | public class RxJavaMvcAutoConfiguration { 31 | @Bean 32 | @RxMVC 33 | @ConditionalOnMissingBean 34 | @ConditionalOnClass(Observable.class) 35 | public ObservableReturnValueHandler observableReturnValueHandler() { 36 | return new ObservableReturnValueHandler(); 37 | } 38 | 39 | @Bean 40 | @RxMVC 41 | @ConditionalOnMissingBean 42 | @ConditionalOnClass(Single.class) 43 | public SingleReturnValueHandler singleReturnValueHandler() { 44 | return new SingleReturnValueHandler(); 45 | } 46 | 47 | @Configuration 48 | public static class RxJavaWebConfiguration extends DelegatingWebMvcConfiguration { 49 | 50 | @RxMVC 51 | @Autowired 52 | private List handlers = new ArrayList<>(); 53 | 54 | @Override 55 | protected void addReturnValueHandlers(List returnValueHandlers) { 56 | super.addReturnValueHandlers(returnValueHandlers); 57 | returnValueHandlers.addAll(handlers); 58 | } 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /第 五 章/rxjava-web-spring-boot-starter/src/main/java/dockerx/spring/boot/rxjava/config/RxMVC.java: -------------------------------------------------------------------------------- 1 | package dockerx.spring.boot.rxjava.config; 2 | 3 | import org.springframework.beans.factory.annotation.Qualifier; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * 通过这个注解来将多个bean注入到一个集合里面 9 | * @author Author 知秋 10 | * @email fei6751803@163.com 11 | * @time Created by Auser on 2018/3/27 21:22. 12 | */ 13 | @Target({ElementType.TYPE, ElementType.PARAMETER, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Documented 16 | @Qualifier 17 | public @interface RxMVC { 18 | } 19 | -------------------------------------------------------------------------------- /第 五 章/rxjava-web-spring-boot-starter/src/main/java/dockerx/spring/boot/rxjava/valuehandler/ObservableReturnValueHandler.java: -------------------------------------------------------------------------------- 1 | package dockerx.spring.boot.rxjava.valuehandler; 2 | 3 | import dockerx.spring.boot.rxjava.asyncresult.ObservableDeferredResult; 4 | import io.reactivex.Observable; 5 | import org.springframework.core.MethodParameter; 6 | import org.springframework.web.context.request.NativeWebRequest; 7 | import org.springframework.web.context.request.async.WebAsyncUtils; 8 | import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; 9 | import org.springframework.web.method.support.ModelAndViewContainer; 10 | 11 | /** 12 | * @author Author 知秋 13 | * @email fei6751803@163.com 14 | * @time Created by Auser on 2018/3/27 21:26. 15 | */ 16 | public class ObservableReturnValueHandler implements AsyncHandlerMethodReturnValueHandler { 17 | public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { 18 | return returnValue != null && supportsReturnType(returnType); 19 | } 20 | 21 | public boolean supportsReturnType(MethodParameter returnType) { 22 | return Observable.class.isAssignableFrom(returnType.getParameterType()); 23 | } 24 | 25 | public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { 26 | if (returnValue == null) { 27 | mavContainer.setRequestHandled(true); 28 | return; 29 | } 30 | 31 | final Observable observable = Observable.class.cast(returnValue); 32 | WebAsyncUtils.getAsyncManager(webRequest) 33 | .startDeferredResultProcessing(new ObservableDeferredResult(observable), mavContainer); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /第 五 章/rxjava-web-spring-boot-starter/src/main/java/dockerx/spring/boot/rxjava/valuehandler/SingleReturnValueHandler.java: -------------------------------------------------------------------------------- 1 | package dockerx.spring.boot.rxjava.valuehandler; 2 | 3 | import dockerx.spring.boot.rxjava.asyncresult.SingleDeferredResult; 4 | import io.reactivex.Single; 5 | import org.springframework.core.MethodParameter; 6 | import org.springframework.web.context.request.NativeWebRequest; 7 | import org.springframework.web.context.request.async.WebAsyncUtils; 8 | import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; 9 | import org.springframework.web.method.support.ModelAndViewContainer; 10 | 11 | /** 12 | * 单值返回值(也就是单值源)处理 13 | * @author Author 知秋 14 | * @email fei6751803@163.com 15 | * @time Created by Auser on 2018/3/27 21:28. 16 | */ 17 | public class SingleReturnValueHandler implements AsyncHandlerMethodReturnValueHandler { 18 | public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { 19 | return returnValue != null && supportsReturnType(returnType); 20 | } 21 | 22 | public boolean supportsReturnType(MethodParameter returnType) { 23 | return Single.class.isAssignableFrom(returnType.getParameterType()); 24 | } 25 | 26 | public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { 27 | if (returnValue == null) { 28 | mavContainer.setRequestHandled(true); 29 | return; 30 | } 31 | 32 | final Single single = Single.class.cast(returnValue); 33 | WebAsyncUtils.getAsyncManager(webRequest) 34 | .startDeferredResultProcessing(new SingleDeferredResult(single), mavContainer); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /第 五 章/rxjava-web-spring-boot-starter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | dockerx.spring.boot.rxjava.config.RxJavaMvcAutoConfiguration -------------------------------------------------------------------------------- /第 五 章/rxjava-web-spring-boot-starter/src/test/java/dockerx/spring/boot/rxjava/asyncresult/ObservableDeferredResultTest.java: -------------------------------------------------------------------------------- 1 | package dockerx.spring.boot.rxjava.asyncresult; 2 | 3 | import dockerx.spring.boot.rxjava.dto.EventDto; 4 | import io.reactivex.Observable; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.boot.test.web.client.TestRestTemplate; 10 | import org.springframework.boot.web.server.LocalServerPort; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.core.ParameterizedTypeReference; 13 | import org.springframework.http.HttpMethod; 14 | import org.springframework.http.HttpStatus; 15 | import org.springframework.http.ResponseEntity; 16 | import org.springframework.test.annotation.DirtiesContext; 17 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 18 | import org.springframework.web.bind.annotation.RequestMapping; 19 | import org.springframework.web.bind.annotation.RequestMethod; 20 | import org.springframework.web.bind.annotation.RestController; 21 | 22 | import java.util.Arrays; 23 | import java.util.Collections; 24 | import java.util.Date; 25 | import java.util.List; 26 | import java.util.concurrent.TimeUnit; 27 | 28 | import static org.junit.Assert.assertEquals; 29 | import static org.junit.Assert.assertNotNull; 30 | import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE; 31 | 32 | /** 33 | * @author Author 知秋 34 | * @email fei6751803@163.com 35 | * @time Created by Auser on 2018/3/27 23:04. 36 | */ 37 | @RunWith(SpringJUnit4ClassRunner.class) 38 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, 39 | classes =ObservableDeferredResultTest.Application.class ) 40 | @DirtiesContext 41 | public class ObservableDeferredResultTest { 42 | 43 | @LocalServerPort 44 | private int port; 45 | 46 | private TestRestTemplate restTemplate = new TestRestTemplate(); 47 | 48 | @Configuration 49 | @EnableAutoConfiguration 50 | @RestController 51 | protected static class Application { 52 | 53 | @RequestMapping(method = RequestMethod.GET, value = "/empty") 54 | public ObservableDeferredResult empty() { 55 | return new ObservableDeferredResult<>(Observable.empty()); 56 | } 57 | 58 | @RequestMapping(method = RequestMethod.GET, value = "/single") 59 | public ObservableDeferredResult single() { 60 | return new ObservableDeferredResult<>(Observable.just("single value")); 61 | } 62 | 63 | @RequestMapping(method = RequestMethod.GET, value = "/multiple") 64 | public ObservableDeferredResult multiple() { 65 | return new ObservableDeferredResult<>(Observable.just("multiple", "values")); 66 | } 67 | 68 | @RequestMapping(method = RequestMethod.GET, value = "/event", produces = APPLICATION_JSON_UTF8_VALUE) 69 | public ObservableDeferredResult event() { 70 | return new ObservableDeferredResult<>( 71 | Observable.just( 72 | new EventDto("Spring.io", new Date()), 73 | new EventDto("JavaOne", new Date()) 74 | ) 75 | ); 76 | } 77 | 78 | @RequestMapping(method = RequestMethod.GET, value = "/throw") 79 | public ObservableDeferredResult error() { 80 | return new ObservableDeferredResult<>(Observable.error(new RuntimeException("Unexpected"))); 81 | } 82 | 83 | @RequestMapping(method = RequestMethod.GET, value = "/timeout") 84 | public ObservableDeferredResult timeout() { 85 | return new ObservableDeferredResult<>(Observable.timer(1, TimeUnit.MINUTES).map(aLong -> "single value")); 86 | } 87 | } 88 | 89 | @Test 90 | public void shouldRetrieveEmptyResponse() { 91 | 92 | // when 93 | ResponseEntity response = restTemplate.getForEntity(path("/empty"), List.class); 94 | 95 | // then 96 | assertNotNull(response); 97 | assertEquals(HttpStatus.OK, response.getStatusCode()); 98 | assertEquals(Collections.emptyList(), response.getBody()); 99 | } 100 | 101 | @Test 102 | public void shouldRetrieveSingleValue() { 103 | 104 | // when 105 | ResponseEntity response = restTemplate.getForEntity(path("/single"), List.class); 106 | 107 | // then 108 | assertNotNull(response); 109 | assertEquals(HttpStatus.OK, response.getStatusCode()); 110 | assertEquals(Collections.singletonList("single value"), response.getBody()); 111 | } 112 | 113 | @Test 114 | public void shouldRetrieveMultipleValues() { 115 | 116 | // when 117 | ResponseEntity response = restTemplate.getForEntity(path("/multiple"), List.class); 118 | 119 | // then 120 | assertNotNull(response); 121 | assertEquals(HttpStatus.OK, response.getStatusCode()); 122 | assertEquals(Arrays.asList("multiple", "values"), response.getBody()); 123 | } 124 | 125 | @Test 126 | public void shouldRetrieveJsonSerializedListValues() { 127 | 128 | // when 129 | ResponseEntity> response = restTemplate.exchange(path("/event"), HttpMethod.GET, null, 130 | new ParameterizedTypeReference>() {}); 131 | System.out.println(response.getBody()); 132 | // then 133 | assertNotNull(response); 134 | assertEquals(HttpStatus.OK, response.getStatusCode()); 135 | assertEquals(2, response.getBody().size()); 136 | assertEquals("JavaOne", response.getBody().get(1).getName()); 137 | } 138 | 139 | @Test 140 | public void shouldRetrieveErrorResponse() { 141 | 142 | // when 143 | ResponseEntity response = restTemplate.getForEntity(path("/throw"), Object.class); 144 | 145 | // then 146 | assertNotNull(response); 147 | assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); 148 | } 149 | 150 | @Test 151 | public void shouldTimeoutOnConnection() { 152 | 153 | // when 154 | ResponseEntity response = restTemplate.getForEntity(path("/timeout"), Object.class); 155 | 156 | // then 157 | assertNotNull(response); 158 | assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); 159 | } 160 | 161 | private String path(String context) { 162 | return String.format("http://localhost:%d%s", port, context); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /第 五 章/rxjava-web-spring-boot-starter/src/test/java/dockerx/spring/boot/rxjava/asyncresult/ObservableSseEmitterTest.java: -------------------------------------------------------------------------------- 1 | package dockerx.spring.boot.rxjava.asyncresult; 2 | 3 | import dockerx.spring.boot.rxjava.dto.EventDto; 4 | import io.reactivex.Observable; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.boot.test.web.client.TestRestTemplate; 10 | import org.springframework.boot.web.server.LocalServerPort; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.http.MediaType; 14 | import org.springframework.http.RequestEntity; 15 | import org.springframework.http.ResponseEntity; 16 | import org.springframework.test.annotation.DirtiesContext; 17 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 18 | import org.springframework.web.bind.annotation.RequestMapping; 19 | import org.springframework.web.bind.annotation.RequestMethod; 20 | import org.springframework.web.bind.annotation.RestController; 21 | 22 | import java.net.URI; 23 | import java.net.URISyntaxException; 24 | import java.util.Date; 25 | import java.util.GregorianCalendar; 26 | 27 | import static org.junit.Assert.assertEquals; 28 | import static org.junit.Assert.assertNotNull; 29 | import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8; 30 | 31 | /** 32 | * @author Author 知秋 33 | * @email fei6751803@163.com 34 | * @time Created by Auser on 2018/3/27 23:20. 35 | */ 36 | @RunWith(SpringJUnit4ClassRunner.class) 37 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, 38 | classes =ObservableSseEmitterTest.Application.class ) 39 | @DirtiesContext 40 | public class ObservableSseEmitterTest { 41 | 42 | @LocalServerPort 43 | private int port; 44 | 45 | private TestRestTemplate restTemplate = new TestRestTemplate(); 46 | 47 | @Configuration 48 | @EnableAutoConfiguration 49 | @RestController 50 | protected static class Application { 51 | 52 | @RequestMapping(method = RequestMethod.GET, value = "/sse") 53 | public ObservableSseEmitter single() { 54 | return new ObservableSseEmitter<>(Observable.just("single value")); 55 | } 56 | 57 | @RequestMapping(method = RequestMethod.GET, value = "/messages" ,produces = MediaType.TEXT_EVENT_STREAM_VALUE) 58 | public ObservableSseEmitter messages() { 59 | return new ObservableSseEmitter<>(Observable.just("message 1", "message 2", "message 3")); 60 | } 61 | 62 | @RequestMapping(method = RequestMethod.GET, value = "/events") 63 | public ObservableSseEmitter event() { 64 | return new ObservableSseEmitter<>(APPLICATION_JSON_UTF8, Observable.just( 65 | new EventDto("Spring.io", getDate(2016, 5, 11)), 66 | new EventDto("JavaOne", getDate(2016, 9, 22)) 67 | )); 68 | } 69 | } 70 | 71 | @Test 72 | public void shouldRetrieveSse() { 73 | 74 | // when 75 | ResponseEntity response = restTemplate.getForEntity(path("/sse"), String.class); 76 | 77 | // then 78 | assertNotNull(response); 79 | assertEquals(HttpStatus.OK, response.getStatusCode()); 80 | assertEquals("data:single value\n\n", response.getBody()); 81 | } 82 | 83 | @Test 84 | public void shouldRetrieveSseWithMultipleMessages() { 85 | 86 | // when 87 | ResponseEntity response = restTemplate.getForEntity(path("/messages"), String.class); 88 | 89 | // then 90 | assertNotNull(response); 91 | assertEquals(HttpStatus.OK, response.getStatusCode()); 92 | assertEquals("data:message 1\n\ndata:message 2\n\ndata:message 3\n\n", response.getBody()); 93 | } 94 | 95 | @Test 96 | public void shouldRetrieveJsonOverSseWithMultipleMessages() { 97 | 98 | // when 99 | ResponseEntity response = restTemplate.getForEntity(path("/events"), String.class); 100 | System.out.println(response.getBody()); 101 | // then 102 | assertNotNull(response); 103 | assertEquals(HttpStatus.OK, response.getStatusCode()); 104 | } 105 | @Test 106 | public void shouldRetrieveJsonOverSseWithMultipleMessages2() throws URISyntaxException { 107 | 108 | // when 109 | 110 | RequestEntity requestEntity = RequestEntity.get(new URI(path("/events"))) 111 | .accept(MediaType.TEXT_EVENT_STREAM).build(); 112 | ResponseEntity responseEntity = restTemplate.exchange(requestEntity, String.class); 113 | System.out.println(responseEntity.getBody()); 114 | // then 115 | assertNotNull(responseEntity); 116 | assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); 117 | } 118 | 119 | private String path(String context) { 120 | return String.format("http://localhost:%d%s", port, context); 121 | } 122 | 123 | private static Date getDate(int year, int month, int day) { 124 | return new GregorianCalendar(year, month, day).getTime(); 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /第 五 章/rxjava-web-spring-boot-starter/src/test/java/dockerx/spring/boot/rxjava/asyncresult/SingleDeferredResultTest.java: -------------------------------------------------------------------------------- 1 | package dockerx.spring.boot.rxjava.asyncresult; 2 | 3 | 4 | import io.reactivex.Single; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.boot.test.web.client.TestRestTemplate; 10 | import org.springframework.boot.web.server.LocalServerPort; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.test.annotation.DirtiesContext; 15 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RequestMethod; 18 | import org.springframework.web.bind.annotation.RestController; 19 | 20 | import static org.junit.Assert.assertEquals; 21 | import static org.junit.Assert.assertNotNull; 22 | 23 | /** 24 | * @author Author 知秋 25 | * @email fei6751803@163.com 26 | * @time Created by Auser on 2018/3/27 21:57. 27 | */ 28 | @RunWith(SpringJUnit4ClassRunner.class) 29 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, 30 | classes =SingleDeferredResultTest.Application.class ) 31 | @DirtiesContext 32 | public class SingleDeferredResultTest { 33 | 34 | @LocalServerPort 35 | private int port; 36 | 37 | private TestRestTemplate restTemplate = new TestRestTemplate(); 38 | 39 | @Configuration 40 | @EnableAutoConfiguration 41 | @RestController 42 | protected static class Application { 43 | 44 | @RequestMapping(method = RequestMethod.GET, value = "/single") 45 | public Single single() { 46 | return Single.just("single value"); 47 | } 48 | 49 | @RequestMapping(method = RequestMethod.GET, value = "/singleWithResponse") 50 | public Single> singleWithResponse() { 51 | return Single.just(new ResponseEntity<>("single value", HttpStatus.NOT_FOUND)); 52 | } 53 | 54 | @RequestMapping(method = RequestMethod.GET, value = "/throw") 55 | public Single error() { 56 | return Single.error(new RuntimeException("Unexpected")); 57 | } 58 | } 59 | 60 | @Test 61 | public void shouldRetrieveSingleValue() { 62 | 63 | // when 64 | ResponseEntity response = restTemplate.getForEntity(path("/single"), String.class); 65 | 66 | // then 67 | assertNotNull(response); 68 | assertEquals(HttpStatus.OK, response.getStatusCode()); 69 | assertEquals("single value", response.getBody()); 70 | } 71 | 72 | @Test 73 | public void shouldRetrieveSingleValueWithStatusCode() { 74 | 75 | // when 76 | ResponseEntity response = restTemplate.getForEntity(path("/singleWithResponse"), String.class); 77 | 78 | // then 79 | assertNotNull(response); 80 | assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); 81 | assertEquals("single value", response.getBody()); 82 | } 83 | 84 | @Test 85 | public void shouldRetrieveErrorResponse() { 86 | 87 | // when 88 | ResponseEntity response = restTemplate.getForEntity(path("/throw"), Object.class); 89 | 90 | // then 91 | assertNotNull(response); 92 | assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); 93 | } 94 | 95 | private String path(String context) { 96 | return String.format("http://localhost:%d%s", port, context); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /第 五 章/rxjava-web-spring-boot-starter/src/test/java/dockerx/spring/boot/rxjava/dto/EventDto.java: -------------------------------------------------------------------------------- 1 | package dockerx.spring.boot.rxjava.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | import java.util.Date; 7 | 8 | /** 9 | * @author Author 知秋 10 | * @email fei6751803@163.com 11 | * @time Created by Auser on 2018/3/27 23:08. 12 | */ 13 | public class EventDto { 14 | private final String name; 15 | 16 | private final Date date; 17 | 18 | @JsonCreator 19 | public EventDto(@JsonProperty("name") String name, @JsonProperty("date") Date date) { 20 | this.name = name; 21 | this.date = date; 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | public Date getDate() { 29 | return date; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/.gitignore: -------------------------------------------------------------------------------- 1 | *#*# 2 | *.#* 3 | *.iml 4 | *.ipr 5 | *.iws 6 | *.pyc 7 | *.pyo 8 | *.swp 9 | *~ 10 | .DS_Store 11 | .cache 12 | .classpath 13 | .ensime 14 | .ensime_cache/ 15 | .ensime_lucene 16 | .generated-mima* 17 | .idea/ 18 | .idea_modules/ 19 | .project 20 | .pydevproject 21 | .scala_dependencies 22 | .settings 23 | /lib/ 24 | R-unit-tests.log 25 | R/unit-tests.out 26 | R/cran-check.out 27 | R/pkg/vignettes/sparkr-vignettes.html 28 | R/pkg/tests/fulltests/Rplots.pdf 29 | build/*.jar 30 | build/apache-maven* 31 | build/scala* 32 | build/zinc* 33 | cache 34 | checkpoint 35 | conf/*.cmd 36 | conf/*.conf 37 | conf/*.properties 38 | conf/*.sh 39 | conf/*.xml 40 | conf/java-opts 41 | conf/slaves 42 | dependency-reduced-pom.xml 43 | derby.log 44 | dev/create-release/*final 45 | dev/create-release/*txt 46 | dev/pr-deps/ 47 | dist/ 48 | docs/_site 49 | docs/api 50 | sql/docs 51 | sql/site 52 | lib_managed/ 53 | lint-r-report.log 54 | log/ 55 | logs/ 56 | out/ 57 | project/boot/ 58 | project/build/target/ 59 | project/plugins/lib_managed/ 60 | project/plugins/project/build.properties 61 | project/plugins/src_managed/ 62 | project/plugins/target/ 63 | python/lib/pyspark.zip 64 | python/deps 65 | python/pyspark/python 66 | reports/ 67 | scalastyle-on-compile.generated.xml 68 | scalastyle-output.xml 69 | scalastyle.txt 70 | spark-*-bin-*.tgz 71 | spark-tests.log 72 | src_managed/ 73 | streaming-tests.log 74 | target/ 75 | unit-tests.log 76 | work/ 77 | 78 | # For Hive 79 | TempStatsStore/ 80 | metastore/ 81 | metastore_db/ 82 | sql/hive-thriftserver/test_warehouses 83 | warehouse/ 84 | spark-warehouse/ 85 | 86 | # For R session data 87 | .RData 88 | .RHistory 89 | .Rhistory 90 | *.Rproj 91 | *.Rproj.* 92 | 93 | .Rproj.user 94 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyinchen/Java-programming-methodology-Rxjava-articles/23d4d5347295e890ccb1f21f1b32800975f5269e/第 五 章/version 1-student/rxjava-web/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip 2 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.dockerx 7 | rxjava-web 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | rxjava-web 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.0.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 9 25 | 2.8.0 26 | 2.8.0 27 | 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-web 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-test 38 | test 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-tomcat 44 | compile 45 | 46 | 47 | 48 | io.springfox 49 | springfox-swagger2 50 | ${springfox.version} 51 | 52 | 53 | org.mapstruct 54 | mapstruct 55 | 56 | 57 | 58 | 59 | io.springfox 60 | springfox-swagger-ui 61 | ${springfoxui.version} 62 | 63 | 64 | mapstruct 65 | org.mapstruct 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.mongodb 73 | mongodb-driver-rx 74 | 1.5.0 75 | 76 | 77 | 78 | 79 | javax.xml.bind 80 | jaxb-api 81 | 2.3.0 82 | 83 | 84 | 85 | 86 | 87 | 88 | org.springframework.boot 89 | spring-boot-maven-plugin 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/RxjavaWebApplication.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.web.client.RestTemplate; 7 | 8 | @SpringBootApplication 9 | public class RxjavaWebApplication { 10 | 11 | @Bean 12 | public RestTemplate restTemplate() { 13 | return new RestTemplate(); 14 | } 15 | 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(RxjavaWebApplication.class, args); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.config; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.builders.RequestHandlerSelectors; 9 | import springfox.documentation.service.ApiInfo; 10 | import springfox.documentation.spi.DocumentationType; 11 | import springfox.documentation.spring.web.plugins.Docket; 12 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 13 | 14 | /** 15 | * @author Author 知秋 16 | * @email fei6751803@163.com 17 | * @time Created by Auser on 2018/3/25 20:41. 18 | */ 19 | @Configuration 20 | @EnableSwagger2 21 | public class SwaggerConfig { 22 | @Value("${swagger.title}") 23 | private String title; 24 | 25 | @Value("${swagger.description}") 26 | private String description; 27 | 28 | @Value("${swagger.version}") 29 | private String version; 30 | 31 | @Bean 32 | public Docket api() { 33 | return new Docket(DocumentationType.SWAGGER_2) 34 | .apiInfo(apiInfo()).select() 35 | .apis(RequestHandlerSelectors.basePackage("com.dockerx.rxjavaweb")).paths( 36 | PathSelectors.any()).build(); 37 | } 38 | 39 | /** 40 | * This method will return the API info object to swagger which will in turn 41 | * display the information on the swagger UI. 42 | * 43 | * @return the API information 44 | */ 45 | private ApiInfo apiInfo() { 46 | return new ApiInfoBuilder() 47 | .title(title) 48 | .description(description) 49 | .version(version) 50 | .build(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/config/Webconfig.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.config; 2 | 3 | import com.dockerx.rxjavaweb.returnhandler.ObservableReturnValueHandler; 4 | import org.springframework.context.annotation.ComponentScan; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.web.method.support.HandlerMethodReturnValueHandler; 7 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 8 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @author Author 知秋 14 | * @email fei6751803@163.com 15 | * @time Created by Auser on 2018/3/23 21:24. 16 | */ 17 | @Configuration 18 | @ComponentScan(basePackages = {"com.dockerx.rxjavaweb.*"}) 19 | public class Webconfig extends WebMvcConfigurationSupport { 20 | @Override 21 | protected void addReturnValueHandlers(List returnValueHandlers) { 22 | super.addReturnValueHandlers(returnValueHandlers); 23 | returnValueHandlers.add(new ObservableReturnValueHandler()); 24 | } 25 | 26 | @Override 27 | protected void addResourceHandlers(ResourceHandlerRegistry registry) { 28 | super.addResourceHandlers(registry); 29 | registry.addResourceHandler("swagger-ui.html") 30 | .addResourceLocations("classpath:/META-INF/resources/"); 31 | 32 | registry.addResourceHandler("/webjars/**") 33 | .addResourceLocations("classpath:/META-INF/resources/webjars/"); 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/controller/CurrencyController.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.controller; 2 | 3 | import com.dockerx.rxjavaweb.domain.dto.CurrencyRatesDTO; 4 | import com.dockerx.rxjavaweb.service.CurrencyConverterService; 5 | import io.swagger.annotations.ApiImplicitParam; 6 | import io.swagger.annotations.ApiOperation; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.MediaType; 11 | import org.springframework.web.bind.annotation.*; 12 | import rx.Observable; 13 | 14 | import java.util.Set; 15 | 16 | /** 17 | * @author Author 知秋 18 | * @email fei6751803@163.com 19 | * @time Created by Auser on 2018/3/25 22:32. 20 | */ 21 | @RestController 22 | @RequestMapping("/api/currencyconverter") 23 | public class CurrencyController { 24 | private static final Logger logger = LoggerFactory.getLogger(CurrencyController.class); 25 | 26 | @Autowired 27 | private CurrencyConverterService currencyConverterService; 28 | 29 | @ApiOperation(value="汇率查询", notes="根据想查的币种来查找与指定基础货币对比币种对应的汇率") 30 | @ApiImplicitParam(name = "symbols", value = "所要查询的汇率的币种集合", required = true) 31 | @GetMapping(value = "/rates",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}) 32 | public Observable getCurrencyRates(@RequestParam("symbols") Set symbols,@RequestParam("base")String base) { 33 | logger.debug("Retrieving currency rates."); 34 | return currencyConverterService.getCurrencyRates(symbols,base); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/controller/StudentController.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.controller; 2 | 3 | import com.dockerx.rxjavaweb.domain.dto.BaseStudentDTO; 4 | import com.dockerx.rxjavaweb.domain.dto.StudentDTO; 5 | import com.dockerx.rxjavaweb.repository.StudentRepository; 6 | import com.mongodb.rx.client.Success; 7 | import io.swagger.annotations.ApiImplicitParam; 8 | import io.swagger.annotations.ApiOperation; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.http.MediaType; 13 | import org.springframework.web.bind.annotation.*; 14 | import rx.Observable; 15 | 16 | /** 17 | * @author Author 知秋 18 | * @email fei6751803@163.com 19 | * @time Created by Auser on 2018/3/23 21:03. 20 | */ 21 | @RestController 22 | @RequestMapping("/api/students") 23 | public class StudentController { 24 | 25 | private static final Logger logger = LoggerFactory.getLogger(StudentController.class); 26 | 27 | private final StudentRepository studentRepository; 28 | 29 | @Autowired 30 | public StudentController(StudentRepository studentRepository) { 31 | this.studentRepository = studentRepository; 32 | } 33 | 34 | @ApiOperation(value="学生创建", notes="根据BaseStudentDTO对象创建学生") 35 | @ApiImplicitParam(name = "student", value = "学生详细实体student", required = true, dataType = "BaseStudentDTO") 36 | @PostMapping(value = "",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}) 37 | public Observable createStudent(@RequestBody BaseStudentDTO student) { 38 | logger.debug("Creating a new Student."); 39 | return studentRepository.createStudent(student); 40 | } 41 | @ApiOperation(value="学生查找", notes="根据name查找相应学生") 42 | @ApiImplicitParam(name = "name", value = "学生name", required = true, dataType = "String") 43 | @GetMapping(value = "",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}) 44 | public Observable getStudentByName(@RequestParam String name) { 45 | logger.debug("Fetching a new student with the Name: " + name); 46 | return studentRepository.findByName(name); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/domain/dto/BaseStudentDTO.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.domain.dto; 2 | 3 | import javax.validation.constraints.Min; 4 | import javax.validation.constraints.NotEmpty; 5 | import javax.validation.constraints.NotNull; 6 | 7 | /** 8 | * @author Author 知秋 9 | * @email fei6751803@163.com 10 | * @time Created by Auser on 2018/3/23 17:41. 11 | */ 12 | public class BaseStudentDTO { 13 | 14 | @NotNull 15 | @NotEmpty 16 | private String name; 17 | @Min(value = 1, message = "Age should be a positive number") 18 | private int age; 19 | @Min(value = 0, message = "Invalid credit value was given") 20 | private double credit; 21 | @NotNull 22 | @NotEmpty 23 | private String major; 24 | 25 | 26 | 27 | private BaseStudentDTO() { 28 | super(); 29 | } 30 | 31 | public BaseStudentDTO(String name, int age, double credit, String major) { 32 | super(); 33 | this.name = name; 34 | this.age = age; 35 | this.credit = credit; 36 | this.major = major; 37 | } 38 | 39 | public String getName() { 40 | return name; 41 | } 42 | 43 | public BaseStudentDTO setName(String name) { 44 | this.name = name; 45 | return this; 46 | } 47 | 48 | public int getAge() { 49 | return age; 50 | } 51 | 52 | public BaseStudentDTO setAge(int age) { 53 | this.age = age; 54 | return this; 55 | } 56 | 57 | public double getCredit() { 58 | return credit; 59 | } 60 | 61 | public BaseStudentDTO setCredit(double credit) { 62 | this.credit = credit; 63 | return this; 64 | } 65 | 66 | public String getMajor() { 67 | return major; 68 | } 69 | 70 | public BaseStudentDTO setMajor(String major) { 71 | this.major = major; 72 | return this; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/domain/dto/CurrencyRatesDTO.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.domain.dto; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * @author Author 知秋 7 | * @email fei6751803@163.com 8 | * @time Created by Auser on 2018/3/25 22:09. 9 | */ 10 | public class CurrencyRatesDTO { 11 | private String base; 12 | private String date; 13 | private Map rates; 14 | 15 | public String getBase() { 16 | return base; 17 | } 18 | 19 | public CurrencyRatesDTO setBase(String base) { 20 | this.base = base; 21 | return this; 22 | } 23 | 24 | public String getDate() { 25 | return date; 26 | } 27 | 28 | public CurrencyRatesDTO setDate(String date) { 29 | this.date = date; 30 | return this; 31 | } 32 | 33 | public Map getRates() { 34 | return rates; 35 | } 36 | 37 | public CurrencyRatesDTO setRates(Map rates) { 38 | this.rates = rates; 39 | return this; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/domain/dto/StudentDTO.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.domain.dto; 2 | 3 | /** 4 | * @author Author 知秋 5 | * @email fei6751803@163.com 6 | * @time Created by Auser on 2018/3/23 17:45. 7 | */ 8 | public class StudentDTO extends BaseStudentDTO{ 9 | private String id; 10 | 11 | public StudentDTO(String name, int age, double credit, String stream) { 12 | super(name, age, credit, stream); 13 | } 14 | 15 | public StudentDTO(String name, int age, double credit, String stream, String id) { 16 | super(name, age, credit, stream); 17 | this.id = id; 18 | } 19 | 20 | public String getId() { 21 | return id; 22 | } 23 | 24 | public void setId(String id) { 25 | this.id = id; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/repository/StudentDao.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.repository; 2 | 3 | import com.dockerx.rxjavaweb.domain.dto.BaseStudentDTO; 4 | import com.dockerx.rxjavaweb.domain.dto.StudentDTO; 5 | import com.dockerx.rxjavaweb.transformer.DocumentToStudentTransformer; 6 | import com.mongodb.rx.client.*; 7 | import org.bson.Document; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.beans.factory.annotation.Value; 12 | import org.springframework.stereotype.Repository; 13 | import rx.Observable; 14 | 15 | import static com.mongodb.client.model.Filters.eq; 16 | 17 | 18 | /** 19 | * @author Author 知秋 20 | * @email fei6751803@163.com 21 | * @time Created by Auser on 2018/3/23 17:51. 22 | */ 23 | @Repository 24 | public class StudentDao implements StudentRepository { 25 | private static final String STUDENT_COLLECTION = "student"; 26 | 27 | MongoCollection collection; 28 | 29 | private static final Logger logger = LoggerFactory.getLogger(StudentDao.class); 30 | 31 | @Autowired 32 | StudentDao(@Value("${spring.data.mongodb.uri}") String connectionUrl, 33 | @Value("${spring.data.mongodb.database}") String dbName) { 34 | MongoClient mongoClient = MongoClients.create(connectionUrl); 35 | MongoDatabase database = mongoClient.getDatabase(dbName); 36 | collection = database.getCollection(STUDENT_COLLECTION); 37 | } 38 | 39 | @Override 40 | public Observable createStudent(BaseStudentDTO student) { 41 | return collection.insertOne(createStudentDocument(student)) 42 | .doOnNext(s -> logger.debug("Student was created successfully.")) 43 | .doOnError(e -> logger.error("An ERROR occurred while creating a new Student", e)); 44 | } 45 | 46 | private Document createStudentDocument(BaseStudentDTO student) { 47 | return new Document(DocumentToStudentTransformer.NAME, student.getName()) 48 | .append(DocumentToStudentTransformer.AGE, student.getAge()) 49 | .append(DocumentToStudentTransformer.CREDIT, student.getCredit()) 50 | .append(DocumentToStudentTransformer.MAJOR, student.getMajor()); 51 | } 52 | @Override 53 | public Observable findByName(String name) { 54 | 55 | logger.debug("Fetching the student with name: " + name); 56 | return collection.find(eq(DocumentToStudentTransformer.NAME, name)) 57 | .toObservable() 58 | .map(document -> new DocumentToStudentTransformer().transform(document)) 59 | .doOnNext(s -> logger.debug("Student with the given name was retrieved.")) 60 | .doOnError(e -> logger.error("An ERROR occurred while fetching the student", e)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/repository/StudentRepository.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.repository; 2 | 3 | import com.dockerx.rxjavaweb.domain.dto.BaseStudentDTO; 4 | import com.dockerx.rxjavaweb.domain.dto.StudentDTO; 5 | import com.mongodb.rx.client.Success; 6 | import rx.Observable; 7 | 8 | /** 9 | * 定义Student Entity的数据访问接口 10 | * 11 | * @author Author 知秋 12 | * @email fei6751803@163.com 13 | * @time Created by Auser on 2018/3/23 17:32. 14 | */ 15 | public interface StudentRepository { 16 | 17 | /** 18 | * Creates a new Student document in the database. 19 | * 20 | * @param student 21 | * new {@link BaseStudentDTO} instance to be created. 22 | * @return The status of the operation. 23 | */ 24 | Observable createStudent(BaseStudentDTO student); 25 | 26 | /** 27 | * Fetches a Student with the given name. 28 | * 29 | * @param name 30 | * name of the student to be fetched. 31 | * @return The student with the specified name. 32 | */ 33 | Observable findByName(String name); 34 | } 35 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/returnhandler/ObservableReturnValueHandler.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.returnhandler; 2 | 3 | import org.springframework.core.MethodParameter; 4 | import org.springframework.web.context.request.NativeWebRequest; 5 | import org.springframework.web.context.request.async.DeferredResult; 6 | import org.springframework.web.context.request.async.WebAsyncUtils; 7 | import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; 8 | import org.springframework.web.method.support.ModelAndViewContainer; 9 | import rx.Observable; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @author Author 知秋 15 | * @email fei6751803@163.com 16 | * @time Created by Auser on 2018/3/23 18:59. 17 | */ 18 | public class ObservableReturnValueHandler implements AsyncHandlerMethodReturnValueHandler{ 19 | @Override 20 | public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { 21 | return returnValue != null && supportsReturnType(returnType); 22 | } 23 | 24 | @Override 25 | public boolean supportsReturnType(MethodParameter returnType) { 26 | return Observable.class.isAssignableFrom(returnType.getParameterType()); 27 | } 28 | 29 | @Override 30 | public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { 31 | if (returnValue == null) { 32 | mavContainer.setRequestHandled(true); 33 | return; 34 | } 35 | final Observable observable = Observable.class.cast(returnValue); 36 | WebAsyncUtils.getAsyncManager(webRequest) 37 | .startDeferredResultProcessing(new ObservableAdapter<>(observable), mavContainer); 38 | } 39 | 40 | public class ObservableAdapter extends DeferredResult> { 41 | ObservableAdapter(Observable observable) { 42 | observable.toList().subscribe(this::setResult, this::setErrorResult); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/service/CurrencyConverterService.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.service; 2 | 3 | import com.dockerx.rxjavaweb.domain.dto.CurrencyRatesDTO; 4 | import rx.Observable; 5 | 6 | import java.util.Set; 7 | 8 | /** 9 | * @author Author 知秋 10 | * @email fei6751803@163.com 11 | * @time Created by Auser on 2018/3/25 22:10. 12 | */ 13 | 14 | public interface CurrencyConverterService { 15 | Observable getCurrencyRates(Set currencies,String base); 16 | } 17 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/service/CurrencyConverterServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.service; 2 | 3 | import com.dockerx.rxjavaweb.domain.dto.CurrencyRatesDTO; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.web.client.RestTemplate; 10 | import org.springframework.web.util.UriComponentsBuilder; 11 | import rx.Observable; 12 | 13 | import java.util.Set; 14 | 15 | /** 16 | * @author Author 知秋 17 | * @email fei6751803@163.com 18 | * @time Created by Auser on 2018/3/25 22:12. 19 | */ 20 | @Service 21 | public class CurrencyConverterServiceImpl implements CurrencyConverterService { 22 | private static final Logger logger = LoggerFactory.getLogger(CurrencyConverterServiceImpl.class); 23 | @Value("${services.currency.uri}") 24 | private String CURRENCY_SERVICE_API; 25 | private static final String SYMBOLS = "symbols"; 26 | 27 | @Autowired 28 | private RestTemplate restTemplate; 29 | 30 | @Override 31 | public Observable getCurrencyRates(Set currencies,String base) { 32 | return getCurrencyRatesObservable(currencies,base); 33 | } 34 | 35 | private Observable getCurrencyRatesObservable(Set currencies,String base) { 36 | return Observable.create(observer -> { 37 | CurrencyRatesDTO currencyRatesDTO = 38 | restTemplate.getForEntity(UriComponentsBuilder.fromUriString(CURRENCY_SERVICE_API) 39 | .queryParam(SYMBOLS, 40 | currencies.toString()).queryParam("base",base) 41 | .toUriString(), 42 | CurrencyRatesDTO.class) 43 | .getBody(); 44 | observer.onNext(currencyRatesDTO); 45 | observer.onCompleted(); 46 | }).doOnNext(c -> logger.debug("Currency rates were retrieved successfully.")) 47 | .doOnError(e -> logger.error("An ERROR occurred while retrieving the currency rates.", e)); 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/transformer/DocumentToStudentTransformer.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.transformer; 2 | 3 | import com.dockerx.rxjavaweb.domain.dto.BaseStudentDTO; 4 | import com.dockerx.rxjavaweb.domain.dto.StudentDTO; 5 | import org.bson.Document; 6 | import org.bson.types.ObjectId; 7 | 8 | /** 9 | * @author Author 知秋 10 | * @email fei6751803@163.com 11 | * @time Created by Auser on 2018/3/23 18:01. 12 | */ 13 | public class DocumentToStudentTransformer implements Transformer { 14 | 15 | private static final String ID = "_id"; 16 | public static final String MAJOR = "major"; 17 | public static final String CREDIT = "credit"; 18 | public static final String AGE = "age"; 19 | public static final String NAME = "name"; 20 | @Override 21 | public StudentDTO transform(Document source) { 22 | return new StudentDTO(source.getString(NAME), source.getInteger(AGE), source.getDouble(CREDIT), 23 | source.getString(MAJOR), ((ObjectId) source.get(ID)).toString()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/java/com/dockerx/rxjavaweb/transformer/Transformer.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.transformer; 2 | 3 | /** 4 | * 将获取到的数据转化为实例对象 5 | * 6 | * @author Author 知秋 7 | * @email fei6751803@163.com 8 | * @time Created by Auser on 2018/3/23 18:02. 9 | */ 10 | public interface Transformer { 11 | R transform(T source); 12 | } 13 | -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | data: 3 | mongodb: 4 | uri: mongodb://localhost:27017 5 | database: studentdb 6 | server: 7 | port: 8080 8 | 9 | swagger: 10 | title: 汇率服务 11 | description: 外汇之间的查询 12 | version: 1.0 13 | 14 | services: 15 | currency: 16 | uri: http://api.fixer.io/latest -------------------------------------------------------------------------------- /第 五 章/version 1-student/rxjava-web/src/test/java/com/dockerx/rxjavaweb/RxjavaWebApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class RxjavaWebApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/.gitignore: -------------------------------------------------------------------------------- 1 | *#*# 2 | *.#* 3 | *.iml 4 | *.ipr 5 | *.iws 6 | *.pyc 7 | *.pyo 8 | *.swp 9 | *~ 10 | .DS_Store 11 | .cache 12 | .classpath 13 | .ensime 14 | .ensime_cache/ 15 | .ensime_lucene 16 | .generated-mima* 17 | .idea/ 18 | .idea_modules/ 19 | .project 20 | .pydevproject 21 | .scala_dependencies 22 | .settings 23 | /lib/ 24 | R-unit-tests.log 25 | R/unit-tests.out 26 | R/cran-check.out 27 | R/pkg/vignettes/sparkr-vignettes.html 28 | R/pkg/tests/fulltests/Rplots.pdf 29 | build/*.jar 30 | build/apache-maven* 31 | build/scala* 32 | build/zinc* 33 | cache 34 | checkpoint 35 | conf/*.cmd 36 | conf/*.conf 37 | conf/*.properties 38 | conf/*.sh 39 | conf/*.xml 40 | conf/java-opts 41 | conf/slaves 42 | dependency-reduced-pom.xml 43 | derby.log 44 | dev/create-release/*final 45 | dev/create-release/*txt 46 | dev/pr-deps/ 47 | dist/ 48 | docs/_site 49 | docs/api 50 | sql/docs 51 | sql/site 52 | lib_managed/ 53 | lint-r-report.log 54 | log/ 55 | logs/ 56 | out/ 57 | project/boot/ 58 | project/build/target/ 59 | project/plugins/lib_managed/ 60 | project/plugins/project/build.properties 61 | project/plugins/src_managed/ 62 | project/plugins/target/ 63 | python/lib/pyspark.zip 64 | python/deps 65 | python/pyspark/python 66 | reports/ 67 | scalastyle-on-compile.generated.xml 68 | scalastyle-output.xml 69 | scalastyle.txt 70 | spark-*-bin-*.tgz 71 | spark-tests.log 72 | src_managed/ 73 | streaming-tests.log 74 | target/ 75 | unit-tests.log 76 | work/ 77 | 78 | # For Hive 79 | TempStatsStore/ 80 | metastore/ 81 | metastore_db/ 82 | sql/hive-thriftserver/test_warehouses 83 | warehouse/ 84 | spark-warehouse/ 85 | 86 | # For R session data 87 | .RData 88 | .RHistory 89 | .Rhistory 90 | *.Rproj 91 | *.Rproj.* 92 | 93 | .Rproj.user 94 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyinchen/Java-programming-methodology-Rxjava-articles/23d4d5347295e890ccb1f21f1b32800975f5269e/第 五 章/version 2-currency/rxjava-web/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip 2 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.dockerx 7 | rxjava-web 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | rxjava-web 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.0.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 9 25 | 2.8.0 26 | 2.8.0 27 | 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-web 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-test 38 | test 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-tomcat 44 | compile 45 | 46 | 47 | 48 | io.springfox 49 | springfox-swagger2 50 | ${springfox.version} 51 | 52 | 53 | org.mapstruct 54 | mapstruct 55 | 56 | 57 | 58 | 59 | io.springfox 60 | springfox-swagger-ui 61 | ${springfoxui.version} 62 | 63 | 64 | mapstruct 65 | org.mapstruct 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.mongodb 73 | mongodb-driver-rx 74 | 1.5.0 75 | 76 | 77 | 78 | 79 | javax.xml.bind 80 | jaxb-api 81 | 2.3.0 82 | 83 | 84 | 85 | 86 | 87 | 88 | org.springframework.boot 89 | spring-boot-maven-plugin 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/RxjavaWebApplication.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.web.client.RestTemplate; 7 | 8 | @SpringBootApplication 9 | public class RxjavaWebApplication { 10 | 11 | @Bean 12 | public RestTemplate restTemplate() { 13 | return new RestTemplate(); 14 | } 15 | 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(RxjavaWebApplication.class, args); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.config; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.builders.RequestHandlerSelectors; 9 | import springfox.documentation.service.ApiInfo; 10 | import springfox.documentation.spi.DocumentationType; 11 | import springfox.documentation.spring.web.plugins.Docket; 12 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 13 | 14 | /** 15 | * @author Author 知秋 16 | * @email fei6751803@163.com 17 | * @time Created by Auser on 2018/3/25 20:41. 18 | */ 19 | @Configuration 20 | @EnableSwagger2 21 | public class SwaggerConfig { 22 | @Value("${swagger.title}") 23 | private String title; 24 | 25 | @Value("${swagger.description}") 26 | private String description; 27 | 28 | @Value("${swagger.version}") 29 | private String version; 30 | 31 | @Bean 32 | public Docket api() { 33 | return new Docket(DocumentationType.SWAGGER_2) 34 | .apiInfo(apiInfo()).select() 35 | .apis(RequestHandlerSelectors.basePackage("com.dockerx.rxjavaweb")).paths( 36 | PathSelectors.any()).build(); 37 | } 38 | 39 | /** 40 | * This method will return the API info object to swagger which will in turn 41 | * display the information on the swagger UI. 42 | * 43 | * @return the API information 44 | */ 45 | private ApiInfo apiInfo() { 46 | return new ApiInfoBuilder() 47 | .title(title) 48 | .description(description) 49 | .version(version) 50 | .build(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/config/Webconfig.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.config; 2 | 3 | import com.dockerx.rxjavaweb.returnhandler.ObservableReturnValueHandler; 4 | import org.springframework.context.annotation.ComponentScan; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.web.method.support.HandlerMethodReturnValueHandler; 7 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 8 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @author Author 知秋 14 | * @email fei6751803@163.com 15 | * @time Created by Auser on 2018/3/23 21:24. 16 | */ 17 | @Configuration 18 | @ComponentScan(basePackages = {"com.dockerx.rxjavaweb.*"}) 19 | public class Webconfig extends WebMvcConfigurationSupport { 20 | @Override 21 | protected void addReturnValueHandlers(List returnValueHandlers) { 22 | super.addReturnValueHandlers(returnValueHandlers); 23 | returnValueHandlers.add(new ObservableReturnValueHandler()); 24 | } 25 | 26 | @Override 27 | protected void addResourceHandlers(ResourceHandlerRegistry registry) { 28 | super.addResourceHandlers(registry); 29 | registry.addResourceHandler("swagger-ui.html") 30 | .addResourceLocations("classpath:/META-INF/resources/"); 31 | 32 | registry.addResourceHandler("/webjars/**") 33 | .addResourceLocations("classpath:/META-INF/resources/webjars/"); 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/controller/CurrencyController.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.controller; 2 | 3 | import com.dockerx.rxjavaweb.domain.dto.CurrencyRatesDTO; 4 | import com.dockerx.rxjavaweb.service.CurrencyConverterService; 5 | import io.swagger.annotations.ApiImplicitParam; 6 | import io.swagger.annotations.ApiOperation; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.MediaType; 11 | import org.springframework.web.bind.annotation.*; 12 | import rx.Observable; 13 | 14 | import java.util.Set; 15 | 16 | /** 17 | * @author Author 知秋 18 | * @email fei6751803@163.com 19 | * @time Created by Auser on 2018/3/25 22:32. 20 | */ 21 | @RestController 22 | @RequestMapping("/api/currencyconverter") 23 | public class CurrencyController { 24 | private static final Logger logger = LoggerFactory.getLogger(CurrencyController.class); 25 | 26 | @Autowired 27 | private CurrencyConverterService currencyConverterService; 28 | 29 | @ApiOperation(value="汇率查询", notes="根据想查的币种来查找与指定基础货币对比币种对应的汇率") 30 | @ApiImplicitParam(name = "symbols", value = "所要查询的汇率的币种集合", required = true) 31 | @GetMapping(value = "/rates",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}) 32 | public Observable getCurrencyRates(@RequestParam("symbols") Set symbols,@RequestParam("base")String base) { 33 | logger.debug("Retrieving currency rates."); 34 | return currencyConverterService.getCurrencyRates(symbols,base); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/controller/StudentController.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.controller; 2 | 3 | import com.dockerx.rxjavaweb.domain.dto.BaseStudentDTO; 4 | import com.dockerx.rxjavaweb.domain.dto.StudentDTO; 5 | import com.dockerx.rxjavaweb.repository.StudentRepository; 6 | import com.mongodb.rx.client.Success; 7 | import io.swagger.annotations.ApiImplicitParam; 8 | import io.swagger.annotations.ApiOperation; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.http.MediaType; 13 | import org.springframework.web.bind.annotation.*; 14 | import rx.Observable; 15 | 16 | /** 17 | * @author Author 知秋 18 | * @email fei6751803@163.com 19 | * @time Created by Auser on 2018/3/23 21:03. 20 | */ 21 | @RestController 22 | @RequestMapping("/api/students") 23 | public class StudentController { 24 | 25 | private static final Logger logger = LoggerFactory.getLogger(StudentController.class); 26 | 27 | private final StudentRepository studentRepository; 28 | 29 | @Autowired 30 | public StudentController(StudentRepository studentRepository) { 31 | this.studentRepository = studentRepository; 32 | } 33 | 34 | @ApiOperation(value="学生创建", notes="根据BaseStudentDTO对象创建学生") 35 | @ApiImplicitParam(name = "student", value = "学生详细实体student", required = true, dataType = "BaseStudentDTO") 36 | @PostMapping(value = "",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}) 37 | public Observable createStudent(@RequestBody BaseStudentDTO student) { 38 | logger.debug("Creating a new Student."); 39 | return studentRepository.createStudent(student); 40 | } 41 | @ApiOperation(value="学生查找", notes="根据name查找相应学生") 42 | @ApiImplicitParam(name = "name", value = "学生name", required = true, dataType = "String") 43 | @GetMapping(value = "",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}) 44 | public Observable getStudentByName(@RequestParam String name) { 45 | logger.debug("Fetching a new student with the Name: " + name); 46 | return studentRepository.findByName(name); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/domain/dto/BaseStudentDTO.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.domain.dto; 2 | 3 | import javax.validation.constraints.Min; 4 | import javax.validation.constraints.NotEmpty; 5 | import javax.validation.constraints.NotNull; 6 | 7 | /** 8 | * @author Author 知秋 9 | * @email fei6751803@163.com 10 | * @time Created by Auser on 2018/3/23 17:41. 11 | */ 12 | public class BaseStudentDTO { 13 | 14 | @NotNull 15 | @NotEmpty 16 | private String name; 17 | @Min(value = 1, message = "Age should be a positive number") 18 | private int age; 19 | @Min(value = 0, message = "Invalid credit value was given") 20 | private double credit; 21 | @NotNull 22 | @NotEmpty 23 | private String major; 24 | 25 | 26 | 27 | private BaseStudentDTO() { 28 | super(); 29 | } 30 | 31 | public BaseStudentDTO(String name, int age, double credit, String major) { 32 | super(); 33 | this.name = name; 34 | this.age = age; 35 | this.credit = credit; 36 | this.major = major; 37 | } 38 | 39 | public String getName() { 40 | return name; 41 | } 42 | 43 | public BaseStudentDTO setName(String name) { 44 | this.name = name; 45 | return this; 46 | } 47 | 48 | public int getAge() { 49 | return age; 50 | } 51 | 52 | public BaseStudentDTO setAge(int age) { 53 | this.age = age; 54 | return this; 55 | } 56 | 57 | public double getCredit() { 58 | return credit; 59 | } 60 | 61 | public BaseStudentDTO setCredit(double credit) { 62 | this.credit = credit; 63 | return this; 64 | } 65 | 66 | public String getMajor() { 67 | return major; 68 | } 69 | 70 | public BaseStudentDTO setMajor(String major) { 71 | this.major = major; 72 | return this; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/domain/dto/CurrencyRatesDTO.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.domain.dto; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * @author Author 知秋 7 | * @email fei6751803@163.com 8 | * @time Created by Auser on 2018/3/25 22:09. 9 | */ 10 | public class CurrencyRatesDTO { 11 | private String base; 12 | private String date; 13 | private Map rates; 14 | 15 | public String getBase() { 16 | return base; 17 | } 18 | 19 | public CurrencyRatesDTO setBase(String base) { 20 | this.base = base; 21 | return this; 22 | } 23 | 24 | public String getDate() { 25 | return date; 26 | } 27 | 28 | public CurrencyRatesDTO setDate(String date) { 29 | this.date = date; 30 | return this; 31 | } 32 | 33 | public Map getRates() { 34 | return rates; 35 | } 36 | 37 | public CurrencyRatesDTO setRates(Map rates) { 38 | this.rates = rates; 39 | return this; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/domain/dto/StudentDTO.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.domain.dto; 2 | 3 | /** 4 | * @author Author 知秋 5 | * @email fei6751803@163.com 6 | * @time Created by Auser on 2018/3/23 17:45. 7 | */ 8 | public class StudentDTO extends BaseStudentDTO{ 9 | private String id; 10 | 11 | public StudentDTO(String name, int age, double credit, String stream) { 12 | super(name, age, credit, stream); 13 | } 14 | 15 | public StudentDTO(String name, int age, double credit, String stream, String id) { 16 | super(name, age, credit, stream); 17 | this.id = id; 18 | } 19 | 20 | public String getId() { 21 | return id; 22 | } 23 | 24 | public void setId(String id) { 25 | this.id = id; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/repository/StudentDao.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.repository; 2 | 3 | import com.dockerx.rxjavaweb.domain.dto.BaseStudentDTO; 4 | import com.dockerx.rxjavaweb.domain.dto.StudentDTO; 5 | import com.dockerx.rxjavaweb.transformer.DocumentToStudentTransformer; 6 | import com.mongodb.rx.client.*; 7 | import org.bson.Document; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.beans.factory.annotation.Value; 12 | import org.springframework.stereotype.Repository; 13 | import rx.Observable; 14 | 15 | import static com.mongodb.client.model.Filters.eq; 16 | 17 | 18 | /** 19 | * @author Author 知秋 20 | * @email fei6751803@163.com 21 | * @time Created by Auser on 2018/3/23 17:51. 22 | */ 23 | @Repository 24 | public class StudentDao implements StudentRepository { 25 | private static final String STUDENT_COLLECTION = "student"; 26 | 27 | MongoCollection collection; 28 | 29 | private static final Logger logger = LoggerFactory.getLogger(StudentDao.class); 30 | 31 | @Autowired 32 | StudentDao(@Value("${spring.data.mongodb.uri}") String connectionUrl, 33 | @Value("${spring.data.mongodb.database}") String dbName) { 34 | MongoClient mongoClient = MongoClients.create(connectionUrl); 35 | MongoDatabase database = mongoClient.getDatabase(dbName); 36 | collection = database.getCollection(STUDENT_COLLECTION); 37 | } 38 | 39 | @Override 40 | public Observable createStudent(BaseStudentDTO student) { 41 | return collection.insertOne(createStudentDocument(student)) 42 | .doOnNext(s -> logger.debug("Student was created successfully.")) 43 | .doOnError(e -> logger.error("An ERROR occurred while creating a new Student", e)); 44 | } 45 | 46 | private Document createStudentDocument(BaseStudentDTO student) { 47 | return new Document(DocumentToStudentTransformer.NAME, student.getName()) 48 | .append(DocumentToStudentTransformer.AGE, student.getAge()) 49 | .append(DocumentToStudentTransformer.CREDIT, student.getCredit()) 50 | .append(DocumentToStudentTransformer.MAJOR, student.getMajor()); 51 | } 52 | @Override 53 | public Observable findByName(String name) { 54 | 55 | logger.debug("Fetching the student with name: " + name); 56 | return collection.find(eq(DocumentToStudentTransformer.NAME, name)) 57 | .toObservable() 58 | .map(document -> new DocumentToStudentTransformer().transform(document)) 59 | .doOnNext(s -> logger.debug("Student with the given name was retrieved.")) 60 | .doOnError(e -> logger.error("An ERROR occurred while fetching the student", e)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/repository/StudentRepository.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.repository; 2 | 3 | import com.dockerx.rxjavaweb.domain.dto.BaseStudentDTO; 4 | import com.dockerx.rxjavaweb.domain.dto.StudentDTO; 5 | import com.mongodb.rx.client.Success; 6 | import rx.Observable; 7 | 8 | /** 9 | * 定义Student Entity的数据访问接口 10 | * 11 | * @author Author 知秋 12 | * @email fei6751803@163.com 13 | * @time Created by Auser on 2018/3/23 17:32. 14 | */ 15 | public interface StudentRepository { 16 | 17 | /** 18 | * Creates a new Student document in the database. 19 | * 20 | * @param student 21 | * new {@link BaseStudentDTO} instance to be created. 22 | * @return The status of the operation. 23 | */ 24 | Observable createStudent(BaseStudentDTO student); 25 | 26 | /** 27 | * Fetches a Student with the given name. 28 | * 29 | * @param name 30 | * name of the student to be fetched. 31 | * @return The student with the specified name. 32 | */ 33 | Observable findByName(String name); 34 | } 35 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/returnhandler/ObservableReturnValueHandler.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.returnhandler; 2 | 3 | import org.springframework.core.MethodParameter; 4 | import org.springframework.web.context.request.NativeWebRequest; 5 | import org.springframework.web.context.request.async.DeferredResult; 6 | import org.springframework.web.context.request.async.WebAsyncUtils; 7 | import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; 8 | import org.springframework.web.method.support.ModelAndViewContainer; 9 | import rx.Observable; 10 | 11 | /** 12 | * @author Author 知秋 13 | * @email fei6751803@163.com 14 | * @time Created by Auser on 2018/3/23 18:59. 15 | */ 16 | public class ObservableReturnValueHandler implements AsyncHandlerMethodReturnValueHandler{ 17 | @Override 18 | public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { 19 | return returnValue != null && supportsReturnType(returnType); 20 | } 21 | 22 | @Override 23 | public boolean supportsReturnType(MethodParameter returnType) { 24 | return Observable.class.isAssignableFrom(returnType.getParameterType()); 25 | } 26 | 27 | @Override 28 | public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { 29 | if (returnValue == null) { 30 | mavContainer.setRequestHandled(true); 31 | return; 32 | } 33 | final Observable observable = Observable.class.cast(returnValue); 34 | WebAsyncUtils.getAsyncManager(webRequest) 35 | .startDeferredResultProcessing(new ObservableAdapter<>(observable), mavContainer); 36 | } 37 | 38 | public class ObservableAdapter extends DeferredResult { 39 | ObservableAdapter(Observable observable) { 40 | observable.subscribe(this::setResult, this::setErrorResult); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/service/CurrencyConverterService.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.service; 2 | 3 | import com.dockerx.rxjavaweb.domain.dto.CurrencyRatesDTO; 4 | import rx.Observable; 5 | 6 | import java.util.Set; 7 | 8 | /** 9 | * @author Author 知秋 10 | * @email fei6751803@163.com 11 | * @time Created by Auser on 2018/3/25 22:10. 12 | */ 13 | 14 | public interface CurrencyConverterService { 15 | Observable getCurrencyRates(Set currencies,String base); 16 | } 17 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/service/CurrencyConverterServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.service; 2 | 3 | import com.dockerx.rxjavaweb.domain.dto.CurrencyRatesDTO; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.web.client.RestTemplate; 10 | import org.springframework.web.util.UriComponentsBuilder; 11 | import rx.Observable; 12 | 13 | import java.util.Set; 14 | 15 | /** 16 | * @author Author 知秋 17 | * @email fei6751803@163.com 18 | * @time Created by Auser on 2018/3/25 22:12. 19 | */ 20 | @Service 21 | public class CurrencyConverterServiceImpl implements CurrencyConverterService { 22 | private static final Logger logger = LoggerFactory.getLogger(CurrencyConverterServiceImpl.class); 23 | @Value("${services.currency.uri}") 24 | private String CURRENCY_SERVICE_API; 25 | private static final String SYMBOLS = "symbols"; 26 | 27 | @Autowired 28 | private RestTemplate restTemplate; 29 | 30 | @Override 31 | public Observable getCurrencyRates(Set currencies,String base) { 32 | return getCurrencyRatesObservable(currencies,base); 33 | } 34 | 35 | private Observable getCurrencyRatesObservable(Set currencies,String base) { 36 | return Observable.create(observer -> { 37 | CurrencyRatesDTO currencyRatesDTO = 38 | restTemplate.getForEntity(UriComponentsBuilder.fromUriString(CURRENCY_SERVICE_API) 39 | .queryParam(SYMBOLS, 40 | currencies.toString()).queryParam("base",base) 41 | .toUriString(), 42 | CurrencyRatesDTO.class) 43 | .getBody(); 44 | observer.onNext(currencyRatesDTO); 45 | observer.onCompleted(); 46 | }).doOnNext(c -> logger.debug("Currency rates were retrieved successfully.")) 47 | .doOnError(e -> logger.error("An ERROR occurred while retrieving the currency rates.", e)); 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/transformer/DocumentToStudentTransformer.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.transformer; 2 | 3 | import com.dockerx.rxjavaweb.domain.dto.BaseStudentDTO; 4 | import com.dockerx.rxjavaweb.domain.dto.StudentDTO; 5 | import org.bson.Document; 6 | import org.bson.types.ObjectId; 7 | 8 | /** 9 | * @author Author 知秋 10 | * @email fei6751803@163.com 11 | * @time Created by Auser on 2018/3/23 18:01. 12 | */ 13 | public class DocumentToStudentTransformer implements Transformer { 14 | 15 | private static final String ID = "_id"; 16 | public static final String MAJOR = "major"; 17 | public static final String CREDIT = "credit"; 18 | public static final String AGE = "age"; 19 | public static final String NAME = "name"; 20 | @Override 21 | public StudentDTO transform(Document source) { 22 | return new StudentDTO(source.getString(NAME), source.getInteger(AGE), source.getDouble(CREDIT), 23 | source.getString(MAJOR), ((ObjectId) source.get(ID)).toString()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/java/com/dockerx/rxjavaweb/transformer/Transformer.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb.transformer; 2 | 3 | /** 4 | * 将获取到的数据转化为实例对象 5 | * 6 | * @author Author 知秋 7 | * @email fei6751803@163.com 8 | * @time Created by Auser on 2018/3/23 18:02. 9 | */ 10 | public interface Transformer { 11 | R transform(T source); 12 | } 13 | -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | data: 3 | mongodb: 4 | uri: mongodb://localhost:27017 5 | database: studentdb 6 | server: 7 | port: 8080 8 | 9 | swagger: 10 | title: 汇率服务 11 | description: 外汇之间的转换 12 | version: 1.0 13 | 14 | services: 15 | currency: 16 | uri: http://api.fixer.io/latest -------------------------------------------------------------------------------- /第 五 章/version 2-currency/rxjava-web/src/test/java/com/dockerx/rxjavaweb/RxjavaWebApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxjavaweb; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class RxjavaWebApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /第 八 章/info.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyinchen/Java-programming-methodology-Rxjava-articles/23d4d5347295e890ccb1f21f1b32800975f5269e/第 八 章/info.md -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.dockerx 7 | rx-react 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | rx-react 12 | RxJava2 Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.1.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 9 25 | 2.8.0 26 | 2.8.0 27 | 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-web 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-tomcat 37 | compile 38 | 39 | 40 | com.google.guava 41 | guava 42 | 23.6-jre 43 | 44 | 45 | io.reactivex.rxjava2 46 | rxjava 47 | 2.1.7 48 | 49 | 50 | com.fasterxml.jackson.datatype 51 | jackson-datatype-jsr310 52 | 2.9.2 53 | 54 | 55 | org.yaml 56 | snakeyaml 57 | 1.19 58 | 59 | 60 | org.apache.commons 61 | commons-lang3 62 | 3.6 63 | 64 | 65 | commons-io 66 | commons-io 67 | 2.5 68 | 69 | 70 | org.projectlombok 71 | lombok 72 | true 73 | 74 | 75 | org.springframework.boot 76 | spring-boot-starter-test 77 | test 78 | 79 | 80 | 81 | com.github.servanter 82 | netsfjson-support-spring 83 | 1.0.0 84 | 85 | 86 | io.springfox 87 | springfox-swagger2 88 | ${springfox.version} 89 | 90 | 91 | org.mapstruct 92 | mapstruct 93 | 94 | 95 | 96 | 97 | io.springfox 98 | springfox-swagger-ui 99 | ${springfoxui.version} 100 | 101 | 102 | mapstruct 103 | org.mapstruct 104 | 105 | 106 | 107 | 108 | javax.xml.bind 109 | jaxb-api 110 | 2.3.0 111 | 112 | 113 | 114 | 115 | 116 | 117 | org.springframework.boot 118 | spring-boot-maven-plugin 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/RxReactApplication.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.web.client.RestTemplate; 7 | 8 | @SpringBootApplication 9 | public class RxReactApplication { 10 | 11 | @Bean 12 | public RestTemplate restTemplate() { 13 | return new RestTemplate(); 14 | } 15 | public static void main(String[] args) { 16 | SpringApplication.run(RxReactApplication.class, args); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.config; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.builders.RequestHandlerSelectors; 9 | import springfox.documentation.service.ApiInfo; 10 | import springfox.documentation.spi.DocumentationType; 11 | import springfox.documentation.spring.web.plugins.Docket; 12 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 13 | 14 | /** 15 | * @author Author 知秋 16 | * @email fei6751803@163.com 17 | * @time Created by Auser on 2018/4/23 20:55. 18 | */ 19 | @Configuration 20 | @EnableSwagger2 21 | public class SwaggerConfig { 22 | @Value("${swagger.title}") 23 | private String title; 24 | 25 | @Value("${swagger.description}") 26 | private String description; 27 | 28 | @Value("${swagger.version}") 29 | private String version; 30 | 31 | @Bean 32 | public Docket api() { 33 | return new Docket(DocumentationType.SWAGGER_2) 34 | .apiInfo(apiInfo()).select() 35 | .apis(RequestHandlerSelectors.basePackage("com.dockerx.rxreact")).paths( 36 | PathSelectors.any()).build(); 37 | } 38 | 39 | /** 40 | * This method will return the API info object to swagger which will in turn 41 | * display the information on the swagger UI. 42 | * 43 | * @return the API information 44 | */ 45 | private ApiInfo apiInfo() { 46 | return new ApiInfoBuilder() 47 | .title(title) 48 | .description(description) 49 | .version(version) 50 | .build(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/config/Webconfig.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.config; 2 | 3 | 4 | import com.dockerx.rxreact.returnhandler.FlowableReturnValueHandler; 5 | import org.springframework.context.annotation.ComponentScan; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.web.method.support.HandlerMethodReturnValueHandler; 8 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 9 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @author Author 知秋 15 | * @email fei6751803@163.com 16 | * @time Created by Auser on 2018/4/23 21:24. 17 | */ 18 | @Configuration 19 | @ComponentScan(basePackages = {"com.dockerx.rxreact.*"}) 20 | public class Webconfig extends WebMvcConfigurationSupport { 21 | @Override 22 | protected void addReturnValueHandlers(List returnValueHandlers) { 23 | super.addReturnValueHandlers(returnValueHandlers); 24 | returnValueHandlers.add(new FlowableReturnValueHandler()); 25 | } 26 | 27 | @Override 28 | protected void addResourceHandlers(ResourceHandlerRegistry registry) { 29 | super.addResourceHandlers(registry); 30 | registry.addResourceHandler("swagger-ui.html") 31 | .addResourceLocations("classpath:/META-INF/resources/"); 32 | 33 | registry.addResourceHandler("/webjars/**") 34 | .addResourceLocations("classpath:/META-INF/resources/webjars/"); 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/controller/ReposController.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.controller; 2 | 3 | import com.dockerx.rxreact.domain.Commit; 4 | import com.dockerx.rxreact.domain.Repository; 5 | import com.dockerx.rxreact.services.GitHubService; 6 | import io.reactivex.Flowable; 7 | import io.reactivex.schedulers.Schedulers; 8 | import io.swagger.annotations.ApiImplicitParam; 9 | import io.swagger.annotations.ApiImplicitParams; 10 | import io.swagger.annotations.ApiOperation; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.http.MediaType; 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.PathVariable; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RestController; 17 | 18 | /** 19 | * @author Author 知秋 20 | * @email fei6751803@163.com 21 | * @time Created by Auser on 2018/4/23 1:41. 22 | */ 23 | @RestController 24 | @RequestMapping("/api/repos") 25 | public class ReposController { 26 | private final GitHubService gitHubService; 27 | 28 | @Autowired 29 | public ReposController(GitHubService gitHubService) { 30 | this.gitHubService = gitHubService; 31 | } 32 | @ApiOperation(value="github用户所有仓库查询", notes="根据用户名称来查找其所有的仓库") 33 | @ApiImplicitParam(name = "user", value = "所要查询的用户名称", required = true) 34 | @GetMapping(value = "/0/{user}",produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 35 | public Flowable list0(@PathVariable String user){ 36 | return gitHubService.getRepos0(user).observeOn(Schedulers.io()); 37 | } 38 | 39 | @ApiOperation(value="github用户所有仓库查询", notes="根据用户名称来查找其所有的仓库") 40 | @ApiImplicitParam(name = "user", value = "所要查询的用户名称", required = true) 41 | @GetMapping(value = "{user}",produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 42 | public Flowable list(@PathVariable String user){ 43 | return gitHubService.getRepos(user).observeOn(Schedulers.io()); 44 | } 45 | @ApiOperation(value="github用户一周内有推送的仓库查询", notes="根据用户名称来查找其一周内有推送的仓库") 46 | @ApiImplicitParam(name = "user", value = "所要查询的用户名称", required = true) 47 | @GetMapping(value = "/inweek/{user}",produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 48 | public Flowable getReposInWeek(@PathVariable String user){ 49 | return gitHubService.getReposInWeek(user).observeOn(Schedulers.io()); 50 | } 51 | @ApiOperation(value="github用户指定仓库一周内的提交信息") 52 | @ApiImplicitParams({ 53 | @ApiImplicitParam(name = "user", value = "所要查询的用户名称", required = true), 54 | @ApiImplicitParam(name = "repo", value = "所要查询的用户的仓库名称", required = true) 55 | }) 56 | @GetMapping(value = "/inweek/{user}/{repo}") 57 | public Flowable getCommitsInWeek(@PathVariable String user, @PathVariable String repo){ 58 | return gitHubService.getCommitsInWeek(user,repo).observeOn(Schedulers.io()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/domain/Author.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.*; 5 | 6 | /** 7 | * @author Author 知秋 8 | * @email fei6751803@163.com 9 | * @time Created by Auser on 2018/4/22 22:29. 10 | */ 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | @Getter 14 | @Builder 15 | @ToString 16 | @JsonIgnoreProperties(ignoreUnknown = true) 17 | public class Author { 18 | private String login; 19 | private String html_url; 20 | private boolean site_admin; 21 | private String organizations_url; 22 | } 23 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/domain/Commit.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import lombok.*; 6 | 7 | /** 8 | * @author Author 知秋 9 | * @email fei6751803@163.com 10 | * @time Created by Auser on 2018/4/22 22:25. 11 | */ 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @ToString 15 | @Getter 16 | @Builder 17 | @JsonIgnoreProperties(ignoreUnknown = true) 18 | public class Commit { 19 | private String sha; 20 | @JsonProperty("committer") 21 | private Committer committer; 22 | @JsonProperty("author") 23 | private Author author; 24 | @JsonProperty("commit") 25 | private CommitContent commitContent; 26 | } 27 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/domain/CommitContent.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.*; 5 | 6 | /** 7 | * @author Author 知秋 8 | * @email fei6751803@163.com 9 | * @time Created by Auser on 2018/4/23 18:55. 10 | */ 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | @ToString 14 | @Getter 15 | @Builder 16 | @JsonIgnoreProperties(ignoreUnknown = true) 17 | public class CommitContent { 18 | private String message; 19 | private String url; 20 | private int comment_count; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/domain/CommittedFile.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.*; 5 | 6 | /** 7 | * @author Author 知秋 8 | * @email fei6751803@163.com 9 | * @time Created by Auser on 2018/4/22 23:21. 10 | */ 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | @Getter 14 | @Builder 15 | @ToString 16 | @JsonIgnoreProperties(ignoreUnknown = true) 17 | public class CommittedFile { 18 | private String filename; 19 | private long changes; 20 | } 21 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/domain/Committer.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.*; 5 | 6 | /** 7 | * @author Author 知秋 8 | * @email fei6751803@163.com 9 | * @time Created by Auser on 2018/4/22 22:29. 10 | */ 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | @Getter 14 | @Builder 15 | @ToString 16 | @JsonIgnoreProperties(ignoreUnknown = true) 17 | public class Committer { 18 | private String login; 19 | private String html_url; 20 | private String organizations_url; 21 | } 22 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/domain/Repository.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import lombok.*; 6 | import org.springframework.format.annotation.DateTimeFormat; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | /** 11 | * @author Author 知秋 12 | * @email fei6751803@163.com 13 | * @time Created by Auser on 2018/4/22 21:55. 14 | */ 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Getter 18 | @Builder 19 | @ToString 20 | @JsonIgnoreProperties(ignoreUnknown = true) 21 | public class Repository { 22 | private String name; 23 | @JsonProperty("pushed_at") 24 | @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) 25 | private LocalDateTime pushed; 26 | } 27 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/domain/SingleCommit.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.*; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author Author 知秋 10 | * @email fei6751803@163.com 11 | * @time Created by Auser on 2018/4/22 23:20. 12 | */ 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | @Getter 16 | @Builder 17 | @ToString 18 | @JsonIgnoreProperties(ignoreUnknown = true) 19 | public class SingleCommit { 20 | private List files; 21 | } 22 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/repository/GitHbubRepos.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.repository; 2 | 3 | import com.dockerx.rxreact.domain.Commit; 4 | import com.dockerx.rxreact.domain.Repository; 5 | import com.dockerx.rxreact.domain.SingleCommit; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.client.RestTemplate; 9 | import org.springframework.web.util.UriComponentsBuilder; 10 | 11 | import java.time.ZoneOffset; 12 | import java.time.ZonedDateTime; 13 | import java.time.format.DateTimeFormatter; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | 17 | import static java.lang.String.format; 18 | 19 | /** 20 | * @author Author 知秋 21 | * @email fei6751803@163.com 22 | * @time Created by Auser on 2018/4/22 21:49. 23 | */ 24 | @Slf4j 25 | @org.springframework.stereotype.Repository 26 | public class GitHbubRepos { 27 | private RestTemplate restTemplate; 28 | 29 | private static final String BASE = "https://api.github.com"; 30 | static final String REPOS = BASE + "/users/%s/repos"; 31 | static final String COMMITS = BASE + "/repos/%s/%s/commits?since=%s"; 32 | static final String SINGLE_COMMIT = BASE + "/repos/%s/%s/commits/%s"; 33 | 34 | @Autowired 35 | public GitHbubRepos(RestTemplate restTemplate) { 36 | this.restTemplate = restTemplate; 37 | } 38 | 39 | public List getRepos(String user){ 40 | log.info(format("Get repos by user(%s)", user)); 41 | String format = format(REPOS, user); 42 | // The results may be found null, that is, it will lead the field become null and should be judged 43 | Repository[] forObject = restTemplate.getForObject(UriComponentsBuilder 44 | .fromUriString(format) 45 | .queryParam("direction","desc") 46 | .toUriString(), Repository[].class); 47 | return Arrays.asList(forObject != null ? forObject : new Repository[0]); 48 | } 49 | 50 | public List getCommitsInWeek(String user, String repo) { 51 | String aWeekAgo = ZonedDateTime.now(ZoneOffset.UTC) 52 | .minusWeeks(1) 53 | .minusDays(1) 54 | .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'00:00:00'Z'")); 55 | 56 | String url = format(COMMITS, user, repo, aWeekAgo); 57 | Commit[] commits = restTemplate.getForObject(url, Commit[].class); 58 | log.info(format("Get commits by repo(%s): %d commits are found", url, commits != null ? commits.length : 0)); 59 | return Arrays.asList(commits != null ? commits : new Commit[0]); 60 | 61 | } 62 | 63 | public SingleCommit getSingleCommit(String user, String repo, String sha) { 64 | log.info(format("Get a single commit by sha(%s)", sha)); 65 | return restTemplate.getForObject(format(SINGLE_COMMIT, user, repo, sha), SingleCommit.class); 66 | } 67 | 68 | public SingleCommit getSingleCommitByUrl(String url) { 69 | log.info(format("Get a single commit by url(%s)", url)); 70 | return restTemplate.getForObject(url, SingleCommit.class); 71 | } 72 | 73 | public static void main(String[] args) { 74 | String aWeekAgo = ZonedDateTime.now(ZoneOffset.UTC) 75 | .minusWeeks(1) 76 | .minusDays(1) 77 | .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'00:00:00'Z'")); 78 | 79 | System.out.println(aWeekAgo); 80 | String url = format(COMMITS, "aaa", "bbb", aWeekAgo); 81 | System.out.println(url); 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/returnhandler/FlowableReturnValueHandler.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.returnhandler; 2 | 3 | import io.reactivex.Flowable; 4 | import io.reactivex.Observable; 5 | import org.springframework.core.MethodParameter; 6 | import org.springframework.web.context.request.NativeWebRequest; 7 | import org.springframework.web.context.request.async.DeferredResult; 8 | import org.springframework.web.context.request.async.WebAsyncUtils; 9 | import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; 10 | import org.springframework.web.method.support.ModelAndViewContainer; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @author Author 知秋 16 | * @email fei6751803@163.com 17 | * @time Created by Auser on 2018/4/23 18:59. 18 | */ 19 | public class FlowableReturnValueHandler implements AsyncHandlerMethodReturnValueHandler { 20 | @Override 21 | public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { 22 | return returnValue != null && supportsReturnType(returnType); 23 | } 24 | 25 | @Override 26 | public boolean supportsReturnType(MethodParameter returnType) { 27 | return Observable.class.isAssignableFrom(returnType.getParameterType()); 28 | } 29 | 30 | @Override 31 | public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { 32 | if (returnValue == null) { 33 | mavContainer.setRequestHandled(true); 34 | return; 35 | } 36 | final Flowable observable = Flowable.class.cast(returnValue); 37 | WebAsyncUtils.getAsyncManager(webRequest) 38 | .startDeferredResultProcessing(new FlowableAdapter<>(observable), mavContainer); 39 | } 40 | 41 | public class FlowableAdapter extends DeferredResult> { 42 | FlowableAdapter(Flowable flowable) { 43 | flowable.toList().subscribe(this::setResult, this::setErrorResult); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/services/GitHubService.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.services; 2 | 3 | import com.dockerx.rxreact.domain.Commit; 4 | import com.dockerx.rxreact.domain.CommittedFile; 5 | import com.dockerx.rxreact.domain.Repository; 6 | import io.reactivex.Flowable; 7 | 8 | /** 9 | * @author Author 知秋 10 | * @email fei6751803@163.com 11 | * @time Created by Auser on 2018/4/22 23:31. 12 | */ 13 | public interface GitHubService { 14 | Flowable getRepos0(String user); 15 | 16 | Flowable getRepos(String user); 17 | 18 | Flowable getReposInWeek(String user); 19 | 20 | Flowable getCommitsInWeek(String user, String repo); 21 | 22 | Flowable getCommittedFiles(String user, String repo, String sha); 23 | 24 | Flowable getCommittedFilesByUrl(String url); 25 | 26 | Flowable getCommittedFilesByUser(String user); 27 | } 28 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/services/GitHubServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.services; 2 | 3 | import com.dockerx.rxreact.domain.Commit; 4 | import com.dockerx.rxreact.domain.CommittedFile; 5 | import com.dockerx.rxreact.domain.Repository; 6 | import com.dockerx.rxreact.domain.SingleCommit; 7 | import com.dockerx.rxreact.repository.GitHbubRepos; 8 | import io.reactivex.BackpressureStrategy; 9 | import io.reactivex.Emitter; 10 | import io.reactivex.Flowable; 11 | import io.reactivex.functions.BiConsumer; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.apache.commons.lang3.tuple.Pair; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.stereotype.Service; 16 | 17 | import java.time.LocalDateTime; 18 | import java.util.Iterator; 19 | import java.util.concurrent.Callable; 20 | 21 | /** 22 | * @author Author 知秋 23 | * @email fei6751803@163.com 24 | * @time Created by Auser on 2018/4/22 23:32. 25 | */ 26 | @Slf4j 27 | @Service 28 | public class GitHubServiceImpl implements GitHubService { 29 | 30 | private final GitHbubRepos gitHbubRepos; 31 | 32 | @Autowired 33 | public GitHubServiceImpl(GitHbubRepos gitHbubRepos) { 34 | this.gitHbubRepos = gitHbubRepos; 35 | } 36 | @Override 37 | public Flowable getRepos0(String user) { 38 | Callable> initialState = 39 | gitHbubRepos.getRepos(user) 40 | .stream()::iterator; 41 | BiConsumer, Emitter> generator = 42 | (iterator, emitter) -> { 43 | if (iterator.hasNext()) { 44 | emitter.onNext(iterator.next()); 45 | } else { 46 | emitter.onComplete(); 47 | } 48 | }; 49 | return Flowable.generate(initialState, generator); 50 | } 51 | @Override 52 | public Flowable getRepos(String user) { 53 | Callable> initialState = 54 | () -> gitHbubRepos.getRepos(user) 55 | .stream() 56 | .map(Repository::getName).iterator(); 57 | BiConsumer, Emitter> generator = 58 | (iterator, emitter) -> { 59 | if (iterator.hasNext()) { 60 | emitter.onNext(iterator.next() + " "); 61 | } else { 62 | emitter.onComplete(); 63 | } 64 | }; 65 | return Flowable.generate(initialState, generator); 66 | } 67 | 68 | 69 | @Override 70 | public Flowable getReposInWeek(String user) { 71 | return Flowable.create(emitter -> { 72 | gitHbubRepos.getRepos(user).stream() 73 | .filter(repo -> repo.getPushed().isAfter(LocalDateTime.now().minusWeeks(1))) 74 | .map(item -> item.getName() + " ") 75 | .forEach(emitter::onNext); 76 | emitter.onComplete(); 77 | }, BackpressureStrategy.BUFFER); 78 | } 79 | 80 | @Override 81 | public Flowable getCommitsInWeek(String user, String repo) { 82 | return Flowable.create(emitter -> { 83 | gitHbubRepos.getCommitsInWeek(user, repo) 84 | .forEach(emitter::onNext); 85 | emitter.onComplete(); 86 | }, BackpressureStrategy.BUFFER); 87 | } 88 | 89 | @Override 90 | public Flowable getCommittedFiles(String user, String repo, String sha) { 91 | return Flowable.create(emitter -> { 92 | gitHbubRepos.getSingleCommit(user, repo, sha).getFiles() 93 | .forEach(emitter::onNext); 94 | emitter.onComplete(); 95 | }, BackpressureStrategy.BUFFER); 96 | } 97 | 98 | @Override 99 | public Flowable getCommittedFilesByUrl(String url) { 100 | return Flowable.create(emitter -> { 101 | SingleCommit commit = gitHbubRepos.getSingleCommitByUrl(url); 102 | log.info(commit.toString()); 103 | commit.getFiles().forEach(files -> { 104 | log.info(files.toString()); 105 | emitter.onNext(files); 106 | }); 107 | emitter.onComplete(); 108 | }, BackpressureStrategy.BUFFER); 109 | } 110 | 111 | @Override 112 | public Flowable getCommittedFilesByUser(String user) { 113 | return getReposInWeek(user) 114 | .flatMap(repo -> Flowable.combineLatest( 115 | Flowable.just(repo), 116 | getCommitsInWeek(user, repo), 117 | Pair::of)) 118 | .flatMap(pair -> getCommittedFiles(user, pair.getLeft(), pair.getRight().getSha())); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/java/com/dockerx/rxreact/utils/LanguageAnalysis.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.utils; 2 | 3 | /** 4 | * Todo 5 | * @author Author 知秋 6 | * @email fei6751803@163.com 7 | * @time Created by Auser on 2018/4/23 20:30. 8 | */ 9 | public class LanguageAnalysis { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | 4 | swagger: 5 | title: GITHUB用户仓库信息二三事 6 | description: 查询事宜 7 | version: 1.0 8 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/test/java/com/dockerx/rxreact/RxReactApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class RxReactApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/test/java/com/dockerx/rxreact/repository/GitHbubReposTest.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.repository; 2 | 3 | import com.dockerx.rxreact.domain.Commit; 4 | import com.dockerx.rxreact.domain.CommittedFile; 5 | import com.dockerx.rxreact.domain.Repository; 6 | import com.dockerx.rxreact.domain.SingleCommit; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | import org.springframework.http.MediaType; 12 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 13 | import org.springframework.test.web.client.MockRestServiceServer; 14 | import org.springframework.web.client.RestTemplate; 15 | 16 | import java.time.ZoneOffset; 17 | import java.time.ZonedDateTime; 18 | import java.time.format.DateTimeFormatter; 19 | import java.util.List; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | import static org.assertj.core.api.AssertionsForClassTypes.tuple; 23 | import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; 24 | import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; 25 | 26 | /** 27 | * @author Author 知秋 28 | * @email fei6751803@163.com 29 | * @time Created by Auser on 2018/4/26 1:19. 30 | */ 31 | @RunWith(SpringJUnit4ClassRunner.class) 32 | @SpringBootTest 33 | public class GitHbubReposTest { 34 | 35 | @Autowired 36 | private RestTemplate restTemplate; 37 | 38 | @Autowired 39 | private GitHbubRepos gitHbubRepos; 40 | 41 | private static final String user = "kkTranslation"; 42 | private static final String repo = "Java-9-Spring-Webflux"; 43 | private static final String sha = "fee4488e595422aacbf02e67aa70e5e46781392c"; 44 | @Test 45 | public void getRepos() { 46 | MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build(); 47 | mockServer.expect(requestTo(String.format(GitHbubRepos.REPOS+"?direction=desc", user))) 48 | .andRespond(withSuccess( 49 | "[{\"name\":\"zheng\",\"pushed_at\":[2017,6,3,10,56,26]}," + 50 | "{\"name\":\"ui-for-docker\",\"pushed_at\":[2016,12,3,22,23,52]}," + 51 | "{\"name\":\"micro-service-practice\",\"pushed_at\":[2016,6,17,1,51,13]}," + 52 | "{\"name\":\"Java-9-Spring-Webflux\",\"pushed_at\":[2018,4,24,16,5,47]}," + 53 | "{\"name\":\"guava\",\"pushed_at\":[2017,5,7,15,17,46]}]", 54 | /*pushed_at 可能会有变化,具体请自行修改*/ 55 | MediaType.APPLICATION_JSON) 56 | ); 57 | 58 | List repos = gitHbubRepos.getRepos(user); 59 | 60 | assertThat(repos.size()).isEqualTo(5); 61 | assertThat(repos.get(0).getPushed().getHour()).isEqualTo(10); 62 | mockServer.verify(); 63 | } 64 | 65 | @Test 66 | public void getCommitsInWeek() { 67 | String aWeekAgo = ZonedDateTime.now(ZoneOffset.UTC).minusWeeks(1).minusDays(1) 68 | .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'00:00:00'Z'")); 69 | MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build(); 70 | mockServer.expect(requestTo(String.format(GitHbubRepos.COMMITS, user, repo, aWeekAgo))) 71 | .andRespond(withSuccess( /*此处请自行添加结果测试,或者找自己的仓库设置*/ 72 | "[{\"sha\":\"fee4488e595422aacbf02e67aa70e5e46781392c\",\"committer\":{\"login\":\"muyinchen\",\"html_url\":\"https://github.com/muyinchen\",\"organizations_url\":\"https://api.github.com/users/muyinchen/orgs\"},\"author\":{\"login\":\"muyinchen\",\"html_url\":\"https://github.com/muyinchen\",\"site_admin\":false,\"organizations_url\":\"https://api.github.com/users/muyinchen/orgs\"},\"commit\":{\"message\":\"Flowable 和Spring web的结合使用\",\"url\":\"https://api.github.com/repos/kkTranslation/Java-9-Spring-Webflux/git/commits/fee4488e595422aacbf02e67aa70e5e46781392c\",\"comment_count\":0}}]", 73 | MediaType.APPLICATION_JSON)); 74 | 75 | List commits = gitHbubRepos.getCommitsInWeek(user, repo); 76 | 77 | assertThat(commits.size()).isEqualTo(1); 78 | assertThat(commits.get(0).getCommitter().getLogin()).isEqualTo("muyinchen"); 79 | mockServer.verify(); 80 | } 81 | 82 | @Test 83 | public void getSingleCommit() { 84 | /* MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build(); 85 | mockServer.expect(requestTo(String.format(GitHbubRepos.SINGLE_COMMIT, user, repo, sha))) 86 | .andRespond(withSuccess( 87 | *//*内容有点多,就省略了,结果比较少的话,可以复制进来打开测试*//* 88 | "", 89 | MediaType.APPLICATION_JSON));*/ 90 | 91 | SingleCommit singleCommit = gitHbubRepos.getSingleCommit(user, repo, sha); 92 | 93 | assertThat(singleCommit.getFiles()) 94 | .extracting(CommittedFile::getFilename, CommittedFile::getChanges) 95 | .contains(tuple("chapter14/Spring Demo/rx-react/pom.xml", 124L)); 96 | } 97 | 98 | 99 | } -------------------------------------------------------------------------------- /第 八 章/结合Spring Web应用使用Flowable/rx-react/src/test/java/com/dockerx/rxreact/services/GitHubServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.rxreact.services; 2 | 3 | import com.dockerx.rxreact.domain.*; 4 | import com.dockerx.rxreact.repository.GitHbubRepos; 5 | import io.reactivex.Flowable; 6 | import io.reactivex.subscribers.TestSubscriber; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.mockito.Mock; 11 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 12 | 13 | import java.time.LocalDateTime; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | 17 | import static org.assertj.core.api.Assertions.assertThat; 18 | import static org.mockito.ArgumentMatchers.anyString; 19 | import static org.mockito.Mockito.when; 20 | import static org.mockito.MockitoAnnotations.initMocks; 21 | 22 | /** 23 | * @author Author 知秋 24 | * @email fei6751803@163.com 25 | * @time Created by Auser on 2018/4/26 18:02. 26 | */ 27 | @RunWith(SpringJUnit4ClassRunner.class) 28 | public class GitHubServiceImplTest { 29 | 30 | @Mock 31 | private GitHbubRepos gitHbubRepos; 32 | private GitHubService gitHubService; 33 | 34 | @Before 35 | public void setup() { 36 | initMocks(this); 37 | gitHubService = new GitHubServiceImpl(gitHbubRepos); 38 | when(gitHbubRepos.getRepos(anyString())).thenAnswer(m -> getReposForTest()); 39 | 40 | when(gitHbubRepos.getCommitsInWeek(anyString(), anyString())).thenAnswer(m -> getCommitsInWeekForTest()); 41 | when(gitHbubRepos.getSingleCommit(anyString(), 42 | anyString(), 43 | anyString())).thenAnswer(m -> getSingleCommitForTest()); 44 | when(gitHbubRepos.getSingleCommitByUrl(anyString())).thenAnswer(m -> getSingleCommitForTest()); 45 | } 46 | 47 | private List getReposForTest() { 48 | try { 49 | Thread.sleep(1000); 50 | } catch (InterruptedException ignore) { 51 | } 52 | return Arrays.asList( 53 | new Repository("repo1", LocalDateTime.now().minusMonths(1)), 54 | new Repository("repo2", LocalDateTime.now()), 55 | new Repository("repo3", LocalDateTime.now().minusDays(3)) 56 | ); 57 | } 58 | 59 | @Test 60 | public void getRepos() { 61 | TestSubscriber testSubscriber = new TestSubscriber<>(); 62 | 63 | 64 | gitHubService.getRepos("test-user") 65 | .subscribe(testSubscriber); 66 | 67 | testSubscriber.assertNoErrors(); 68 | assertThat(testSubscriber.values()) 69 | .as("getRepos returns all user code repository ") 70 | .containsExactly("repo1 ", "repo2 ", "repo3 "); 71 | } 72 | 73 | @Test 74 | public void getReposInWeek() { 75 | TestSubscriber testSubscriber = new TestSubscriber<>(); 76 | 77 | gitHubService.getReposInWeek("test-user") 78 | .subscribe(testSubscriber); 79 | 80 | testSubscriber.assertNoErrors(); 81 | assertThat(testSubscriber.values()) 82 | .as("getReposInWeek returns updated repos within one week") 83 | .containsExactly("repo2 ", "repo3 "); 84 | } 85 | 86 | @Test 87 | public void getCommitsInWeek() { 88 | TestSubscriber testSubscriber = new TestSubscriber<>(); 89 | 90 | gitHubService.getCommitsInWeek("test-user", "test-repo") 91 | .subscribe(testSubscriber); 92 | 93 | testSubscriber.assertNoErrors(); 94 | assertThat(testSubscriber.values()) 95 | .as("getCommitsInWeek returns commits updated by `user` within one week") 96 | .extracting(Commit::getSha) 97 | .containsExactly("sha1"); 98 | } 99 | 100 | 101 | @Test 102 | public void getCommittedFilesByUser() { 103 | TestSubscriber testSubscriber = new TestSubscriber<>(); 104 | 105 | gitHubService.getCommittedFilesByUser("test-user") 106 | .subscribe(testSubscriber); 107 | 108 | testSubscriber.assertNoErrors(); 109 | assertThat(testSubscriber.values()) 110 | .as("getCommittedFilesByUser returns CommittedFiles by `user`") 111 | .extracting(CommittedFile::getFilename) 112 | .containsOnly("filename1", "filename1", "filename2", "filename2"); 113 | } 114 | 115 | private List getCommitsInWeekForTest() { 116 | try { 117 | Thread.sleep(300); 118 | } catch (InterruptedException ignore) { 119 | } 120 | return Arrays.asList( 121 | Commit.builder() 122 | .sha("sha1") 123 | .committer(new Committer("test-user", 124 | "https://github.com/muyinchen", 125 | "https://api.github.com/users/muyinchen/orgs")) 126 | .author(new Author("url2", 127 | "https://github.com/muyinchen", 128 | true, 129 | "https://api.github.com/users/muyinchen/orgs")) 130 | .build(), 131 | Commit.builder() 132 | .sha("sha2") 133 | .committer(new Committer("no-test-user", 134 | "https://github.com/muyinchen/no-test-user", 135 | "https://api.github.com/users/muyinchen/orgs/no-test-user")) 136 | .author(new Author("url2", 137 | "https://github.com/muyinchen/no-test-user", 138 | true, 139 | "https://api.github.com/users/muyinchen/orgs/no-test-user")) 140 | .build() 141 | ); 142 | } 143 | 144 | private SingleCommit getSingleCommitForTest() { 145 | try { 146 | Thread.sleep(300); 147 | } catch (InterruptedException ignore) { 148 | } 149 | return new SingleCommit( 150 | Arrays.asList( 151 | new CommittedFile("filename1", 10), 152 | new CommittedFile("filename2", 20) 153 | ) 154 | ); 155 | } 156 | 157 | @Test 158 | public void testTestSubscriber() { 159 | 160 | TestSubscriber testSubscriber = new TestSubscriber<>(); 161 | //In order to emit "1", "2", "3" 162 | Flowable.just("1", "2", "3").subscribe(testSubscriber); 163 | //Assert whether values are equal 164 | testSubscriber.assertValues("1", "2", "3"); 165 | //Assert value does not exist 166 | testSubscriber.assertNever("4"); 167 | //Is the number of asserted values equal? 168 | testSubscriber.assertValueCount(3); 169 | //Assertion terminated 170 | testSubscriber.assertTerminated(); 171 | } 172 | } --------------------------------------------------------------------------------