├── .gitignore ├── .idea ├── .gitignore ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── dataSources.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml ├── sqldialects.xml ├── uiDesigner.xml └── vcs.xml ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── part1 ├── build.gradle ├── chapter1 │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── itvillage │ │ ├── Example1_1.java │ │ └── Example1_2.java ├── chapter2 │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── itvillage │ │ └── Example2_5.java ├── chapter3 │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ ├── spring-mvc-branchoffice │ │ ├── build.gradle │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── settings.gradle │ │ └── src │ │ │ ├── main │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── itvillage │ │ │ │ │ ├── SpringMvcBranchOfficeApplication.java │ │ │ │ │ ├── SpringMvcBranchOfficeController.java │ │ │ │ │ └── domain │ │ │ │ │ └── Book.java │ │ │ └── resources │ │ │ │ └── application.yml │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── itvillage │ │ │ └── SpringMvcBranchOfficeApplicationTests.java │ ├── spring-mvc-headoffice │ │ ├── build.gradle │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── settings.gradle │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── com │ │ │ │ └── itvillage │ │ │ │ ├── SpringMvcHeadOfficeApplication.java │ │ │ │ ├── SpringMvcHeadOfficeController.java │ │ │ │ └── domain │ │ │ │ └── Book.java │ │ │ └── resources │ │ │ └── application.yml │ ├── spring-reactive-branchoffice │ │ ├── build.gradle │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── settings.gradle │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── com │ │ │ │ └── itvillage │ │ │ │ ├── SpringReactiveBranchOfficeApplication.java │ │ │ │ ├── SpringReactiveBranchOfficeController.java │ │ │ │ └── domain │ │ │ │ └── Book.java │ │ │ └── resources │ │ │ └── application.yml │ └── spring-reactive-headoffice │ │ ├── build.gradle │ │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── settings.gradle │ │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── itvillage │ │ │ ├── SpringReactiveHeadOfficeApplication.java │ │ │ ├── SpringReactiveHeadOfficeController.java │ │ │ └── domain │ │ │ └── Book.java │ │ └── resources │ │ └── application.yml ├── chapter4 │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── itvillage │ │ ├── CryptoCurrency.java │ │ ├── Example4_1.java │ │ ├── Example4_11.java │ │ ├── Example4_13.java │ │ ├── Example4_15.java │ │ ├── Example4_17.java │ │ ├── Example4_4.java │ │ ├── Example4_5.java │ │ ├── Example4_6.java │ │ ├── Example4_7.java │ │ ├── Example4_8.java │ │ ├── Example4_9.java │ │ ├── PaymentCalculator.java │ │ └── SampleData.java ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── part2 ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── hs_err_pid20044.log ├── replay_pid20044.log ├── settings.gradle └── src │ ├── main │ ├── java │ │ ├── chapter10 │ │ │ ├── Example10_1.java │ │ │ ├── Example10_10.java │ │ │ ├── Example10_11.java │ │ │ ├── Example10_2.java │ │ │ ├── Example10_3.java │ │ │ ├── Example10_4.java │ │ │ ├── Example10_5.java │ │ │ ├── Example10_6.java │ │ │ ├── Example10_7.java │ │ │ ├── Example10_8.java │ │ │ └── Example10_9.java │ │ ├── chapter11 │ │ │ ├── Example11_1.java │ │ │ ├── Example11_3.java │ │ │ ├── Example11_4.java │ │ │ ├── Example11_5.java │ │ │ ├── Example11_6.java │ │ │ ├── Example11_7.java │ │ │ └── Example11_8.java │ │ ├── chapter12 │ │ │ ├── Example12_1.java │ │ │ ├── Example12_2.java │ │ │ ├── Example12_3.java │ │ │ ├── Example12_4.java │ │ │ ├── Example12_5.java │ │ │ ├── Example12_6.java │ │ │ └── Example12_7.java │ │ ├── chapter13 │ │ │ ├── BackpressureTestExample.java │ │ │ ├── ContextTestExample.java │ │ │ ├── GeneralTestExample.java │ │ │ ├── PublisherProbeTestExample.java │ │ │ ├── RecordTestExample.java │ │ │ └── TimeBasedTestExample.java │ │ ├── chapter14 │ │ │ ├── Book.java │ │ │ ├── CryptoCurrencyPriceEmitter.java │ │ │ ├── CryptoCurrencyPriceListener.java │ │ │ ├── SampleData.java │ │ │ ├── operator_1_create │ │ │ │ ├── Example14_1.java │ │ │ │ ├── Example14_10.java │ │ │ │ ├── Example14_11.java │ │ │ │ ├── Example14_12.java │ │ │ │ ├── Example14_13.java │ │ │ │ ├── Example14_14.java │ │ │ │ ├── Example14_2.java │ │ │ │ ├── Example14_3.java │ │ │ │ ├── Example14_4.java │ │ │ │ ├── Example14_5.java │ │ │ │ ├── Example14_6.java │ │ │ │ ├── Example14_7.java │ │ │ │ ├── Example14_8.java │ │ │ │ └── Example14_9.java │ │ │ ├── operator_2_filter │ │ │ │ ├── Example14_15.java │ │ │ │ ├── Example14_16.java │ │ │ │ ├── Example14_17.java │ │ │ │ ├── Example14_18.java │ │ │ │ ├── Example14_19.java │ │ │ │ ├── Example14_20.java │ │ │ │ ├── Example14_21.java │ │ │ │ ├── Example14_22.java │ │ │ │ ├── Example14_23.java │ │ │ │ ├── Example14_24.java │ │ │ │ ├── Example14_25.java │ │ │ │ └── Example14_26.java │ │ │ ├── operator_3_transformation │ │ │ │ ├── Example14_27.java │ │ │ │ ├── Example14_28.java │ │ │ │ ├── Example14_29.java │ │ │ │ ├── Example14_30.java │ │ │ │ ├── Example14_31.java │ │ │ │ ├── Example14_32.java │ │ │ │ ├── Example14_33.java │ │ │ │ ├── Example14_34.java │ │ │ │ ├── Example14_35.java │ │ │ │ ├── Example14_36.java │ │ │ │ ├── Example14_37.java │ │ │ │ ├── Example14_38.java │ │ │ │ ├── Example14_39.java │ │ │ │ ├── Example14_40.java │ │ │ │ └── Example14_41.java │ │ │ ├── operator_4_peek │ │ │ │ └── Example14_42.java │ │ │ ├── operator_5_error │ │ │ │ ├── Example14_43.java │ │ │ │ ├── Example14_44.java │ │ │ │ ├── Example14_45.java │ │ │ │ ├── Example14_46.java │ │ │ │ ├── Example14_47.java │ │ │ │ ├── Example14_48.java │ │ │ │ ├── Example14_49.java │ │ │ │ └── Example14_50.java │ │ │ ├── operator_6_time │ │ │ │ ├── Example14_51.java │ │ │ │ └── Example14_52.java │ │ │ ├── operator_7_split │ │ │ │ ├── Example14_53.java │ │ │ │ ├── Example14_54.java │ │ │ │ ├── Example14_55.java │ │ │ │ ├── Example14_56.java │ │ │ │ ├── Example14_57.java │ │ │ │ ├── Example14_58.java │ │ │ │ └── Example14_59.java │ │ │ └── operator_8_multicast │ │ │ │ ├── Example14_60.java │ │ │ │ ├── Example14_61.java │ │ │ │ ├── Example14_62.java │ │ │ │ └── Example14_63.java │ │ ├── chapter5 │ │ │ └── Example5_1.java │ │ ├── chapter6 │ │ │ ├── Example6_1.java │ │ │ ├── Example6_2.java │ │ │ ├── Example6_3.java │ │ │ ├── Example6_4.java │ │ │ ├── Example6_5.java │ │ │ ├── Example6_6.java │ │ │ └── Example6_7.java │ │ ├── chapter7 │ │ │ ├── Example7_1.java │ │ │ ├── Example7_2.java │ │ │ ├── Example7_3.java │ │ │ └── Example7_4.java │ │ ├── chapter8 │ │ │ ├── Example8_1.java │ │ │ ├── Example8_2.java │ │ │ ├── Example8_3.java │ │ │ ├── Example8_4.java │ │ │ ├── Example8_5.java │ │ │ └── Example8_6.java │ │ └── chapter9 │ │ │ ├── Example9_1.java │ │ │ ├── Example9_10.java │ │ │ ├── Example9_2.java │ │ │ ├── Example9_4.java │ │ │ ├── Example9_8.java │ │ │ └── Example9_9.java │ └── resources │ │ └── logback.xml │ └── test │ └── java │ └── chapter13 │ ├── ExampleTest13_1.java │ ├── ExampleTest13_11.java │ ├── ExampleTest13_12.java │ ├── ExampleTest13_14.java │ ├── ExampleTest13_16.java │ ├── ExampleTest13_17.java │ ├── ExampleTest13_18.java │ ├── ExampleTest13_19.java │ ├── ExampleTest13_21.java │ ├── ExampleTest13_3.java │ ├── ExampleTest13_4.java │ ├── ExampleTest13_5.java │ ├── ExampleTest13_7.java │ ├── ExampleTest13_8.java │ └── ExampleTest13_9.java ├── part3 ├── build.gradle ├── chapter15 │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── itvillage │ │ │ │ ├── Chapter15Application.java │ │ │ │ └── book │ │ │ │ ├── controller │ │ │ │ └── BookController.java │ │ │ │ ├── dto │ │ │ │ └── BookDto.java │ │ │ │ ├── filter │ │ │ │ ├── BookLogFilter.java │ │ │ │ └── BookRouterFunctionFilter.java │ │ │ │ └── router_function │ │ │ │ └── BookRouterFunction.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── itvillage │ │ └── Chapter15ApplicationTests.java ├── chapter16 │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── itvillage │ │ │ │ ├── Chapter16Application.java │ │ │ │ ├── mvc │ │ │ │ └── book │ │ │ │ │ ├── Book.java │ │ │ │ │ ├── BookDto.java │ │ │ │ │ ├── BookMvcController.java │ │ │ │ │ ├── BookMvcMapper.java │ │ │ │ │ └── BookMvcService.java │ │ │ │ └── reactive │ │ │ │ ├── v1 │ │ │ │ └── book │ │ │ │ │ ├── Book.java │ │ │ │ │ ├── BookController.java │ │ │ │ │ ├── BookDto.java │ │ │ │ │ ├── BookMapper.java │ │ │ │ │ └── BookService.java │ │ │ │ └── v2 │ │ │ │ └── book │ │ │ │ ├── Book.java │ │ │ │ ├── BookController.java │ │ │ │ ├── BookDto.java │ │ │ │ ├── BookMapper.java │ │ │ │ └── BookService.java │ │ └── resources │ │ │ └── application.yml │ │ └── test │ │ └── java │ │ └── com │ │ └── itvillage │ │ └── Chapter16ApplicationTests.java ├── chapter17 │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── itvillage │ │ │ │ ├── Chapter17Application.java │ │ │ │ └── book │ │ │ │ ├── v1 │ │ │ │ ├── Book.java │ │ │ │ ├── BookDto.java │ │ │ │ ├── BookHandler.java │ │ │ │ ├── BookMapper.java │ │ │ │ └── BookRouter.java │ │ │ │ ├── v2 │ │ │ │ ├── Book.java │ │ │ │ ├── BookDto.java │ │ │ │ ├── BookHandler.java │ │ │ │ ├── BookMapper.java │ │ │ │ ├── BookRouter.java │ │ │ │ └── BookValidator.java │ │ │ │ ├── v3 │ │ │ │ ├── Book.java │ │ │ │ ├── BookDto.java │ │ │ │ ├── BookHandler.java │ │ │ │ ├── BookMapper.java │ │ │ │ ├── BookRouter.java │ │ │ │ └── BookValidator.java │ │ │ │ └── v4 │ │ │ │ ├── Book.java │ │ │ │ ├── BookDto.java │ │ │ │ ├── BookHandler.java │ │ │ │ ├── BookMapper.java │ │ │ │ ├── BookRouter.java │ │ │ │ └── BookValidator.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── itvillage │ │ └── Chapter17ApplicationTests.java ├── chapter18 │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── itvillage │ │ │ │ ├── Chapter18Application.java │ │ │ │ ├── book │ │ │ │ ├── config │ │ │ │ │ └── RouterConfig.java │ │ │ │ ├── v5 │ │ │ │ │ ├── Book.java │ │ │ │ │ ├── BookDto.java │ │ │ │ │ ├── BookHandler.java │ │ │ │ │ ├── BookMapper.java │ │ │ │ │ ├── BookRepository.java │ │ │ │ │ ├── BookRouter.java │ │ │ │ │ ├── BookService.java │ │ │ │ │ └── BookValidator.java │ │ │ │ ├── v6 │ │ │ │ │ ├── Book.java │ │ │ │ │ ├── BookDto.java │ │ │ │ │ ├── BookHandler.java │ │ │ │ │ ├── BookMapper.java │ │ │ │ │ ├── BookRouter.java │ │ │ │ │ ├── BookService.java │ │ │ │ │ └── BookValidator.java │ │ │ │ ├── v7 │ │ │ │ │ ├── Book.java │ │ │ │ │ ├── BookDto.java │ │ │ │ │ ├── BookHandler.java │ │ │ │ │ ├── BookMapper.java │ │ │ │ │ ├── BookRepository.java │ │ │ │ │ ├── BookRouter.java │ │ │ │ │ ├── BookService.java │ │ │ │ │ └── BookValidator.java │ │ │ │ └── v8 │ │ │ │ │ ├── Book.java │ │ │ │ │ ├── BookDto.java │ │ │ │ │ ├── BookHandler.java │ │ │ │ │ ├── BookMapper.java │ │ │ │ │ ├── BookRouter.java │ │ │ │ │ ├── BookService.java │ │ │ │ │ └── BookValidator.java │ │ │ │ ├── exception │ │ │ │ ├── BusinessLogicException.java │ │ │ │ └── ExceptionCode.java │ │ │ │ └── utils │ │ │ │ └── CustomBeanUtils.java │ │ └── resources │ │ │ ├── application.yml │ │ │ └── db │ │ │ └── h2 │ │ │ ├── data.sql │ │ │ └── schema.sql │ │ └── test │ │ └── java │ │ └── com │ │ └── itvillage │ │ ├── Chapter18ApplicationTests.java │ │ └── utils │ │ └── CustomBeanUtilsTest.java ├── chapter19 │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── itvillage │ │ │ │ ├── Chapter19Application.java │ │ │ │ ├── book │ │ │ │ ├── config │ │ │ │ │ └── RouterConfig.java │ │ │ │ ├── v10 │ │ │ │ │ ├── Book.java │ │ │ │ │ ├── BookDto.java │ │ │ │ │ ├── BookHandler.java │ │ │ │ │ ├── BookMapper.java │ │ │ │ │ ├── BookRouter.java │ │ │ │ │ ├── BookService.java │ │ │ │ │ ├── BookValidator.java │ │ │ │ │ └── ErrorResponse.java │ │ │ │ └── v9 │ │ │ │ │ ├── Book.java │ │ │ │ │ ├── BookDto.java │ │ │ │ │ ├── BookHandler.java │ │ │ │ │ ├── BookMapper.java │ │ │ │ │ ├── BookRouter.java │ │ │ │ │ ├── BookService.java │ │ │ │ │ ├── BookValidator.java │ │ │ │ │ └── ErrorResponse.java │ │ │ │ ├── exception │ │ │ │ ├── BusinessLogicException.java │ │ │ │ ├── ExceptionCode.java │ │ │ │ └── GlobalWebExceptionHandler.java │ │ │ │ └── utils │ │ │ │ └── CustomBeanUtils.java │ │ └── resources │ │ │ ├── application.yml │ │ │ └── db │ │ │ └── h2 │ │ │ ├── data.sql │ │ │ └── schema.sql │ │ └── test │ │ └── java │ │ └── com │ │ └── itvillage │ │ └── Chapter19ApplicationTests.java ├── chapter20 │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── itvillage │ │ │ │ ├── Chapter20Application.java │ │ │ │ ├── config │ │ │ │ └── RouterConfig.java │ │ │ │ ├── example │ │ │ │ ├── BookDto.java │ │ │ │ ├── WebClientExample01.java │ │ │ │ └── WebClientExample02.java │ │ │ │ ├── exception │ │ │ │ ├── BusinessLogicException.java │ │ │ │ ├── ExceptionCode.java │ │ │ │ └── GlobalWebExceptionHandler.java │ │ │ │ ├── utils │ │ │ │ └── CustomBeanUtils.java │ │ │ │ └── v10 │ │ │ │ ├── Book.java │ │ │ │ ├── BookDto.java │ │ │ │ ├── BookHandler.java │ │ │ │ ├── BookMapper.java │ │ │ │ ├── BookRouter.java │ │ │ │ ├── BookService.java │ │ │ │ ├── BookValidator.java │ │ │ │ └── ErrorResponse.java │ │ └── resources │ │ │ ├── application.yml │ │ │ └── db │ │ │ └── h2 │ │ │ ├── data.sql │ │ │ └── schema.sql │ │ └── test │ │ └── java │ │ └── com │ │ └── itvillage │ │ └── Chapter20ApplicationTests.java ├── chapter21 │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── itvillage │ │ │ │ ├── Chapter21Application.java │ │ │ │ ├── config │ │ │ │ └── RouterConfig.java │ │ │ │ ├── example │ │ │ │ ├── BookDto.java │ │ │ │ ├── WebClientExample01.java │ │ │ │ └── WebClientExample02.java │ │ │ │ ├── exception │ │ │ │ ├── BusinessLogicException.java │ │ │ │ ├── ExceptionCode.java │ │ │ │ └── GlobalWebExceptionHandler.java │ │ │ │ ├── utils │ │ │ │ └── CustomBeanUtils.java │ │ │ │ └── v11 │ │ │ │ ├── Book.java │ │ │ │ ├── BookDto.java │ │ │ │ ├── BookHandler.java │ │ │ │ ├── BookMapper.java │ │ │ │ ├── BookRouter.java │ │ │ │ ├── BookService.java │ │ │ │ ├── BookValidator.java │ │ │ │ ├── BookWebClient.java │ │ │ │ └── ErrorResponse.java │ │ └── resources │ │ │ ├── application.yml │ │ │ └── db │ │ │ └── h2 │ │ │ ├── data.sql │ │ │ └── schema.sql │ │ └── test │ │ └── java │ │ └── com │ │ └── itvillage │ │ └── Chapter21ApplicationTests.java ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idear 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/r 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | **/out/ 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/dataSources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | h2.unified 6 | true 7 | org.h2.Driver 8 | jdbc:h2:mem:test 9 | $ProjectFileDir$ 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/sqldialects.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring-Reactive! 2 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'com.itvillage' 6 | version '1.0-SNAPSHOT' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 14 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 15 | } 16 | 17 | test { 18 | useJUnitPlatform() 19 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part1/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.6.1' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.itvillage' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '1.8' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | } 20 | 21 | dependencies { 22 | implementation 'org.springframework.boot:spring-boot-starter-web' 23 | compileOnly 'org.projectlombok:lombok' 24 | annotationProcessor 'org.projectlombok:lombok' 25 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 26 | } 27 | 28 | test { 29 | useJUnitPlatform() 30 | } -------------------------------------------------------------------------------- /part1/chapter1/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'com.itvillage' 6 | version '1.0-SNAPSHOT' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 14 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 15 | } 16 | 17 | test { 18 | useJUnitPlatform() 19 | } -------------------------------------------------------------------------------- /part1/chapter1/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part1/chapter1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part1/chapter1/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part1/chapter1/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'chapter1' 2 | 3 | -------------------------------------------------------------------------------- /part1/chapter1/src/main/java/com/itvillage/Example1_1.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | public class Example1_1 { 7 | public static void main(String[] args) { 8 | List numbers = Arrays.asList(1, 3, 21, 10, 8, 11); 9 | int sum = 0; 10 | for(int number : numbers){ 11 | if(number > 6 && (number % 2 != 0)){ 12 | sum += number; 13 | } 14 | } 15 | 16 | System.out.println("합계: " + sum); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /part1/chapter1/src/main/java/com/itvillage/Example1_2.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | public class Example1_2 { 7 | public static void main(String[] args) { 8 | List numbers = Arrays.asList(1, 3, 21, 10, 8, 11); 9 | int sum = numbers.stream() 10 | .filter(number -> number > 6 && (number % 2 != 0)) 11 | .mapToInt(number -> number) 12 | .sum(); 13 | 14 | System.out.println("합계: " + sum); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /part1/chapter2/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'com.itvillage' 6 | version '1.0-SNAPSHOT' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | implementation 'io.projectreactor:reactor-core:3.4.13' 14 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 15 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 16 | } 17 | 18 | test { 19 | useJUnitPlatform() 20 | } -------------------------------------------------------------------------------- /part1/chapter2/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part1/chapter2/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part1/chapter2/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part1/chapter2/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'chapter2' 2 | 3 | -------------------------------------------------------------------------------- /part1/chapter2/src/main/java/com/itvillage/Example2_5.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import reactor.core.publisher.Flux; 4 | 5 | public class Example2_5 { 6 | public static void main(String[] args) { 7 | Flux 8 | .just(1, 2, 3, 4, 5, 6) 9 | .filter(n -> n % 2 == 0) 10 | .map(n -> n * 2) 11 | .subscribe(System.out::println); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /part1/chapter3/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'com.itvillage' 6 | version '1.0-SNAPSHOT' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 14 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 15 | } 16 | 17 | test { 18 | useJUnitPlatform() 19 | } -------------------------------------------------------------------------------- /part1/chapter3/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part1/chapter3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part1/chapter3/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part1/chapter3/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'chapter3' 2 | 3 | -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-branchoffice/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.2' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.itvillage' 8 | version = '0.0.1-SNAPSHOT' 9 | 10 | configurations { 11 | compileOnly { 12 | extendsFrom annotationProcessor 13 | } 14 | } 15 | 16 | repositories { 17 | mavenCentral() 18 | } 19 | 20 | dependencies { 21 | implementation 'org.springframework.boot:spring-boot-starter-web' 22 | compileOnly 'org.projectlombok:lombok' 23 | annotationProcessor 'org.projectlombok:lombok' 24 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 25 | } 26 | 27 | test { 28 | useJUnitPlatform() 29 | } -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-branchoffice/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part1/chapter3/spring-mvc-branchoffice/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-branchoffice/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-branchoffice/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'spring-mvc-branchoffice' 2 | 3 | -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-branchoffice/src/main/java/com/itvillage/SpringMvcBranchOfficeApplication.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import com.itvillage.domain.Book; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.annotation.Bean; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | @SpringBootApplication 11 | public class SpringMvcBranchOfficeApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(SpringMvcBranchOfficeApplication.class, args); 15 | } 16 | 17 | @Bean 18 | public Map bookMap() { 19 | Map bookMap = new HashMap<>(); 20 | for (long i = 1; i <= 2_000_000; i++) { 21 | bookMap.put(i, new Book(i, "IT Book" + i, 2000)); 22 | } 23 | 24 | return bookMap; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-branchoffice/src/main/java/com/itvillage/SpringMvcBranchOfficeController.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import com.itvillage.domain.Book; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | import java.util.Map; 11 | 12 | /** 13 | * 본사 서버에서 들어오는 요청을 처리하는 Spring MVC 기반 14 | * 지점 API Server 15 | */ 16 | @Slf4j 17 | @RestController 18 | @RequestMapping("/v1/books") 19 | public class SpringMvcBranchOfficeController { 20 | private Map bookMap; 21 | 22 | @Autowired 23 | public SpringMvcBranchOfficeController(Map bookMap) { 24 | this.bookMap = bookMap; 25 | } 26 | 27 | @ResponseStatus(HttpStatus.OK) 28 | @GetMapping("/{book-id}") 29 | public ResponseEntity getBook(@PathVariable("book-id") long bookId) 30 | throws InterruptedException { 31 | Thread.sleep(5000); 32 | 33 | Book book = bookMap.get(bookId); 34 | 35 | return ResponseEntity.ok(book); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-branchoffice/src/main/java/com/itvillage/domain/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data 7 | @AllArgsConstructor 8 | public class Book { 9 | private long bookId; 10 | private String name; 11 | private int price; 12 | } 13 | -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-branchoffice/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 7070 3 | 4 | logging: 5 | pattern: 6 | console: "%d{HH:mm:ss.SSS} [%thread] %-5level- %msg%n" -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-branchoffice/src/test/java/com/itvillage/SpringMvcBranchOfficeApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringMvcBranchOfficeApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-headoffice/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.2' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.itvillage' 8 | version = '0.0.1-SNAPSHOT' 9 | 10 | configurations { 11 | compileOnly { 12 | extendsFrom annotationProcessor 13 | } 14 | } 15 | 16 | repositories { 17 | mavenCentral() 18 | } 19 | 20 | dependencies { 21 | implementation 'org.springframework.boot:spring-boot-starter-web' 22 | compileOnly 'org.projectlombok:lombok' 23 | annotationProcessor 'org.projectlombok:lombok' 24 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 25 | } 26 | 27 | test { 28 | useJUnitPlatform() 29 | } 30 | -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-headoffice/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part1/chapter3/spring-mvc-headoffice/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-headoffice/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-headoffice/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'spring-mvc-headoffice' 2 | 3 | -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-headoffice/src/main/java/com/itvillage/domain/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.domain; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class Book { 7 | private String name; 8 | private int price; 9 | } 10 | -------------------------------------------------------------------------------- /part1/chapter3/spring-mvc-headoffice/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | pattern: 3 | console: "%d{HH:mm:ss.SSS} [%thread] %-5level- %msg%n" -------------------------------------------------------------------------------- /part1/chapter3/spring-reactive-branchoffice/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.2' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.itvillage' 8 | version = '0.0.1-SNAPSHOT' 9 | 10 | configurations { 11 | compileOnly { 12 | extendsFrom annotationProcessor 13 | } 14 | } 15 | 16 | repositories { 17 | mavenCentral() 18 | } 19 | 20 | dependencies { 21 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 22 | compileOnly 'org.projectlombok:lombok' 23 | annotationProcessor 'org.projectlombok:lombok' 24 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 25 | testImplementation 'io.projectreactor:reactor-test' 26 | } 27 | 28 | test { 29 | useJUnitPlatform() 30 | } 31 | -------------------------------------------------------------------------------- /part1/chapter3/spring-reactive-branchoffice/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part1/chapter3/spring-reactive-branchoffice/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part1/chapter3/spring-reactive-branchoffice/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part1/chapter3/spring-reactive-branchoffice/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'spring-reactive-branchoffice' 2 | 3 | -------------------------------------------------------------------------------- /part1/chapter3/spring-reactive-branchoffice/src/main/java/com/itvillage/domain/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data 7 | @AllArgsConstructor 8 | public class Book { 9 | private long bookId; 10 | private String name; 11 | private int price; 12 | } 13 | -------------------------------------------------------------------------------- /part1/chapter3/spring-reactive-branchoffice/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 5050 3 | 4 | logging: 5 | pattern: 6 | console: "%d{HH:mm:ss.SSS} [%thread] %-5level- %msg%n" -------------------------------------------------------------------------------- /part1/chapter3/spring-reactive-headoffice/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.2' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.itvillage' 8 | version = '0.0.1-SNAPSHOT' 9 | 10 | configurations { 11 | compileOnly { 12 | extendsFrom annotationProcessor 13 | } 14 | } 15 | 16 | repositories { 17 | mavenCentral() 18 | } 19 | 20 | dependencies { 21 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 22 | compileOnly 'org.projectlombok:lombok' 23 | annotationProcessor 'org.projectlombok:lombok' 24 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 25 | testImplementation 'io.projectreactor:reactor-test' 26 | } 27 | 28 | test { 29 | useJUnitPlatform() 30 | } 31 | -------------------------------------------------------------------------------- /part1/chapter3/spring-reactive-headoffice/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part1/chapter3/spring-reactive-headoffice/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part1/chapter3/spring-reactive-headoffice/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part1/chapter3/spring-reactive-headoffice/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'spring-reactive-headoffice' 2 | 3 | -------------------------------------------------------------------------------- /part1/chapter3/spring-reactive-headoffice/src/main/java/com/itvillage/domain/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.domain; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class Book { 7 | private String name; 8 | private int price; 9 | } 10 | -------------------------------------------------------------------------------- /part1/chapter3/spring-reactive-headoffice/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 6060 3 | 4 | logging: 5 | pattern: 6 | console: "%d{HH:mm:ss.SSS} [%thread] %-5level- %msg%n" -------------------------------------------------------------------------------- /part1/chapter4/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'com.itvillage' 6 | version '1.0-SNAPSHOT' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | implementation 'org.apache.commons:commons-lang3:3.12.0' 14 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 15 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 16 | } 17 | 18 | test { 19 | useJUnitPlatform() 20 | } -------------------------------------------------------------------------------- /part1/chapter4/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part1/chapter4/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part1/chapter4/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part1/chapter4/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'chapter4' 2 | 3 | -------------------------------------------------------------------------------- /part1/chapter4/src/main/java/com/itvillage/CryptoCurrency.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | public class CryptoCurrency { 4 | private String name; 5 | private CurrencyUnit unit; 6 | private int price; 7 | 8 | public CryptoCurrency(String name, CurrencyUnit unit) { 9 | this.name = name; 10 | this.unit = unit; 11 | } 12 | 13 | public CryptoCurrency(String name, CurrencyUnit unit, int price) { 14 | this.name = name; 15 | this.unit = unit; 16 | this.price = price; 17 | } 18 | 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public CurrencyUnit getUnit() { 24 | return unit; 25 | } 26 | 27 | public int getPrice() { 28 | return price; 29 | } 30 | 31 | public enum CurrencyUnit { 32 | BTC, 33 | ETH, 34 | DOT, 35 | ADA, 36 | SOL 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /part1/chapter4/src/main/java/com/itvillage/Example4_1.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import java.util.Collections; 4 | import java.util.Comparator; 5 | import java.util.List; 6 | 7 | /** 8 | * 기존에 사용되던 단 하나의 추상 메서드를 가지는 인터페이스 예제 9 | */ 10 | public class Example4_1 { 11 | public static void main(String[] args) { 12 | List cryptoCurrencies = SampleData.cryptoCurrencies; 13 | 14 | Collections.sort(cryptoCurrencies, new Comparator() { 15 | @Override 16 | public int compare(CryptoCurrency cc1, CryptoCurrency cc2) { 17 | return cc1.getUnit().name().compareTo(cc2.getUnit().name()); 18 | } 19 | }); 20 | 21 | for(CryptoCurrency cryptoCurrency : cryptoCurrencies) 22 | System.out.println("암호 화폐명: " + cryptoCurrency.getName() + 23 | ", 가격: " + cryptoCurrency.getUnit()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /part1/chapter4/src/main/java/com/itvillage/Example4_11.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.function.Predicate; 6 | 7 | /** 8 | * Predicate 사용 예제 9 | */ 10 | public class Example4_11 { 11 | public static void main(String[] args) { 12 | List cryptoCurrencies = SampleData.cryptoCurrencies; 13 | List result = filter(cryptoCurrencies, cc -> cc.getPrice() > 500_000); 14 | 15 | for (CryptoCurrency cc : result) { 16 | System.out.println(cc.getName()); 17 | } 18 | } 19 | 20 | private static List filter(List cryptoCurrencies, 21 | Predicate p){ 22 | List result = new ArrayList<>(); 23 | for (CryptoCurrency cc : cryptoCurrencies) { 24 | if (p.test(cc)) { 25 | result.add(cc); 26 | } 27 | } 28 | return result; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /part1/chapter4/src/main/java/com/itvillage/Example4_4.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | 6 | /** 7 | * Comparator 함수형 인터페이스의 람다 표현 예제 8 | */ 9 | public class Example4_4 { 10 | public static void main(String[] args) { 11 | List cryptoCurrencies = SampleData.cryptoCurrencies; 12 | 13 | Collections.sort(cryptoCurrencies, 14 | (cc1, cc2) -> cc1.getUnit().name().compareTo(cc2.getUnit().name())); 15 | 16 | for(CryptoCurrency cryptoCurrency : cryptoCurrencies) 17 | System.out.println("암호 화폐명: " + cryptoCurrency.getName() + 18 | ", 가격: " + cryptoCurrency.getUnit()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /part1/chapter4/src/main/java/com/itvillage/Example4_5.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import java.util.List; 4 | 5 | import static com.itvillage.CryptoCurrency.CurrencyUnit; 6 | 7 | /** 8 | * 람다 캡처링 예제 9 | */ 10 | public class Example4_5 { 11 | public static void main(String[] args) { 12 | List cryptoCurrencies = SampleData.cryptoCurrencies; 13 | 14 | String korBTC = "비트코인"; 15 | // korBTC = "빗코인"; 16 | cryptoCurrencies.stream() 17 | .filter(cc -> cc.getUnit() == CurrencyUnit.BTC) 18 | .map(cc -> cc.getName() + "(" + korBTC + ")" ) 19 | .forEach(cc -> System.out.println(cc)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part1/chapter4/src/main/java/com/itvillage/Example4_6.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import java.util.List; 5 | 6 | /** 7 | * ClassName :: static method 형태의 메서드 레퍼런스 예제 8 | */ 9 | public class Example4_6 { 10 | public static void main(String[] args) { 11 | List cryptoCurrencies = SampleData.cryptoCurrencies; 12 | 13 | cryptoCurrencies.stream() 14 | .map(cc -> cc.getName()) 15 | // .map(name -> StringUtils.upperCase(name)) 16 | .map(StringUtils::upperCase) 17 | .forEach(name -> System.out.println(name)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part1/chapter4/src/main/java/com/itvillage/Example4_7.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * ClassName :: instance method 형태의 메서드 레퍼런스 예제 7 | */ 8 | public class Example4_7 { 9 | public static void main(String[] args) { 10 | List cryptoCurrencies = SampleData.cryptoCurrencies; 11 | 12 | cryptoCurrencies.stream() 13 | .map(cc -> cc.getName()) 14 | // .map(name -> name.toUpperCase()) 15 | .map(String::toUpperCase) 16 | .forEach(name -> System.out.println(name)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /part1/chapter4/src/main/java/com/itvillage/Example4_8.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.apache.commons.lang3.tuple.ImmutablePair; 4 | 5 | import java.util.List; 6 | 7 | import static com.itvillage.CryptoCurrency.CurrencyUnit; 8 | 9 | /** 10 | * object :: instance method 형태의 메서드 레퍼런스 예제 11 | */ 12 | public class Example4_8 { 13 | public static void main(String[] args) { 14 | List cryptoCurrencies = SampleData.cryptoCurrencies; 15 | int btcPrice = cryptoCurrencies.stream() 16 | .filter(cc -> cc.getUnit() == CurrencyUnit.BTC) 17 | .findFirst() 18 | .get() 19 | .getPrice(); 20 | 21 | int amount = 2; 22 | 23 | PaymentCalculator calculator = new PaymentCalculator(); 24 | cryptoCurrencies.stream() 25 | .filter(cc -> cc.getUnit() == CurrencyUnit.BTC) 26 | .map(cc -> new ImmutablePair(cc.getPrice(), amount)) 27 | // .map(pair -> calculator.getTotalPayment(pair)) 28 | .map(calculator::getTotalPayment) 29 | .forEach(System.out::println); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /part1/chapter4/src/main/java/com/itvillage/Example4_9.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.apache.commons.lang3.tuple.ImmutablePair; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | import static com.itvillage.CryptoCurrency.CurrencyUnit; 9 | 10 | /** 11 | * ClassName :: new 형태의 메서드 레퍼런스 예제 12 | */ 13 | public class Example4_9 { 14 | public static void main(String[] args) { 15 | List cryptoCurrencies = SampleData.cryptoCurrencies; 16 | 17 | int amount = 2; 18 | 19 | Optional optional = 20 | cryptoCurrencies.stream() 21 | .filter(cc -> cc.getUnit() == CurrencyUnit.BTC) 22 | .map(cc -> new ImmutablePair(cc.getPrice(), amount)) 23 | // .map(pair -> new PaymentCalculator(pair)) 24 | .map(PaymentCalculator::new) 25 | .findFirst(); 26 | 27 | System.out.println(optional.get().getTotalPayment()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /part1/chapter4/src/main/java/com/itvillage/PaymentCalculator.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.apache.commons.lang3.tuple.Pair; 4 | 5 | public class PaymentCalculator { 6 | private Pair pair; 7 | 8 | public PaymentCalculator(){} 9 | 10 | public PaymentCalculator(Pair pair) { 11 | this.pair = pair; 12 | } 13 | 14 | public int getTotalPayment() { 15 | return pair.getLeft() * pair.getRight(); 16 | } 17 | 18 | public int getTotalPayment(Pair pair) { 19 | return pair.getLeft() * pair.getRight(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part1/chapter4/src/main/java/com/itvillage/SampleData.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | public class SampleData { 7 | public final static List cryptoCurrencies = Arrays.asList( 8 | new CryptoCurrency("Bitcoin", 9 | CryptoCurrency.CurrencyUnit.BTC, 40_000_000), 10 | new CryptoCurrency("Ethereum", 11 | CryptoCurrency.CurrencyUnit.ETH, 2_000_000), 12 | new CryptoCurrency("Polkadot", 13 | CryptoCurrency.CurrencyUnit.DOT, 10_000), 14 | new CryptoCurrency("Cardano", 15 | CryptoCurrency.CurrencyUnit.ADA, 1500), 16 | new CryptoCurrency("Solana", 17 | CryptoCurrency.CurrencyUnit.SOL, 150_000) 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /part1/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part1/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part1/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'part1' 2 | 3 | -------------------------------------------------------------------------------- /part2/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.2' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.itvillage' 8 | version = '0.0.1-SNAPSHOT' 9 | 10 | configurations { 11 | compileOnly { 12 | extendsFrom annotationProcessor 13 | } 14 | } 15 | 16 | repositories { 17 | mavenCentral() 18 | } 19 | 20 | dependencies { 21 | implementation 'com.jayway.jsonpath:json-path:2.7.0' 22 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 23 | compileOnly 'org.projectlombok:lombok' 24 | annotationProcessor 'org.projectlombok:lombok' 25 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 26 | testImplementation 'io.projectreactor:reactor-test' 27 | implementation 'io.projectreactor.addons:reactor-extra:3.4.8' 28 | } 29 | 30 | test { 31 | useJUnitPlatform() 32 | } -------------------------------------------------------------------------------- /part2/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part2/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part2/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part2/hs_err_pid20044.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part2/hs_err_pid20044.log -------------------------------------------------------------------------------- /part2/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'part2' 2 | 3 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter10/Example10_1.java: -------------------------------------------------------------------------------- 1 | package chapter10; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | /** 8 | * subscribeOn() 기본 예제 9 | * - 구독 시점에 Publisher의 실행을 위한 쓰레드를 지정한다 10 | */ 11 | @Slf4j 12 | public class Example10_1 { 13 | public static void main(String[] args) throws InterruptedException { 14 | Flux.fromArray(new Integer[] {1, 3, 5, 7}) 15 | .subscribeOn(Schedulers.boundedElastic()) 16 | .doOnNext(data -> log.info("# doOnNext: {}", data)) 17 | .doOnSubscribe(subscription -> log.info("# doOnSubscribe")) 18 | .subscribe(data -> log.info("# onNext: {}", data)); 19 | 20 | Thread.sleep(500L); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter10/Example10_10.java: -------------------------------------------------------------------------------- 1 | package chapter10; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | 8 | /** 9 | * Schedulers.single() 예 10 | * - Scheduler가 제거될 때까지 동일한 쓰레드를 재사용한다. 11 | * 12 | */ 13 | @Slf4j 14 | public class Example10_10 { 15 | public static void main(String[] args) throws InterruptedException { 16 | doTask("task1") 17 | .subscribe(data -> log.info("# onNext: {}", data)); 18 | 19 | doTask("task2") 20 | .subscribe(data -> log.info("# onNext: {}", data)); 21 | 22 | Thread.sleep(200L); 23 | } 24 | 25 | private static Flux doTask(String taskName) { 26 | return Flux.fromArray(new Integer[] {1, 3, 5, 7}) 27 | .publishOn(Schedulers.single()) 28 | .filter(data -> data > 3) 29 | .doOnNext(data -> log.info("# {} doOnNext filter: {}", taskName, data)) 30 | .map(data -> data * 10) 31 | .doOnNext(data -> log.info("# {} doOnNext map: {}", taskName, data)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter10/Example10_11.java: -------------------------------------------------------------------------------- 1 | package chapter10; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | 8 | /** 9 | * Schedulers.newSingle() 예 10 | * - 호출할 때 마다 매번 하나의 쓰레드를 새로 생성한다. 11 | * 12 | */ 13 | @Slf4j 14 | public class Example10_11 { 15 | public static void main(String[] args) throws InterruptedException { 16 | doTask("task1") 17 | .subscribe(data -> log.info("# onNext: {}", data)); 18 | 19 | doTask("task2") 20 | .subscribe(data -> log.info("# onNext: {}", data)); 21 | 22 | Thread.sleep(200L); 23 | } 24 | 25 | private static Flux doTask(String taskName) { 26 | return Flux.fromArray(new Integer[] {1, 3, 5, 7}) 27 | .publishOn(Schedulers.newSingle("new-single", true)) 28 | .filter(data -> data > 3) 29 | .doOnNext(data -> log.info("# {} doOnNext filter: {}", taskName, data)) 30 | .map(data -> data * 10) 31 | .doOnNext(data -> log.info("# {} doOnNext map: {}", taskName, data)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter10/Example10_2.java: -------------------------------------------------------------------------------- 1 | package chapter10; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | /** 8 | * publishOn() 기본 예제 9 | * - Operator 체인에서 Downstream Operator의 실행을 위한 쓰레드를 지정한다. 10 | */ 11 | @Slf4j 12 | public class Example10_2 { 13 | public static void main(String[] args) throws InterruptedException { 14 | Flux.fromArray(new Integer[] {1, 3, 5, 7}) 15 | .doOnNext(data -> log.info("# doOnNext: {}", data)) 16 | .doOnSubscribe(subscription -> log.info("# doOnSubscribe")) 17 | .publishOn(Schedulers.parallel()) 18 | .subscribe(data -> log.info("# onNext: {}", data)); 19 | 20 | Thread.sleep(500L); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter10/Example10_3.java: -------------------------------------------------------------------------------- 1 | package chapter10; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | /** 8 | * parallel() 기본 사용 예제 9 | * - parallel()만 사용할 경우에는 병렬로 작업을 수행하지 않는다. 10 | * - runOn()을 사용해서 Scheduler를 할당해주어야 병렬로 작업을 수행한다. 11 | * - **** CPU 코어 갯수내에서 worker thread를 할당한다. **** 12 | */ 13 | @Slf4j 14 | public class Example10_3 { 15 | public static void main(String[] args) throws InterruptedException { 16 | Flux.fromArray(new Integer[]{1, 3, 5, 7, 9, 11, 13, 15, 17, 19}) 17 | .parallel(4) 18 | .runOn(Schedulers.parallel()) 19 | .subscribe(data -> log.info("# onNext: {}", data)); 20 | 21 | Thread.sleep(100L); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter10/Example10_4.java: -------------------------------------------------------------------------------- 1 | package chapter10; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | /** 8 | * parallel() 기본 사용 예제 9 | * - parallel()만 사용할 경우에는 병렬로 작업을 수행하지 않는다. 10 | * - runOn()을 사용해서 Scheduler를 할당해주어야 병렬로 작업을 수행한다. 11 | * - **** CPU 코어 갯수내에서 worker thread를 할당한다. **** 12 | */ 13 | @Slf4j 14 | public class Example10_4 { 15 | public static void main(String[] args) throws InterruptedException { 16 | Flux.fromArray(new Integer[]{1, 3, 5, 7, 9, 11, 13, 15, 17, 19}) 17 | .parallel(4) 18 | .runOn(Schedulers.parallel()) 19 | .subscribe(data -> log.info("# onNext: {}", data)); 20 | 21 | Thread.sleep(100L); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter10/Example10_5.java: -------------------------------------------------------------------------------- 1 | package chapter10; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | 7 | /** 8 | * subscribeOn()과 publishOn()의 동작 과정 예 9 | * - subscribeOn()과 publishOn()을 사용하지 않은 경우 10 | * - Sequence의 Operator 체인에서 최초의 쓰레드는 subscribe()가 11 | * 호출되는 scope에 있는 쓰레드이다. 12 | */ 13 | @Slf4j 14 | public class Example10_5 { 15 | public static void main(String[] args) { 16 | Flux 17 | .fromArray(new Integer[] {1, 3, 5, 7}) 18 | .doOnNext(data -> log.info("# doOnNext fromArray: {}", data)) 19 | .filter(data -> data > 3) 20 | .doOnNext(data -> log.info("# doOnNext filter: {}", data)) 21 | .map(data -> data * 10) 22 | .doOnNext(data -> log.info("# doOnNext map: {}", data)) 23 | .subscribe(data -> log.info("# onNext: {}", data)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter10/Example10_6.java: -------------------------------------------------------------------------------- 1 | package chapter10; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | 8 | /** 9 | * subscribeOn()과 publishOn()의 동작 과정 예 10 | * - 하나의 publishOn()만 사용한 경우 11 | * - publishOn() 아래 쪽 Operator들의 실행 쓰레드를 변경한다. 12 | * 13 | */ 14 | @Slf4j 15 | public class Example10_6 { 16 | public static void main(String[] args) throws InterruptedException { 17 | Flux 18 | .fromArray(new Integer[] {1, 3, 5, 7}) 19 | .doOnNext(data -> log.info("# doOnNext fromArray: {}", data)) 20 | .publishOn(Schedulers.parallel()) 21 | .filter(data -> data > 3) 22 | .doOnNext(data -> log.info("# doOnNext filter: {}", data)) 23 | .map(data -> data * 10) 24 | .doOnNext(data -> log.info("# doOnNext map: {}", data)) 25 | .subscribe(data -> log.info("# onNext: {}", data)); 26 | 27 | Thread.sleep(500L); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter10/Example10_7.java: -------------------------------------------------------------------------------- 1 | package chapter10; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | 8 | /** 9 | * subscribeOn()과 publishOn()의 동작 과정 예 10 | * - 두 개의 publishOn()을 사용한 경우 11 | * - 다음 publishOn()을 만나기 전까지 publishOn() 아래 쪽 Operator들의 실행 쓰레드를 변경한다. 12 | * 13 | */ 14 | @Slf4j 15 | public class Example10_7 { 16 | public static void main(String[] args) throws InterruptedException { 17 | Flux 18 | .fromArray(new Integer[] {1, 3, 5, 7}) 19 | .doOnNext(data -> log.info("# doOnNext fromArray: {}", data)) 20 | .publishOn(Schedulers.parallel()) 21 | .filter(data -> data > 3) 22 | .doOnNext(data -> log.info("# doOnNext filter: {}", data)) 23 | .publishOn(Schedulers.parallel()) 24 | .map(data -> data * 10) 25 | .doOnNext(data -> log.info("# doOnNext map: {}", data)) 26 | .subscribe(data -> log.info("# onNext: {}", data)); 27 | 28 | Thread.sleep(500L); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter10/Example10_8.java: -------------------------------------------------------------------------------- 1 | package chapter10; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | 8 | /** 9 | * subscribeOn()과 publishOn()의 동작 과정 예 10 | * - subscribeOn()과 publishOn()을 함께 사용한 경우 11 | * - subscribeOn()은 구독 직후에 실행될 쓰레드를 지정하고, publishOn()을 만나기 전까지 쓰레드를 변경하지 않는다. 12 | * 13 | */ 14 | @Slf4j 15 | public class Example10_8 { 16 | public static void main(String[] args) throws InterruptedException { 17 | Flux 18 | .fromArray(new Integer[] {1, 3, 5, 7}) 19 | .subscribeOn(Schedulers.boundedElastic()) 20 | .doOnNext(data -> log.info("# doOnNext fromArray: {}", data)) 21 | .filter(data -> data > 3) 22 | .doOnNext(data -> log.info("# doOnNext filter: {}", data)) 23 | .publishOn(Schedulers.parallel()) 24 | .map(data -> data * 10) 25 | .doOnNext(data -> log.info("# doOnNext map: {}", data)) 26 | .subscribe(data -> log.info("# onNext: {}", data)); 27 | 28 | Thread.sleep(500L); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter10/Example10_9.java: -------------------------------------------------------------------------------- 1 | package chapter10; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | 8 | /** 9 | * Schedulers.immediate() 예 10 | * - 별도의 쓰레드를 할당하지 않고, 현재 쓰레드에서 실행된다. 11 | * 12 | */ 13 | @Slf4j 14 | public class Example10_9 { 15 | public static void main(String[] args) throws InterruptedException { 16 | Flux 17 | .fromArray(new Integer[] {1, 3, 5, 7}) 18 | .publishOn(Schedulers.parallel()) 19 | .filter(data -> data > 3) 20 | .doOnNext(data -> log.info("# doOnNext filter: {}", data)) 21 | .publishOn(Schedulers.immediate()) 22 | .map(data -> data * 10) 23 | .doOnNext(data -> log.info("# doOnNext map: {}", data)) 24 | .subscribe(data -> log.info("# onNext: {}", data)); 25 | 26 | Thread.sleep(200L); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter11/Example11_3.java: -------------------------------------------------------------------------------- 1 | package chapter11; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Mono; 5 | import reactor.core.scheduler.Schedulers; 6 | import reactor.util.context.Context; 7 | 8 | /** 9 | * Context API 사용 예제 10 | */ 11 | @Slf4j 12 | public class Example11_3 { 13 | public static void main(String[] args) throws InterruptedException { 14 | final String key1 = "company"; 15 | final String key2 = "firstName"; 16 | final String key3 = "lastName"; 17 | 18 | Mono 19 | .deferContextual(ctx -> 20 | Mono.just(ctx.get(key1) + ", " + ctx.get(key2) + " " + ctx.get(key3)) 21 | ) 22 | .publishOn(Schedulers.parallel()) 23 | .contextWrite(context -> 24 | context.putAll(Context.of(key2, "Steve", key3, "Jobs").readOnly()) 25 | ) 26 | .contextWrite(context -> context.put(key1, "Apple")) 27 | .subscribe(data -> log.info("# onNext: {}" , data)); 28 | 29 | Thread.sleep(100L); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter11/Example11_4.java: -------------------------------------------------------------------------------- 1 | package chapter11; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Mono; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | /** 8 | * ContextView API 사용 예제 9 | */ 10 | @Slf4j 11 | public class Example11_4 { 12 | public static void main(String[] args) throws InterruptedException { 13 | final String key1 = "company"; 14 | final String key2 = "firstName"; 15 | final String key3 = "lastName"; 16 | 17 | Mono 18 | .deferContextual(ctx -> 19 | Mono.just(ctx.get(key1) + ", " + 20 | ctx.getOrEmpty(key2).orElse("no firstName") + " " + 21 | ctx.getOrDefault(key3, "no lastName")) 22 | ) 23 | .publishOn(Schedulers.parallel()) 24 | .contextWrite(context -> context.put(key1, "Apple")) 25 | .subscribe(data -> log.info("# onNext: {}" , data)); 26 | 27 | Thread.sleep(100L); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter11/Example11_5.java: -------------------------------------------------------------------------------- 1 | package chapter11; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Mono; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | /** 8 | * Context의 특징 예제 9 | * - Context는 각각의 구독을 통해 Reactor Sequence에 연결 되며 체인의 각 Operator는 연결된 Context에 접근할 수 있어야 한다. 10 | */ 11 | @Slf4j 12 | public class Example11_5 { 13 | public static void main(String[] args) throws InterruptedException { 14 | final String key1 = "company"; 15 | 16 | Mono mono = Mono.deferContextual(ctx -> 17 | Mono.just("Company: " + " " + ctx.get(key1)) 18 | ) 19 | .publishOn(Schedulers.parallel()); 20 | 21 | 22 | mono.contextWrite(context -> context.put(key1, "Apple")) 23 | .subscribe(data -> log.info("# subscribe1 onNext: {}", data)); 24 | 25 | mono.contextWrite(context -> context.put(key1, "Microsoft")) 26 | .subscribe(data -> log.info("# subscribe2 onNext: {}", data)); 27 | 28 | Thread.sleep(100L); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter11/Example11_6.java: -------------------------------------------------------------------------------- 1 | package chapter11; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Mono; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | /** 8 | * Context의 특징 예제 9 | * - Context는 Operator 체인의 아래에서부터 위로 전파된다. 10 | * - 따라서 Operator 체인 상에서 Context read 메서드가 Context write 메서드 밑에 있을 경우에는 write된 값을 read할 수 없다. 11 | */ 12 | @Slf4j 13 | public class Example11_6 { 14 | public static void main(String[] args) throws InterruptedException { 15 | String key1 = "company"; 16 | String key2 = "name"; 17 | 18 | Mono 19 | .deferContextual(ctx -> 20 | Mono.just(ctx.get(key1)) 21 | ) 22 | .publishOn(Schedulers.parallel()) 23 | .contextWrite(context -> context.put(key2, "Bill")) 24 | .transformDeferredContextual((mono, ctx) -> 25 | mono.map(data -> data + ", " + ctx.getOrDefault(key2, "Steve")) 26 | ) 27 | .contextWrite(context -> context.put(key1, "Apple")) 28 | .subscribe(data -> log.info("# onNext: {}", data)); 29 | 30 | Thread.sleep(100L); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter12/Example12_2.java: -------------------------------------------------------------------------------- 1 | package chapter12; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | /** 7 | * checkpoint()를 사용한 디버깅 예 8 | * - checkpoint()를 지정한 Operator 체인에서만 동작한다. 9 | */ 10 | @Slf4j 11 | public class Example12_2 { 12 | public static void main(String[] args) { 13 | Flux 14 | .just(2, 4, 6, 8) 15 | .zipWith(Flux.just(1, 2, 3, 0), (x, y) -> x/y) 16 | .map(num -> num + 2) 17 | .checkpoint() 18 | .subscribe( 19 | data -> log.info("# onNext: {}", data), 20 | error -> log.error("# onError:", error) 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter12/Example12_3.java: -------------------------------------------------------------------------------- 1 | package chapter12; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | /** 7 | * checkpoint()를 사용한 디버깅 예 8 | * - checkpoint()를 지정한 Operator 체인에서만 동작한다. 9 | */ 10 | @Slf4j 11 | public class Example12_3 { 12 | public static void main(String[] args) { 13 | Flux 14 | .just(2, 4, 6, 8) 15 | .zipWith(Flux.just(1, 2, 3, 0), (x, y) -> x/y) 16 | .checkpoint() 17 | .map(num -> num + 2) 18 | .checkpoint() 19 | .subscribe( 20 | data -> log.info("# onNext: {}", data), 21 | error -> log.error("# onError:", error) 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter12/Example12_4.java: -------------------------------------------------------------------------------- 1 | package chapter12; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | /** 7 | * checkpoint(description)을 사용한 디버깅 예 8 | * - description 을 추가해서 에러가 발생한 지점을 구분할 수 있다. 9 | * - description 을 지정할 경우 traceback 을 추가하지 않는다. 10 | */ 11 | @Slf4j 12 | public class Example12_4 { 13 | public static void main(String[] args) { 14 | Flux 15 | .just(2, 4, 6, 8) 16 | .zipWith(Flux.just(1, 2, 3, 0), (x, y) -> x/y) 17 | .checkpoint("Example12_4.zipWith.checkpoint") 18 | .map(num -> num + 2) 19 | .checkpoint("Example12_4.map.checkpoint") 20 | .subscribe( 21 | data -> log.info("# onNext: {}", data), 22 | error -> log.error("# onError:", error) 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter12/Example12_5.java: -------------------------------------------------------------------------------- 1 | package chapter12; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | /** 7 | * checkpoint(description)을 사용한 디버깅 예 8 | * - description 을 추가해서 에러가 발생한 지점을 구분할 수 있다. 9 | * - forceStackTrace 을 true로 지정할 경우 traceback도 추가한다. 10 | */ 11 | @Slf4j 12 | public class Example12_5 { 13 | public static void main(String[] args) { 14 | Flux 15 | .just(2, 4, 6, 8) 16 | .zipWith(Flux.just(1, 2, 3, 0), (x, y) -> x/y) 17 | .checkpoint("Example12_4.zipWith.checkpoint", true) 18 | .map(num -> num + 2) 19 | .checkpoint("Example12_4.map.checkpoint", true) 20 | .subscribe( 21 | data -> log.info("# onNext: {}", data), 22 | error -> log.error("# onError:", error) 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter12/Example12_6.java: -------------------------------------------------------------------------------- 1 | package chapter12; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | /** 7 | * 복잡한 단계를 거치는 Operator 체인에서 checkpoint()를 사용하는 예제 8 | */ 9 | @Slf4j 10 | public class Example12_6 { 11 | public static void main(String[] args) { 12 | Flux source = Flux.just(2, 4, 6, 8); 13 | Flux other = Flux.just(1, 2, 3, 0); 14 | 15 | Flux multiplySource = divide(source, other).checkpoint(); 16 | Flux plusSource = plus(multiplySource).checkpoint(); 17 | 18 | 19 | plusSource.subscribe( 20 | data -> log.info("# onNext: {}", data), 21 | error -> log.error("# onError:", error) 22 | ); 23 | } 24 | 25 | private static Flux divide(Flux source, Flux other) { 26 | return source.zipWith(other, (x, y) -> x/y); 27 | } 28 | 29 | private static Flux plus(Flux source) { 30 | return source.map(num -> num + 2); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter12/Example12_7.java: -------------------------------------------------------------------------------- 1 | package chapter12; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * log() operator를 사용한 예제 11 | */ 12 | @Slf4j 13 | public class Example12_7 { 14 | public static Map fruits = new HashMap<>(); 15 | 16 | static { 17 | fruits.put("banana", "바나나"); 18 | fruits.put("apple", "사과"); 19 | fruits.put("pear", "배"); 20 | fruits.put("grape", "포도"); 21 | } 22 | 23 | public static void main(String[] args) { 24 | Flux.fromArray(new String[]{"BANANAS", "APPLES", "PEARS", "MELONS"}) 25 | .map(String::toLowerCase) 26 | .map(fruit -> fruit.substring(0, fruit.length() - 1)) 27 | .log() 28 | // .log("Fruit.Substring", Level.FINE) 29 | .map(fruits::get) 30 | .subscribe( 31 | log::info, 32 | error -> log.error("# onError:", error)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter13/BackpressureTestExample.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import reactor.core.publisher.Flux; 4 | import reactor.core.publisher.FluxSink; 5 | 6 | public class BackpressureTestExample { 7 | public static Flux generateNumber() { 8 | return Flux 9 | .create(emitter -> { 10 | for (int i = 1; i <= 100; i++) { 11 | emitter.next(i); 12 | } 13 | emitter.complete(); 14 | }, FluxSink.OverflowStrategy.ERROR); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter13/ContextTestExample.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import org.springframework.util.Base64Utils; 4 | import reactor.core.publisher.Mono; 5 | 6 | public class ContextTestExample { 7 | public static Mono getSecretMessage(Mono keySource) { 8 | return keySource 9 | .zipWith(Mono.deferContextual(ctx -> 10 | Mono.just((String)ctx.get("secretKey")))) 11 | .filter(tp -> 12 | tp.getT1().equals( 13 | new String(Base64Utils.decodeFromString(tp.getT2()))) 14 | ) 15 | .transformDeferredContextual( 16 | (mono, ctx) -> mono.map(notUse -> ctx.get("secretMessage")) 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter13/GeneralTestExample.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import reactor.core.publisher.Flux; 4 | 5 | public class GeneralTestExample { 6 | public static Flux sayHello() { 7 | return Flux 8 | .just("Hello", "Reactor"); 9 | } 10 | 11 | public static Flux divideByTwo(Flux source) { 12 | return source 13 | .zipWith(Flux.just(2, 2, 2, 2, 0), (x, y) -> x/y); 14 | } 15 | 16 | public static Flux takeNumber(Flux source, long n) { 17 | return source 18 | .take(n); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter13/PublisherProbeTestExample.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | public class PublisherProbeTestExample { 6 | public static Mono processTask(Mono main, Mono standby) { 7 | return main 8 | .flatMap(massage -> Mono.just(massage)) 9 | .switchIfEmpty(standby); 10 | } 11 | 12 | public static Mono supplyMainPower() { 13 | return Mono.empty(); 14 | } 15 | 16 | public static Mono supplyStandbyPower() { 17 | return Mono.just("# supply Standby Power"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter13/RecordTestExample.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import reactor.core.publisher.Flux; 4 | 5 | public class RecordTestExample { 6 | public static Flux getCapitalizedCountry(Flux source) { 7 | return source 8 | .map(country -> country.substring(0, 1).toUpperCase() + 9 | country.substring(1)); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/Book.java: -------------------------------------------------------------------------------- 1 | package chapter14; 2 | 3 | 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | @Getter 8 | @AllArgsConstructor 9 | public class Book { 10 | private String bookName; 11 | private String authorName; 12 | private String penName; 13 | private int price; 14 | private int stockQuantity; 15 | } 16 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/CryptoCurrencyPriceEmitter.java: -------------------------------------------------------------------------------- 1 | package chapter14; 2 | 3 | public class CryptoCurrencyPriceEmitter { 4 | private CryptoCurrencyPriceListener listener; 5 | 6 | public void setListener(CryptoCurrencyPriceListener listener) { 7 | this.listener = listener; 8 | } 9 | 10 | public void flowInto() { 11 | listener.onPrice(SampleData.btcPrices); 12 | } 13 | 14 | public void complete() { 15 | listener.onComplete(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/CryptoCurrencyPriceListener.java: -------------------------------------------------------------------------------- 1 | package chapter14; 2 | 3 | import java.util.List; 4 | 5 | public interface CryptoCurrencyPriceListener { 6 | void onPrice(List priceList); 7 | void onComplete(); 8 | } 9 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_1_create/Example14_1.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_1_create; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Mono; 5 | 6 | @Slf4j 7 | public class Example14_1 { 8 | public static void main(String[] args) { 9 | Mono 10 | .justOrEmpty(null) 11 | .subscribe(data -> {}, 12 | error -> {}, 13 | () -> log.info("# onComplete")); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_1_create/Example14_10.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_1_create; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.util.function.Tuples; 6 | 7 | /** 8 | * generate 예제 9 | */ 10 | @Slf4j 11 | public class Example14_10 { 12 | public static void main(String[] args) { 13 | final int dan = 3; 14 | Flux 15 | .generate(() -> Tuples.of(dan, 1), (state, sink) -> { 16 | sink.next(state.getT1() + " * " + 17 | state.getT2() + " = " + state.getT1() * state.getT2()); 18 | if (state.getT2() == 9) 19 | sink.complete(); 20 | return Tuples.of(state.getT1(), state.getT2() + 1); 21 | }, state -> log.info("# 구구단 {}단 종료!", state.getT1())) 22 | .subscribe(data -> log.info("# onNext: {}", data)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_1_create/Example14_11.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_1_create; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | import reactor.util.function.Tuple2; 7 | 8 | import java.util.Map; 9 | 10 | /** 11 | * generate 예제 12 | */ 13 | @Slf4j 14 | public class Example14_11 { 15 | public static void main(String[] args) { 16 | Map> map = 17 | SampleData.getBtcTopPricesPerYearMap(); 18 | Flux 19 | .generate(() -> 2019, (state, sink) -> { 20 | if (state > 2021) { 21 | sink.complete(); 22 | } else { 23 | sink.next(map.get(state)); 24 | } 25 | 26 | return ++state; 27 | }) 28 | .subscribe(data -> log.info("# onNext: {}", data)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_1_create/Example14_2.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_1_create; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | 7 | /** 8 | * fromIterable 예제 9 | */ 10 | @Slf4j 11 | public class Example14_2 { 12 | public static void main(String[] args) { 13 | Flux 14 | .fromIterable(SampleData.coins) 15 | .subscribe(coin -> 16 | log.info("coin 명: {}, 현재가: {}", coin.getT1(), coin.getT2()) 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_1_create/Example14_3.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_1_create; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | 7 | /** 8 | * fromStream 예제 9 | */ 10 | @Slf4j 11 | public class Example14_3 { 12 | public static void main(String[] args) { 13 | Flux 14 | .fromStream(() -> SampleData.coinNames.stream()) 15 | .filter(coin -> coin.equals("BTC") || coin.equals("ETH")) 16 | .subscribe(data -> log.info("{}", data)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_1_create/Example14_4.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_1_create; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | /** 7 | * range 예제 8 | */ 9 | @Slf4j 10 | public class Example14_4 { 11 | public static void main(String[] args) { 12 | Flux 13 | .range(5, 10) 14 | .subscribe(data -> log.info("{}", data)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_1_create/Example14_5.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_1_create; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | 7 | /** 8 | * range 예제 9 | */ 10 | @Slf4j 11 | public class Example14_5 { 12 | public static void main(String[] args) { 13 | Flux 14 | .range(7, 5) 15 | .map(idx -> SampleData.btcTopPricesPerYear.get(idx)) 16 | .subscribe(tuple -> log.info("{}'s {}", tuple.getT1(), tuple.getT2())); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_1_create/Example14_6.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_1_create; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Mono; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | /** 9 | * defer 예제 10 | */ 11 | @Slf4j 12 | public class Example14_6 { 13 | public static void main(String[] args) throws InterruptedException { 14 | log.info("# start: {}", LocalDateTime.now()); 15 | Mono justMono = Mono.just(LocalDateTime.now()); 16 | Mono deferMono = Mono.defer(() -> 17 | Mono.just(LocalDateTime.now())); 18 | 19 | Thread.sleep(2000); 20 | 21 | justMono.subscribe(data -> log.info("# onNext just1: {}", data)); 22 | deferMono.subscribe(data -> log.info("# onNext defer1: {}", data)); 23 | 24 | Thread.sleep(2000); 25 | 26 | justMono.subscribe(data -> log.info("# onNext just2: {}", data)); 27 | deferMono.subscribe(data -> log.info("# onNext defer2: {}", data)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_1_create/Example14_7.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_1_create; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Mono; 5 | 6 | import java.time.Duration; 7 | import java.time.LocalDateTime; 8 | 9 | /** 10 | * defer 예제 11 | */ 12 | @Slf4j 13 | public class Example14_7 { 14 | public static void main(String[] args) throws InterruptedException { 15 | log.info("# start: {}", LocalDateTime.now()); 16 | Mono 17 | .just("Hello") 18 | .delayElement(Duration.ofSeconds(3)) 19 | .switchIfEmpty(sayDefault()) 20 | // .switchIfEmpty(Mono.defer(() -> sayDefault())) 21 | .subscribe(data -> log.info("# onNext: {}", data)); 22 | 23 | Thread.sleep(3500); 24 | } 25 | 26 | private static Mono sayDefault() { 27 | log.info("# Say Hi"); 28 | return Mono.just("Hi"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_1_create/Example14_8.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_1_create; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | import java.util.stream.Stream; 10 | 11 | /** 12 | * using 예제 13 | */ 14 | @Slf4j 15 | public class Example14_8 { 16 | public static void main(String[] args) { 17 | Path path = Paths.get("D:\\resources\\using_example.txt"); 18 | 19 | Flux 20 | .using(() -> Files.lines(path), Flux::fromStream, Stream::close) 21 | .subscribe(log::info); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_1_create/Example14_9.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_1_create; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | /** 7 | * generate 예제 8 | */ 9 | @Slf4j 10 | public class Example14_9 { 11 | public static void main(String[] args) { 12 | Flux 13 | .generate(() -> 0, (state, sink) -> { 14 | sink.next(state); 15 | if (state == 10) 16 | sink.complete(); 17 | return ++state; 18 | }) 19 | .subscribe(data -> log.info("# onNext: {}", data)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_2_filter/Example14_15.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_2_filter; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | /** 7 | * filter 예제 8 | */ 9 | @Slf4j 10 | public class Example14_15 { 11 | public static void main(String[] args) { 12 | Flux 13 | .range(1, 20) 14 | .filter(num -> num % 2 != 0) 15 | .subscribe(data -> log.info("# onNext: {}", data)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_2_filter/Example14_16.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_2_filter; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | 7 | /** 8 | * filter 예제 9 | */ 10 | @Slf4j 11 | public class Example14_16 { 12 | public static void main(String[] args) { 13 | Flux 14 | .fromIterable(SampleData.btcTopPricesPerYear) 15 | .filter(tuple -> tuple.getT2() > 20_000_000) 16 | .subscribe(data -> log.info(data.getT1() + ":" + data.getT2())); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_2_filter/Example14_17.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_2_filter; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | import reactor.core.publisher.Mono; 7 | import reactor.core.scheduler.Schedulers; 8 | import reactor.util.function.Tuple2; 9 | 10 | import java.util.Map; 11 | 12 | import static chapter14.SampleData.*; 13 | 14 | /** 15 | * filterWhen 예제 16 | */ 17 | @Slf4j 18 | public class Example14_17 { 19 | public static void main(String[] args) throws InterruptedException { 20 | Map> vaccineMap = 21 | getCovidVaccines(); 22 | Flux 23 | .fromIterable(SampleData.coronaVaccineNames) 24 | .filterWhen(vaccine -> Mono 25 | .just(vaccineMap.get(vaccine).getT2() >= 3_000_000) 26 | .publishOn(Schedulers.parallel())) 27 | .subscribe(data -> log.info("# onNext: {}", data)); 28 | 29 | Thread.sleep(1000); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_2_filter/Example14_18.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_2_filter; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | import java.time.Duration; 7 | 8 | /** 9 | * skip 예제 10 | */ 11 | @Slf4j 12 | public class Example14_18 { 13 | public static void main(String[] args) throws InterruptedException { 14 | Flux 15 | .interval(Duration.ofSeconds(1)) 16 | .skip(2) 17 | .subscribe(data -> log.info("# onNext: {}", data)); 18 | 19 | Thread.sleep(5500L); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_2_filter/Example14_19.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_2_filter; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | import java.time.Duration; 7 | 8 | /** 9 | * skip 예제 10 | */ 11 | @Slf4j 12 | public class Example14_19 { 13 | public static void main(String[] args) throws InterruptedException { 14 | Flux 15 | .interval(Duration.ofMillis(300)) 16 | .skip(Duration.ofSeconds(1)) 17 | .subscribe(data -> log.info("# onNext: {}", data)); 18 | 19 | Thread.sleep(2000L); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_2_filter/Example14_20.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_2_filter; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | 7 | /** 8 | * skip 예제 9 | */ 10 | @Slf4j 11 | public class Example14_20 { 12 | public static void main(String[] args) { 13 | Flux 14 | .fromIterable(SampleData.btcTopPricesPerYear) 15 | .filter(tuple -> tuple.getT2() >= 20_000_000) 16 | .skip(2) 17 | .subscribe(tuple -> log.info("{}, {}", tuple.getT1(), tuple.getT2())); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_2_filter/Example14_21.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_2_filter; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | import java.time.Duration; 7 | 8 | /** 9 | * take 예제 10 | */ 11 | @Slf4j 12 | public class Example14_21 { 13 | public static void main(String[] args) throws InterruptedException { 14 | Flux 15 | .interval(Duration.ofSeconds(1)) 16 | .take(3) 17 | .subscribe(data -> log.info("# onNext: {}", data)); 18 | 19 | Thread.sleep(4000L); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_2_filter/Example14_22.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_2_filter; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | import java.time.Duration; 7 | 8 | /** 9 | * take 예제 10 | */ 11 | @Slf4j 12 | public class Example14_22 { 13 | public static void main(String[] args) throws InterruptedException { 14 | Flux 15 | .interval(Duration.ofSeconds(1)) 16 | .take(Duration.ofMillis(2500)) 17 | .subscribe(data -> log.info("# onNext: {}", data)); 18 | 19 | Thread.sleep(3000L); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_2_filter/Example14_23.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_2_filter; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | 7 | /** 8 | * takeLast 예제 9 | */ 10 | @Slf4j 11 | public class Example14_23 { 12 | public static void main(String[] args) { 13 | Flux 14 | .fromIterable(SampleData.btcTopPricesPerYear) 15 | .takeLast(2) 16 | .subscribe(tuple -> log.info("# onNext: {}, {}", 17 | tuple.getT1(), tuple.getT2())); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_2_filter/Example14_24.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_2_filter; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | 7 | /** 8 | * takeUntil 예제 9 | */ 10 | @Slf4j 11 | public class Example14_24 { 12 | public static void main(String[] args) { 13 | Flux 14 | .fromIterable(SampleData.btcTopPricesPerYear) 15 | .takeUntil(tuple -> tuple.getT2() > 20_000_000) 16 | .subscribe(tuple -> log.info("# onNext: {}, {}", 17 | tuple.getT1(), tuple.getT2())); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_2_filter/Example14_25.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_2_filter; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | 7 | /** 8 | * takeWhile 예제 9 | */ 10 | @Slf4j 11 | public class Example14_25 { 12 | public static void main(String[] args) { 13 | Flux 14 | .fromIterable(SampleData.btcTopPricesPerYear) 15 | .takeWhile(tuple -> tuple.getT2() < 20_000_000) 16 | .subscribe(tuple -> log.info("# onNext: {}, {}", 17 | tuple.getT1(), tuple.getT2())); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_2_filter/Example14_26.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_2_filter; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | 7 | /** 8 | * next 예제 9 | */ 10 | @Slf4j 11 | public class Example14_26 { 12 | public static void main(String[] args) { 13 | Flux 14 | .fromIterable(SampleData.btcTopPricesPerYear) 15 | .next() 16 | .subscribe(tuple -> log.info("# onNext: {}, {}", tuple.getT1(), tuple.getT2())); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_3_transformation/Example14_27.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_3_transformation; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | /** 7 | * map 예제 8 | */ 9 | @Slf4j 10 | public class Example14_27 { 11 | public static void main(String[] args) { 12 | Flux 13 | .just("1-Circle", "3-Circle", "5-Circle") 14 | .map(circle -> circle.replace("Circle", "Rectangle")) 15 | .subscribe(data -> log.info("# onNext: {}", data)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_3_transformation/Example14_28.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_3_transformation; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | 7 | /** 8 | * map 예제 9 | */ 10 | @Slf4j 11 | public class Example14_28 { 12 | public static void main(String[] args) { 13 | final double buyPrice = 50_000_000; 14 | Flux 15 | .fromIterable(SampleData.btcTopPricesPerYear) 16 | .filter(tuple -> tuple.getT1() == 2021) 17 | .doOnNext(data -> log.info("# doOnNext: {}", data)) 18 | .map(tuple -> calculateProfitRate(buyPrice, tuple.getT2())) 19 | .subscribe(data -> log.info("# onNext: {}%", data)); 20 | } 21 | 22 | private static double calculateProfitRate(final double buyPrice, Long topPrice) { 23 | return (topPrice - buyPrice) / buyPrice * 100; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_3_transformation/Example14_29.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_3_transformation; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | /** 7 | * flatMap 예제 8 | */ 9 | @Slf4j 10 | public class Example14_29 { 11 | public static void main(String[] args) { 12 | Flux 13 | .just("Good", "Bad") 14 | .flatMap(feeling -> Flux 15 | .just("Morning", "Afternoon", "Evening") 16 | .map(time -> feeling + " " + time)) 17 | .subscribe(log::info); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_3_transformation/Example14_30.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_3_transformation; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | /** 8 | * flatMap 예제 9 | */ 10 | @Slf4j 11 | public class Example14_30 { 12 | public static void main(String[] args) throws InterruptedException { 13 | Flux 14 | .range(2, 8) 15 | .flatMap(dan -> Flux 16 | .range(1, 9) 17 | .publishOn(Schedulers.parallel()) 18 | .map(n -> dan + " * " + n + " = " + dan * n)) 19 | .subscribe(log::info); 20 | 21 | Thread.sleep(100L); 22 | } 23 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_3_transformation/Example14_31.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_3_transformation; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | /** 7 | * concat 예제 8 | */ 9 | @Slf4j 10 | public class Example14_31 { 11 | public static void main(String[] args) { 12 | Flux 13 | .concat(Flux.just(1, 2, 3), Flux.just(4, 5)) 14 | .subscribe(data -> log.info("# onNext: {}", data)); 15 | } 16 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_3_transformation/Example14_32.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_3_transformation; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | import reactor.util.function.Tuple2; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * concat 예제 12 | */ 13 | @Slf4j 14 | public class Example14_32 { 15 | public static void main(String[] args) { 16 | Flux 17 | .concat( 18 | Flux.fromIterable(getViralVector()), 19 | Flux.fromIterable(getMRNA()), 20 | Flux.fromIterable(getSubunit())) 21 | .subscribe(data -> log.info("# onNext: {}", data)); 22 | } 23 | 24 | private static List> getViralVector() { 25 | return SampleData.viralVectorVaccines; 26 | } 27 | 28 | private static List> getMRNA() { 29 | return SampleData.mRNAVaccines; 30 | } 31 | 32 | private static List> getSubunit() { 33 | return SampleData.subunitVaccines; 34 | } 35 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_3_transformation/Example14_33.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_3_transformation; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | import java.time.Duration; 7 | 8 | /** 9 | * merge 예제 10 | */ 11 | @Slf4j 12 | public class Example14_33 { 13 | public static void main(String[] args) throws InterruptedException { 14 | Flux 15 | .merge( 16 | Flux.just(1, 2, 3, 4).delayElements(Duration.ofMillis(300L)), 17 | Flux.just(5, 6, 7).delayElements(Duration.ofMillis(500L)) 18 | ) 19 | .subscribe(data -> log.info("# onNext: {}", data)); 20 | 21 | Thread.sleep(2000L); 22 | } 23 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_3_transformation/Example14_35.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_3_transformation; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | import java.time.Duration; 7 | 8 | /** 9 | * zip 예제 10 | */ 11 | @Slf4j 12 | public class Example14_35 { 13 | public static void main(String[] args) throws InterruptedException { 14 | Flux 15 | .zip( 16 | Flux.just(1, 2, 3).delayElements(Duration.ofMillis(300L)), 17 | Flux.just(4, 5, 6).delayElements(Duration.ofMillis(500L)) 18 | ) 19 | .subscribe(tuple2 -> log.info("# onNext: {}", tuple2)); 20 | 21 | Thread.sleep(2500L); 22 | } 23 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_3_transformation/Example14_36.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_3_transformation; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | import java.time.Duration; 7 | 8 | /** 9 | * zip 예제 10 | */ 11 | @Slf4j 12 | public class Example14_36 { 13 | public static void main(String[] args) throws InterruptedException { 14 | Flux 15 | .zip( 16 | Flux.just(1, 2, 3).delayElements(Duration.ofMillis(300L)), 17 | Flux.just(4, 5, 6).delayElements(Duration.ofMillis(500L)), 18 | (n1, n2) -> n1 * n2 19 | ) 20 | .subscribe(data -> log.info("# onNext: {}", data)); 21 | 22 | Thread.sleep(2500L); 23 | } 24 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_3_transformation/Example14_40.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_3_transformation; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | 7 | import java.util.stream.Collectors; 8 | 9 | /** 10 | * collectList 예제 11 | */ 12 | @Slf4j 13 | public class Example14_40 { 14 | public static void main(String[] args) { 15 | Flux 16 | .just("...", "---", "...") 17 | .map(code -> transformMorseCode(code)) 18 | .collectList() 19 | .subscribe(list -> log.info(list.stream().collect(Collectors.joining()))); 20 | } 21 | 22 | public static String transformMorseCode(String morseCode) { 23 | return SampleData.morseCodeMap.get(morseCode); 24 | } 25 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_3_transformation/Example14_41.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_3_transformation; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | 7 | /** 8 | * collectMap 예제 9 | */ 10 | @Slf4j 11 | public class Example14_41 { 12 | public static void main(String[] args) { 13 | Flux 14 | .range(0, 26) 15 | .collectMap(key -> SampleData.morseCodes[key], 16 | value -> transformToLetter(value)) 17 | .subscribe(map -> log.info("# onNext: {}", map)); 18 | } 19 | 20 | private static String transformToLetter(int value) { 21 | return Character.toString((char) ('a' + value)); 22 | } 23 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_5_error/Example14_43.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_5_error; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.publisher.Mono; 6 | 7 | /** 8 | * error 처리 예제 9 | * - error Operator 10 | * - 명시적으로 error 이벤트를 발생시켜야 하는 경우 11 | */ 12 | @Slf4j 13 | public class Example14_43 { 14 | public static void main(String[] args) { 15 | Flux 16 | .range(1, 5) 17 | .flatMap(num -> { 18 | if ((num * 2) % 3 == 0) { 19 | return Flux.error( 20 | new IllegalArgumentException("Not allowed multiple of 3")); 21 | } else { 22 | return Mono.just(num * 2); 23 | } 24 | }) 25 | .subscribe(data -> log.info("# onNext: {}", data), 26 | error -> log.error("# onError: ", error)); 27 | } 28 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_5_error/Example14_45.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_5_error; 2 | 3 | import chapter14.Book; 4 | import chapter14.SampleData; 5 | import lombok.extern.slf4j.Slf4j; 6 | import reactor.core.publisher.Flux; 7 | 8 | /** 9 | * error 처리 예제 10 | * - onErrorReturn Operator 11 | * - 예외가 발생했을 때, error 이벤트를 발생시키지 않고, default value로 대체해서 emit하고자 할 경우 12 | * - try ~ catch 문의 경우, catch해서 return default value 하는 것과 같다. 13 | */ 14 | @Slf4j 15 | public class Example14_45 { 16 | public static void main(String[] args) { 17 | getBooks() 18 | .map(book -> book.getPenName().toUpperCase()) 19 | .onErrorReturn("No pen name") 20 | .subscribe(log::info); 21 | } 22 | 23 | public static Flux getBooks() { 24 | return Flux.fromIterable(SampleData.books); 25 | } 26 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_5_error/Example14_46.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_5_error; 2 | 3 | import chapter14.Book; 4 | import chapter14.SampleData; 5 | import lombok.extern.slf4j.Slf4j; 6 | import reactor.core.publisher.Flux; 7 | 8 | import java.util.IllegalFormatException; 9 | 10 | /** 11 | * error 처리 예제 12 | * - onErrorReturn Operator 13 | * - 예외가 발생했을 때, error 이벤트를 발생시키지 않고, default value로 대체해서 emit하고자 할 경우 14 | * - try ~ catch 문의 경우, catch해서 return default value 하는 것과 같다. 15 | */ 16 | @Slf4j 17 | public class Example14_46 { 18 | public static void main(String[] args) { 19 | getBooks() 20 | .map(book -> book.getPenName().toUpperCase()) 21 | .onErrorReturn(NullPointerException.class, "no pen name") 22 | .onErrorReturn(IllegalFormatException.class, "Illegal pen name") 23 | .subscribe(log::info); 24 | } 25 | 26 | public static Flux getBooks() { 27 | return Flux.fromIterable(SampleData.books); 28 | } 29 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_5_error/Example14_48.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_5_error; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | /** 7 | * error 처리 예제 8 | * - onErrorContinue Operator 9 | * - 예외가 발생했을 때, 예외를 발생시킨 데이터를 건너뛰고 Upstream에서 emit된 다음 데이터를 10 | * 처리한다. 11 | */ 12 | @Slf4j 13 | public class Example14_48 { 14 | public static void main(String[] args) { 15 | Flux 16 | .just(1, 2, 4, 0, 6, 12) 17 | .map(num -> 12 / num) 18 | .onErrorContinue((error, num) -> 19 | log.error("error: {}, num: {}", error, num)) 20 | .subscribe(data -> log.info("# onNext: {}", data), 21 | error -> log.error("# onError: ", error)); 22 | } 23 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_6_time/Example14_51.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_6_time; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | import java.time.Duration; 7 | 8 | /** 9 | * 시간 측정 예제 10 | * - elapsed Operator 11 | * - emit된 데이터 사이의 경과된 시간을 측정한다. 12 | * - emit된 첫번째 데이터는 onSubscribe Signal과 첫번째 데이터 사이의 시간을 기준으로 측정한다. 13 | * - 측정된 시간 단위는 milliseconds이다. 14 | */ 15 | @Slf4j 16 | public class Example14_51 { 17 | public static void main(String[] args) throws InterruptedException { 18 | Flux 19 | .range(1, 5) 20 | .delayElements(Duration.ofSeconds(1)) 21 | .elapsed() 22 | .subscribe(data -> log.info("# onNext: {}, time: {}", 23 | data.getT2(), data.getT1())); 24 | 25 | Thread.sleep(6000); 26 | } 27 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_7_split/Example14_55.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_7_split; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | /** 7 | * split 예제 8 | * - buffer(maxSize) Operator 9 | * - Upstream에서 emit되는 첫 번째 데이터부터 maxSize 숫자만큼의 데이터를 List 버퍼로 한번에 emit한다. 10 | * - 마지막 버퍼가 포함하는 데이터는 maxSize보다 작거나 같다. 11 | */ 12 | @Slf4j 13 | public class Example14_55 { 14 | public static void main(String[] args) { 15 | Flux.range(1, 95) 16 | .buffer(10) 17 | .subscribe(buffer -> log.info("# onNext: {}", buffer)); 18 | } 19 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_7_split/Example14_56.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_7_split; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | import java.time.Duration; 7 | 8 | /** 9 | * split 예제 10 | * - bufferTimeout(maxSize, maxTime) Operator 11 | * - Upstream에서 emit되는 첫 번째 데이터부터 maxSize 숫자 만큼의 데이터 또는 maxTime 내에 emit된 데이터를 List 버퍼로 한번에 emit한다. 12 | * - maxSize나 maxTime에서 먼저 조건에 부합할때까지 emit된 데이터를 List 버퍼로 emit한다. 13 | * - 마지막 버퍼가 포함하는 데이터는 maxSize보다 작거나 같다. 14 | */ 15 | @Slf4j 16 | public class Example14_56 { 17 | public static void main(String[] args) { 18 | Flux 19 | .range(1, 20) 20 | .map(num -> { 21 | try { 22 | if (num < 10) { 23 | Thread.sleep(100L); 24 | } else { 25 | Thread.sleep(300L); 26 | } 27 | } catch (InterruptedException e) {} 28 | return num; 29 | }) 30 | .bufferTimeout(3, Duration.ofMillis(400L)) 31 | .subscribe(buffer -> log.info("# onNext: {}", buffer)); 32 | } 33 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_7_split/Example14_57.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_7_split; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | 7 | /** 8 | * split 예제 9 | * - groupBy(keyMapper) Operator 10 | * - emit되는 데이터를 key를 기준으로 그룹화 한 GroupedFlux를 리턴한다. 11 | * - 그룹화 된 GroupedFlux로 그룹별 작업을 할 수 있다. 12 | */ 13 | @Slf4j 14 | public class Example14_57 { 15 | public static void main(String[] args) { 16 | Flux.fromIterable(SampleData.books) 17 | .groupBy(book -> book.getAuthorName()) 18 | .flatMap(groupedFlux -> 19 | groupedFlux 20 | .map(book -> book.getBookName() + 21 | "(" + book.getAuthorName() + ")") 22 | .collectList() 23 | ) 24 | .subscribe(bookByAuthor -> 25 | log.info("# book by author: {}", bookByAuthor)); 26 | } 27 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter14/operator_7_split/Example14_58.java: -------------------------------------------------------------------------------- 1 | package chapter14.operator_7_split; 2 | 3 | import chapter14.SampleData; 4 | import lombok.extern.slf4j.Slf4j; 5 | import reactor.core.publisher.Flux; 6 | 7 | /** 8 | * split 예제 9 | * - groupBy(keyMapper, valueMapper) Operator 10 | * - emit되는 데이터를 key를 기준으로 그룹화 한 GroupedFlux를 리턴한다. 11 | * - 그룹화 된 GroupedFlux로 그룹별 작업을 할 수 있다. 12 | * - valueMapper를 추가로 전달해서 그룹화 되어 emit되는 데이터의 값을 미리 다른 값으로 변경할 수 있다. 13 | */ 14 | @Slf4j 15 | public class Example14_58 { 16 | public static void main(String[] args) { 17 | Flux.fromIterable(SampleData.books) 18 | .groupBy(book -> 19 | book.getAuthorName(), 20 | book -> book.getBookName() + "(" + book.getAuthorName() + ")") 21 | .flatMap(groupedFlux -> groupedFlux.collectList()) 22 | .subscribe(bookByAuthor -> 23 | log.info("# book by author: {}", bookByAuthor)); 24 | } 25 | } -------------------------------------------------------------------------------- /part2/src/main/java/chapter5/Example5_1.java: -------------------------------------------------------------------------------- 1 | package chapter5; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | /** 7 | * Hello Reactor 예제 8 | */ 9 | @Slf4j 10 | public class Example5_1 { 11 | public static void main(String[] args) { 12 | Flux sequence = Flux.just("Hello", "Reactor"); 13 | sequence.map(data -> data.toLowerCase()) 14 | .subscribe(data -> System.out.println(data)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter6/Example6_1.java: -------------------------------------------------------------------------------- 1 | package chapter6; 2 | 3 | 4 | import reactor.core.publisher.Mono; 5 | 6 | /** 7 | * Mono 기본 개념 예제 8 | * - 1개의 데이터를 생성해서 emit한다. 9 | */ 10 | public class Example6_1 { 11 | public static void main(String[] args) { 12 | Mono.just("Hello Reactor") 13 | .subscribe(System.out::println); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter6/Example6_2.java: -------------------------------------------------------------------------------- 1 | package chapter6; 2 | 3 | 4 | import reactor.core.publisher.Mono; 5 | 6 | /** 7 | * Mono 기본 개념 예제 8 | * - 원본 데이터의 emit 없이 onComplete signal 만 emit 한다. 9 | */ 10 | public class Example6_2 { 11 | public static void main(String[] args) { 12 | Mono 13 | .empty() 14 | .subscribe( 15 | none -> System.out.println("# emitted onNext signal"), 16 | error -> {}, 17 | () -> System.out.println("# emitted onComplete signal") 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter6/Example6_4.java: -------------------------------------------------------------------------------- 1 | package chapter6; 2 | 3 | import reactor.core.publisher.Flux; 4 | 5 | /** 6 | * Flux 기본 예제 7 | */ 8 | public class Example6_4 { 9 | public static void main(String[] args) { 10 | Flux.just(6, 9, 13) 11 | .map(num -> num % 2) 12 | .subscribe(System.out::println); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter6/Example6_5.java: -------------------------------------------------------------------------------- 1 | package chapter6; 2 | 3 | import reactor.core.publisher.Flux; 4 | 5 | /** 6 | * Flux 에서의 Operator 체인 사용 예제 7 | */ 8 | public class Example6_5 { 9 | public static void main(String[] args) { 10 | Flux.fromArray(new Integer[]{3, 6, 7, 9}) 11 | .filter(num -> num > 6) 12 | .map(num -> num * 2) 13 | .subscribe(System.out::println); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter6/Example6_6.java: -------------------------------------------------------------------------------- 1 | package chapter6; 2 | 3 | import reactor.core.publisher.Flux; 4 | import reactor.core.publisher.Mono; 5 | 6 | /** 7 | * 2개의 Mono를 연결해서 Flux로 변환하는 예제 8 | */ 9 | public class Example6_6 { 10 | public static void main(String[] args) { 11 | Flux flux = 12 | Mono.justOrEmpty("Steve") 13 | .concatWith(Mono.justOrEmpty("Jobs")); 14 | flux.subscribe(System.out::println); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter6/Example6_7.java: -------------------------------------------------------------------------------- 1 | package chapter6; 2 | 3 | import reactor.core.publisher.Flux; 4 | 5 | /** 6 | * 여러개의 Flux를 연결해서 하나의 Flux로 결합하는 예제 7 | */ 8 | public class Example6_7 { 9 | public static void main(String[] args) { 10 | Flux.concat( 11 | Flux.just("Mercury", "Venus", "Earth"), 12 | Flux.just("Mars", "Jupiter", "Saturn"), 13 | Flux.just("Uranus", "Neptune", "Pluto")) 14 | .collectList() 15 | .subscribe(planets -> System.out.println(planets)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter7/Example7_1.java: -------------------------------------------------------------------------------- 1 | package chapter7; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | import java.util.Arrays; 7 | 8 | /** 9 | * Cold Sequence 예제 10 | */ 11 | @Slf4j 12 | public class Example7_1 { 13 | public static void main(String[] args) throws InterruptedException { 14 | 15 | Flux coldFlux = 16 | Flux 17 | .fromIterable(Arrays.asList("KOREA", "JAPAN", "CHINESE")) 18 | .map(String::toLowerCase); 19 | 20 | coldFlux.subscribe(country -> log.info("# Subscriber1: {}", country)); 21 | System.out.println("----------------------------------------------------------------------"); 22 | Thread.sleep(2000L); 23 | coldFlux.subscribe(country -> log.info("# Subscriber2: {}", country)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter7/Example7_2.java: -------------------------------------------------------------------------------- 1 | package chapter7; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | 6 | import java.time.Duration; 7 | 8 | /** 9 | * Hot Sequence 예제 10 | */ 11 | @Slf4j 12 | public class Example7_2 { 13 | public static void main(String[] args) throws InterruptedException { 14 | String[] singers = {"Singer A", "Singer B", "Singer C", "Singer D", "Singer E"}; 15 | 16 | log.info("# Begin concert:"); 17 | Flux concertFlux = 18 | Flux 19 | .fromArray(singers) 20 | .delayElements(Duration.ofSeconds(1)) 21 | .share(); 22 | 23 | concertFlux.subscribe( 24 | singer -> log.info("# Subscriber1 is watching {}'s song", singer) 25 | ); 26 | 27 | Thread.sleep(2500); 28 | 29 | concertFlux.subscribe( 30 | singer -> log.info("# Subscriber2 is watching {}'s song", singer) 31 | ); 32 | 33 | Thread.sleep(3000); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter8/Example8_1.java: -------------------------------------------------------------------------------- 1 | package chapter8; 2 | 3 | import lombok.SneakyThrows; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.reactivestreams.Subscription; 6 | import reactor.core.publisher.BaseSubscriber; 7 | import reactor.core.publisher.Flux; 8 | 9 | /** 10 | * doOnXXXX 예제 11 | * - doOnXXXX() Operator의 호출 시점을 알 수 있다. 12 | */ 13 | @Slf4j 14 | public class Example8_1 { 15 | public static void main(String[] args) { 16 | Flux.range(1, 5) 17 | .doOnRequest(data -> log.info("# doOnRequest: {}", data)) 18 | .subscribe(new BaseSubscriber() { 19 | @Override 20 | protected void hookOnSubscribe(Subscription subscription) { 21 | request(1); 22 | } 23 | 24 | @SneakyThrows 25 | @Override 26 | protected void hookOnNext(Integer value) { 27 | Thread.sleep(2000L); 28 | log.info("# hookOnNext: {}", value); 29 | request(1); 30 | } 31 | }); 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter8/Example8_2.java: -------------------------------------------------------------------------------- 1 | package chapter8; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | import java.time.Duration; 8 | 9 | /** 10 | * Unbounded request 일 경우, Downstream 에 Backpressure Error 전략을 적용하는 예제 11 | * - Downstream 으로 전달 할 데이터가 버퍼에 가득 찰 경우, Exception을 발생 시키는 전략 12 | */ 13 | @Slf4j 14 | public class Example8_2 { 15 | public static void main(String[] args) throws InterruptedException { 16 | Flux 17 | .interval(Duration.ofMillis(1L)) 18 | .onBackpressureError() 19 | .doOnNext(data -> log.info("# doOnNext: {}", data)) 20 | .publishOn(Schedulers.parallel()) 21 | .subscribe(data -> { 22 | try { 23 | Thread.sleep(5L); 24 | } catch (InterruptedException e) {} 25 | log.info("# onNext: {}", data); 26 | }, 27 | error -> log.error("# onError", error)); 28 | 29 | Thread.sleep(2000L); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter8/Example8_3.java: -------------------------------------------------------------------------------- 1 | package chapter8; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | import java.time.Duration; 8 | 9 | /** 10 | * Unbounded request 일 경우, Downstream 에 Backpressure Drop 전략을 적용하는 예제 11 | * - Downstream 으로 전달 할 데이터가 버퍼에 가득 찰 경우, 버퍼 밖에서 대기하는 먼저 emit 된 데이터를 Drop 시키는 전략 12 | */ 13 | @Slf4j 14 | public class Example8_3 { 15 | public static void main(String[] args) throws InterruptedException { 16 | Flux 17 | .interval(Duration.ofMillis(1L)) 18 | .onBackpressureDrop(dropped -> log.info("# dropped: {}", dropped)) 19 | .publishOn(Schedulers.parallel()) 20 | .subscribe(data -> { 21 | try { 22 | Thread.sleep(5L); 23 | } catch (InterruptedException e) {} 24 | log.info("# onNext: {}", data); 25 | }, 26 | error -> log.error("# onError", error)); 27 | 28 | Thread.sleep(2000L); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter8/Example8_4.java: -------------------------------------------------------------------------------- 1 | package chapter8; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.scheduler.Schedulers; 6 | 7 | import java.time.Duration; 8 | 9 | /** 10 | * Unbounded request 일 경우, Downstream 에 Backpressure Latest 전략을 적용하는 예제 11 | * - Downstream 으로 전달 할 데이터가 버퍼에 가득 찰 경우, 12 | * 버퍼 밖에서 대기하는 가장 나중에(최근에) emit 된 데이터부터 버퍼에 채우는 전략 13 | */ 14 | @Slf4j 15 | public class Example8_4 { 16 | public static void main(String[] args) throws InterruptedException { 17 | Flux 18 | .interval(Duration.ofMillis(1L)) 19 | .onBackpressureLatest() 20 | .publishOn(Schedulers.parallel()) 21 | .subscribe(data -> { 22 | try { 23 | Thread.sleep(5L); 24 | } catch (InterruptedException e) {} 25 | log.info("# onNext: {}", data); 26 | }, 27 | error -> log.error("# onError", error)); 28 | 29 | Thread.sleep(2000L); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter9/Example9_10.java: -------------------------------------------------------------------------------- 1 | package chapter9; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.publisher.Sinks; 6 | 7 | import static reactor.core.publisher.Sinks.EmitFailureHandler.FAIL_FAST; 8 | 9 | /** 10 | * Sinks.Many 예제 11 | * - replay()를 사용하여 이미 emit된 데이터 중에서 특정 개수의 최신 데이터만 전달하는 예제 12 | */ 13 | @Slf4j 14 | public class Example9_10 { 15 | public static void main(String[] args) { 16 | Sinks.Many replaySink = Sinks.many().replay().limit(2); 17 | Flux fluxView = replaySink.asFlux(); 18 | 19 | replaySink.emitNext(1, FAIL_FAST); 20 | replaySink.emitNext(2, FAIL_FAST); 21 | replaySink.emitNext(3, FAIL_FAST); 22 | 23 | fluxView.subscribe(data -> log.info("# Subscriber1: {}", data)); 24 | 25 | replaySink.emitNext(4, FAIL_FAST); 26 | 27 | fluxView.subscribe(data -> log.info("# Subscriber2: {}", data)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter9/Example9_4.java: -------------------------------------------------------------------------------- 1 | package chapter9; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Mono; 5 | import reactor.core.publisher.Sinks; 6 | 7 | import static reactor.core.publisher.Sinks.EmitFailureHandler.FAIL_FAST; 8 | 9 | /** 10 | * Sinks.One 예제 11 | * - emit 된 데이터 중에서 단 하나의 데이터만 Subscriber에게 전달한다. 나머지 데이터는 Drop 됨. 12 | */ 13 | @Slf4j 14 | public class Example9_4 { 15 | public static void main(String[] args) throws InterruptedException { 16 | Sinks.One sinkOne = Sinks.one(); 17 | Mono mono = sinkOne.asMono(); 18 | 19 | sinkOne.emitValue("Hello Reactor", FAIL_FAST); 20 | sinkOne.emitValue("Hi Reactor", FAIL_FAST); 21 | sinkOne.emitValue(null, FAIL_FAST); 22 | 23 | mono.subscribe(data -> log.info("# Subscriber1 {}", data)); 24 | mono.subscribe(data -> log.info("# Subscriber2 {}", data)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter9/Example9_8.java: -------------------------------------------------------------------------------- 1 | package chapter9; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.publisher.Sinks; 6 | 7 | import static reactor.core.publisher.Sinks.EmitFailureHandler.FAIL_FAST; 8 | 9 | /** 10 | * Sinks.Many 예제 11 | * - unicast()통해 단 하나의 Subscriber만 데이터를 전달 받을 수 있다 12 | */ 13 | @Slf4j 14 | public class Example9_8 { 15 | public static void main(String[] args) throws InterruptedException { 16 | Sinks.Many unicastSink = Sinks.many().unicast().onBackpressureBuffer(); 17 | Flux fluxView = unicastSink.asFlux(); 18 | 19 | unicastSink.emitNext(1, FAIL_FAST); 20 | unicastSink.emitNext(2, FAIL_FAST); 21 | 22 | 23 | fluxView.subscribe(data -> log.info("# Subscriber1: {}", data)); 24 | 25 | unicastSink.emitNext(3, FAIL_FAST); 26 | 27 | fluxView.subscribe(data -> log.info("# Subscriber2: {}", data)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /part2/src/main/java/chapter9/Example9_9.java: -------------------------------------------------------------------------------- 1 | package chapter9; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.publisher.Sinks; 6 | 7 | import static reactor.core.publisher.Sinks.EmitFailureHandler.FAIL_FAST; 8 | 9 | /** 10 | * Sinks.Many 예제 11 | * - multicast()를 사용해서 하나 이상의 Subscriber에게 데이터를 emit하는 예제 12 | */ 13 | @Slf4j 14 | public class Example9_9 { 15 | public static void main(String[] args) { 16 | Sinks.Many multicastSink = 17 | Sinks.many().multicast().onBackpressureBuffer(); 18 | Flux fluxView = multicastSink.asFlux(); 19 | 20 | multicastSink.emitNext(1, FAIL_FAST); 21 | multicastSink.emitNext(2, FAIL_FAST); 22 | 23 | fluxView.subscribe(data -> log.info("# Subscriber1: {}", data)); 24 | fluxView.subscribe(data -> log.info("# Subscriber2: {}", data)); 25 | 26 | multicastSink.emitNext(3, FAIL_FAST); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /part2/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{HH:mm:ss.SSS} [%thread] %-5level- %msg%n 6 | 7 | 8 | 9 | 10 | /tmp/access.log 11 | 12 | /tmp/access-%d{yyyy-MM-dd}.log 13 | 50 14 | 15 | 16 | 17 | %d{HH:mm:ss} [%-5level] %logger{36}[line: %L] - %msg%n 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /part2/src/test/java/chapter13/ExampleTest13_1.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Mono; 5 | import reactor.test.StepVerifier; 6 | 7 | /** 8 | * StepVerifier 기본 예제 9 | */ 10 | public class ExampleTest13_1 { 11 | @Test 12 | public void sayHelloReactorTest() { 13 | StepVerifier 14 | .create(Mono.just("Hello Reactor")) // 테스트 대상 Sequence 생성 15 | .expectNext("Hello Reactor") // emit 된 데이터 검증 16 | .expectComplete() // onComplete Signal 검증 17 | .verify(); // 검증 실행. 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part2/src/test/java/chapter13/ExampleTest13_11.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.test.StepVerifier; 5 | 6 | /** 7 | * StepVerifier Backpressure 테스트 예제 8 | */ 9 | public class ExampleTest13_11 { 10 | @Test 11 | public void generateNumberTest() { 12 | StepVerifier 13 | .create(BackpressureTestExample.generateNumber(), 1L) 14 | .thenConsumeWhile(num -> num >= 1) 15 | .verifyComplete(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /part2/src/test/java/chapter13/ExampleTest13_12.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.test.StepVerifier; 5 | 6 | /** 7 | * StepVerifier Backpressure 테스트 예제 8 | */ 9 | public class ExampleTest13_12 { 10 | @Test 11 | public void generateNumberTest() { 12 | StepVerifier 13 | .create(BackpressureTestExample.generateNumber(), 1L) 14 | .thenConsumeWhile(num -> num >= 1) 15 | .expectError() 16 | .verifyThenAssertThat() 17 | .hasDroppedElements(); 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /part2/src/test/java/chapter13/ExampleTest13_14.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Mono; 5 | import reactor.test.StepVerifier; 6 | 7 | /** 8 | * StepVerifier Context 테스트 예제 9 | */ 10 | public class ExampleTest13_14 { 11 | @Test 12 | public void getSecretMessageTest() { 13 | Mono source = Mono.just("hello"); 14 | 15 | StepVerifier 16 | .create( 17 | ContextTestExample 18 | .getSecretMessage(source) 19 | .contextWrite(context -> 20 | context.put("secretMessage", "Hello, Reactor")) 21 | .contextWrite(context -> context.put("secretKey", "aGVsbG8=")) 22 | ) 23 | .expectSubscription() 24 | .expectAccessibleContext() 25 | .hasKey("secretKey") 26 | .hasKey("secretMessage") 27 | .then() 28 | .expectNext("Hello, Reactor") 29 | .expectComplete() 30 | .verify(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /part2/src/test/java/chapter13/ExampleTest13_17.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Flux; 5 | import reactor.test.StepVerifier; 6 | 7 | import java.util.ArrayList; 8 | 9 | /** 10 | * StepVerifier Record 테스트 예제 11 | */ 12 | public class ExampleTest13_17 { 13 | @Test 14 | public void getCountryTest() { 15 | StepVerifier 16 | .create(RecordTestExample.getCapitalizedCountry( 17 | Flux.just("korea", "england", "canada", "india"))) 18 | .expectSubscription() 19 | .recordWith(ArrayList::new) 20 | .thenConsumeWhile(country -> !country.isEmpty()) 21 | .expectRecordedMatches(countries -> 22 | countries 23 | .stream() 24 | .allMatch(country -> 25 | Character.isUpperCase(country.charAt(0)))) 26 | .expectComplete() 27 | .verify(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /part2/src/test/java/chapter13/ExampleTest13_18.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.test.StepVerifier; 5 | import reactor.test.publisher.TestPublisher; 6 | 7 | /** 8 | * 정상동작 하는 TestPublisher 예제 9 | */ 10 | public class ExampleTest13_18 { 11 | @Test 12 | public void divideByTwoTest() { 13 | TestPublisher source = TestPublisher.create(); 14 | 15 | StepVerifier 16 | .create(GeneralTestExample.divideByTwo(source.flux())) 17 | .expectSubscription() 18 | .then(() -> source.emit(2, 4, 6, 8, 10)) 19 | .expectNext(1, 2, 3, 4) 20 | .expectError() 21 | .verify(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /part2/src/test/java/chapter13/ExampleTest13_21.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.test.StepVerifier; 5 | import reactor.test.publisher.PublisherProbe; 6 | 7 | /** 8 | * PublisherProbe 예제 9 | */ 10 | public class ExampleTest13_21 { 11 | @Test 12 | public void publisherProbeTest() { 13 | PublisherProbe probe = 14 | PublisherProbe.of(PublisherProbeTestExample.supplyStandbyPower()); 15 | 16 | StepVerifier 17 | .create(PublisherProbeTestExample 18 | .processTask( 19 | PublisherProbeTestExample.supplyMainPower(), 20 | probe.mono()) 21 | ) 22 | .expectNextCount(1) 23 | .verifyComplete(); 24 | 25 | probe.assertWasSubscribed(); 26 | probe.assertWasRequested(); 27 | probe.assertWasNotCancelled(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /part2/src/test/java/chapter13/ExampleTest13_3.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.test.StepVerifier; 5 | 6 | /** 7 | * StepVerifier 활용 예제 8 | */ 9 | public class ExampleTest13_3 { 10 | @Test 11 | public void sayHelloTest() { 12 | StepVerifier 13 | .create(GeneralTestExample.sayHello()) 14 | .expectSubscription() 15 | .as("# expect subscription") 16 | .expectNext("Hi") 17 | .as("# expect Hi") 18 | .expectNext("Reactor") 19 | .as("# expect Reactor") 20 | .verifyComplete(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /part2/src/test/java/chapter13/ExampleTest13_4.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Flux; 5 | import reactor.test.StepVerifier; 6 | 7 | /** 8 | * StepVerifier 활용 예제. 9 | */ 10 | public class ExampleTest13_4 { 11 | @Test 12 | public void divideByTwoTest() { 13 | Flux source = Flux.just(2, 4, 6, 8, 10); 14 | StepVerifier 15 | .create(GeneralTestExample.divideByTwo(source)) 16 | .expectSubscription() 17 | .expectNext(1) 18 | .expectNext(2) 19 | .expectNext(3) 20 | .expectNext(4) 21 | // .expectNext(1, 2, 3, 4) 22 | .expectError() 23 | .verify(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /part2/src/test/java/chapter13/ExampleTest13_5.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Flux; 5 | import reactor.test.StepVerifier; 6 | import reactor.test.StepVerifierOptions; 7 | 8 | /** 9 | * StepVerifier 활용 예제 10 | */ 11 | public class ExampleTest13_5 { 12 | @Test 13 | public void takeNumberTest() { 14 | Flux source = Flux.range(0, 1000); 15 | StepVerifier 16 | .create(GeneralTestExample.takeNumber(source, 500), 17 | StepVerifierOptions.create().scenarioName("Verify from 0 to 499")) 18 | .expectSubscription() 19 | .expectNext(0) 20 | .expectNextCount(498) 21 | .expectNext(500) 22 | .expectComplete() 23 | .verify(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /part2/src/test/java/chapter13/ExampleTest13_7.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Flux; 5 | import reactor.test.StepVerifier; 6 | import reactor.test.scheduler.VirtualTimeScheduler; 7 | 8 | import java.time.Duration; 9 | 10 | /** 11 | * StepVerifier 활용 예제 12 | * - 주어진 시간을 앞당겨서 테스트 한다. 13 | */ 14 | public class ExampleTest13_7 { 15 | @Test 16 | public void getCOVID19CountTest() { 17 | StepVerifier 18 | .withVirtualTime(() -> TimeBasedTestExample.getCOVID19Count( 19 | Flux.interval(Duration.ofHours(1)).take(1) 20 | ) 21 | ) 22 | .expectSubscription() 23 | .then(() -> VirtualTimeScheduler 24 | .get() 25 | .advanceTimeBy(Duration.ofHours(1))) 26 | .expectNextCount(11) 27 | .expectComplete() 28 | .verify(); 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /part2/src/test/java/chapter13/ExampleTest13_8.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Flux; 5 | import reactor.test.StepVerifier; 6 | 7 | import java.time.Duration; 8 | 9 | /** 10 | * StepVerifier 활용 예제 11 | * -검증에 소요되는 시간을 제한한다. 12 | */ 13 | public class ExampleTest13_8 { 14 | @Test 15 | public void getCOVID19CountTest() { 16 | StepVerifier 17 | .create(TimeBasedTestExample.getCOVID19Count( 18 | Flux.interval(Duration.ofMinutes(1)).take(1) 19 | ) 20 | ) 21 | .expectSubscription() 22 | .expectNextCount(11) 23 | .expectComplete() 24 | .verify(Duration.ofSeconds(3)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /part2/src/test/java/chapter13/ExampleTest13_9.java: -------------------------------------------------------------------------------- 1 | package chapter13; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Flux; 5 | import reactor.test.StepVerifier; 6 | 7 | import java.time.Duration; 8 | 9 | /** 10 | * StepVerifier 활용 예제 11 | * - 지정된 대기 시간동안 이벤트가 없을을 확인한다. 12 | */ 13 | public class ExampleTest13_9 { 14 | @Test 15 | public void getVoteCountTest() { 16 | StepVerifier 17 | .withVirtualTime(() -> TimeBasedTestExample.getVoteCount( 18 | Flux.interval(Duration.ofMinutes(1)) 19 | ) 20 | ) 21 | .expectSubscription() 22 | .expectNoEvent(Duration.ofMinutes(1)) 23 | .expectNoEvent(Duration.ofMinutes(1)) 24 | .expectNoEvent(Duration.ofMinutes(1)) 25 | .expectNoEvent(Duration.ofMinutes(1)) 26 | .expectNoEvent(Duration.ofMinutes(1)) 27 | .expectNextCount(5) 28 | .expectComplete() 29 | .verify(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /part3/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'com.itvillage' 6 | version '1.0-SNAPSHOT' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 14 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 15 | } 16 | 17 | test { 18 | useJUnitPlatform() 19 | } -------------------------------------------------------------------------------- /part3/chapter15/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /part3/chapter15/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.2' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.itvillage' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '1.8' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | } 20 | 21 | dependencies { 22 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 23 | compileOnly 'org.projectlombok:lombok' 24 | annotationProcessor 'org.projectlombok:lombok' 25 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 26 | testImplementation 'io.projectreactor:reactor-test' 27 | } 28 | 29 | tasks.named('test') { 30 | useJUnitPlatform() 31 | } 32 | -------------------------------------------------------------------------------- /part3/chapter15/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part3/chapter15/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part3/chapter15/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part3/chapter15/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'chapter15' 2 | -------------------------------------------------------------------------------- /part3/chapter15/src/main/java/com/itvillage/Chapter15Application.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Chapter15Application { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Chapter15Application.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part3/chapter15/src/main/java/com/itvillage/book/controller/BookController.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.controller; 2 | 3 | import com.itvillage.book.dto.BookDto; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.PathVariable; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | import reactor.core.publisher.Mono; 9 | 10 | @RestController 11 | @RequestMapping("/v1/controller/books") 12 | public class BookController { 13 | @GetMapping("/{book-id}") 14 | public Mono getBook(@PathVariable("book-id") long bookId) { 15 | return Mono.just(BookDto.Response.builder() 16 | .bookId(bookId) 17 | .bookName("Advanced Java") 18 | .author("Kevin") 19 | .isbn("111-11-1111-111-1").build()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part3/chapter15/src/main/java/com/itvillage/book/dto/BookDto.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.dto; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | 6 | public class BookDto { 7 | @Builder 8 | @Getter 9 | public static class Response { 10 | private long bookId; 11 | private String bookName; 12 | private String author; 13 | private String isbn; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /part3/chapter15/src/main/java/com/itvillage/book/filter/BookLogFilter.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.filter; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.springframework.web.server.ServerWebExchange; 5 | import org.springframework.web.server.WebFilter; 6 | import org.springframework.web.server.WebFilterChain; 7 | import reactor.core.publisher.Mono; 8 | 9 | @Component 10 | public class BookLogFilter implements WebFilter { 11 | @Override 12 | public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { 13 | String path = exchange.getRequest().getURI().getPath(); 14 | return chain.filter(exchange).doAfterTerminate(() -> { 15 | if (path.contains("books")) { 16 | System.out.println("path: " + path + ", status: " + 17 | exchange.getResponse().getStatusCode()); 18 | } 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part3/chapter15/src/main/java/com/itvillage/book/filter/BookRouterFunctionFilter.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.filter; 2 | 3 | import org.springframework.web.reactive.function.server.HandlerFilterFunction; 4 | import org.springframework.web.reactive.function.server.HandlerFunction; 5 | import org.springframework.web.reactive.function.server.ServerRequest; 6 | import org.springframework.web.reactive.function.server.ServerResponse; 7 | import reactor.core.publisher.Mono; 8 | 9 | public class BookRouterFunctionFilter implements HandlerFilterFunction { 10 | @Override 11 | public Mono filter(ServerRequest request, HandlerFunction next) { 12 | String path = request.requestPath().value(); 13 | 14 | return next.handle(request).doAfterTerminate(() -> { 15 | System.out.println("path: " + path + ", status: " + 16 | request.exchange().getResponse().getStatusCode()); 17 | }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part3/chapter15/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /part3/chapter15/src/test/java/com/itvillage/Chapter15ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class Chapter15ApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part3/chapter16/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /part3/chapter16/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.2' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.itvillage' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '1.8' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | } 20 | 21 | dependencies { 22 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 23 | compileOnly 'org.projectlombok:lombok' 24 | annotationProcessor 'org.projectlombok:lombok' 25 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 26 | testImplementation 'io.projectreactor:reactor-test' 27 | implementation 'org.mapstruct:mapstruct:1.5.1.Final' 28 | annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.1.Final' 29 | } 30 | 31 | tasks.named('test') { 32 | useJUnitPlatform() 33 | } 34 | -------------------------------------------------------------------------------- /part3/chapter16/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part3/chapter16/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part3/chapter16/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part3/chapter16/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'chapter16' 2 | -------------------------------------------------------------------------------- /part3/chapter16/src/main/java/com/itvillage/Chapter16Application.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @Slf4j 8 | @SpringBootApplication 9 | public class Chapter16Application { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(Chapter16Application.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /part3/chapter16/src/main/java/com/itvillage/mvc/book/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.mvc.book; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | @Builder 9 | @Getter 10 | public class Book { 11 | private long bookId; 12 | private String titleKorean; 13 | private String titleEnglish; 14 | private String description; 15 | private String author; 16 | private String isbn; 17 | private String publishDate; 18 | private LocalDateTime createdAt; 19 | private LocalDateTime modifiedAt; 20 | } 21 | -------------------------------------------------------------------------------- /part3/chapter16/src/main/java/com/itvillage/mvc/book/BookMvcMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.mvc.book; 2 | 3 | import org.mapstruct.Mapper; 4 | 5 | @Mapper(componentModel = "spring") 6 | public interface BookMvcMapper { 7 | Book bookPostToBook(BookDto.Post requestBody); 8 | Book bookPatchToBook(BookDto.Patch requestBody); 9 | BookDto.Response bookToBookResponse(Book book); 10 | } 11 | -------------------------------------------------------------------------------- /part3/chapter16/src/main/java/com/itvillage/mvc/book/BookMvcService.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.mvc.book; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | @Service 6 | public class BookMvcService { 7 | public Book createBook(Book book) { 8 | // not implement business logic; 9 | return book; 10 | } 11 | 12 | public Book updateBook(Book book) { 13 | // not implement business logic; 14 | return book; 15 | } 16 | 17 | public Book findBook(long bookId) { 18 | return Book.builder() 19 | .bookId(bookId) 20 | .titleKorean("Java 고급") 21 | .titleEnglish("Advanced Java") 22 | .author("Kevin") 23 | .isbn("111-11-1111-111-1") 24 | .description("Java 중급 프로그래밍 마스터") 25 | .publishDate("2022-03-22") 26 | .build(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /part3/chapter16/src/main/java/com/itvillage/reactive/v1/book/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.reactive.v1.book; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | @Getter 9 | @AllArgsConstructor 10 | public class Book { 11 | private long bookId; 12 | private String titleKorean; 13 | private String titleEnglish; 14 | private String description; 15 | private String author; 16 | private String isbn; 17 | private String publishDate; 18 | private LocalDateTime createdAt; 19 | private LocalDateTime modifiedAt; 20 | } 21 | -------------------------------------------------------------------------------- /part3/chapter16/src/main/java/com/itvillage/reactive/v1/book/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.reactive.v1.book; 2 | 3 | import org.mapstruct.Mapper; 4 | import reactor.core.publisher.Mono; 5 | 6 | @Mapper(componentModel = "spring") 7 | public interface BookMapper { 8 | Book bookPostToBook(BookDto.Post requestBody); 9 | Book bookPatchToBook(BookDto.Patch requestBody); 10 | BookDto.Response bookToResponse(Book book); 11 | default Mono bookToBookResponse(Mono mono) { 12 | return mono.flatMap(book -> Mono.just(bookToResponse(book))); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /part3/chapter16/src/main/java/com/itvillage/reactive/v1/book/BookService.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.reactive.v1.book; 2 | 3 | import org.springframework.stereotype.Service; 4 | import reactor.core.publisher.Mono; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | @Service 9 | public class BookService { 10 | public Mono createBook(Book book) { 11 | // not implement business logic; 12 | return Mono.just(book); 13 | } 14 | 15 | public Mono updateBook(Book book) { 16 | // not implement business logic; 17 | return Mono.just(book); 18 | } 19 | 20 | public Mono findBook(long bookId) { 21 | return Mono.just( 22 | new Book(bookId, 23 | "Java 고급", 24 | "Advanced Java", 25 | "Kevin", 26 | "111-11-1111-111-1", 27 | "Java 중급 프로그래밍 마스터", 28 | "2022-03-22", 29 | LocalDateTime.now(), 30 | LocalDateTime.now()) 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part3/chapter16/src/main/java/com/itvillage/reactive/v2/book/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.reactive.v2.book; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | @Getter 9 | @AllArgsConstructor 10 | public class Book { 11 | private long bookId; 12 | private String titleKorean; 13 | private String titleEnglish; 14 | private String description; 15 | private String author; 16 | private String isbn; 17 | private String publishDate; 18 | private LocalDateTime createdAt; 19 | private LocalDateTime modifiedAt; 20 | } 21 | -------------------------------------------------------------------------------- /part3/chapter16/src/main/java/com/itvillage/reactive/v2/book/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.reactive.v2.book; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.MappingConstants; 5 | 6 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING, 7 | implementationName = "bookMapperV2") 8 | public interface BookMapper { 9 | Book bookPostToBook(BookDto.Post requestBody); 10 | Book bookPatchToBook(BookDto.Patch requestBody); 11 | BookDto.Response bookToResponse(Book book); 12 | } 13 | -------------------------------------------------------------------------------- /part3/chapter16/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | pattern: 3 | console: "%d{HH:mm:ss.SSS} [%thread] %-5level- %msg%n" -------------------------------------------------------------------------------- /part3/chapter16/src/test/java/com/itvillage/Chapter16ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class Chapter16ApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part3/chapter17/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /part3/chapter17/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.2' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.itvillage' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '11' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 17 | implementation 'org.springframework.boot:spring-boot-starter-validation' 18 | compileOnly 'org.projectlombok:lombok' 19 | annotationProcessor 'org.projectlombok:lombok' 20 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 21 | testImplementation 'io.projectreactor:reactor-test' 22 | implementation 'org.mapstruct:mapstruct:1.5.1.Final' 23 | annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.1.Final' 24 | } 25 | 26 | tasks.named('test') { 27 | useJUnitPlatform() 28 | } 29 | -------------------------------------------------------------------------------- /part3/chapter17/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part3/chapter17/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part3/chapter17/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part3/chapter17/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'chapter17' 2 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/java/com/itvillage/Chapter17Application.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Chapter17Application { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Chapter17Application.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/java/com/itvillage/book/v1/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v1; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | @Getter 9 | @AllArgsConstructor 10 | public class Book { 11 | private long bookId; 12 | private String titleKorean; 13 | private String titleEnglish; 14 | private String description; 15 | private String author; 16 | private String isbn; 17 | private String publishDate; 18 | private LocalDateTime createdAt; 19 | private LocalDateTime modifiedAt; 20 | } 21 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/java/com/itvillage/book/v1/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v1; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.MappingConstants; 5 | 6 | import java.util.List; 7 | 8 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING, 9 | implementationName = "bookMapperV1") 10 | public interface BookMapper { 11 | Book bookPostToBook(BookDto.Post requestBody); 12 | Book bookPatchToBook(BookDto.Patch requestBody); 13 | BookDto.Response bookToResponse(Book book); 14 | List booksToResponse(List books); 15 | } 16 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/java/com/itvillage/book/v1/BookRouter.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v1; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.reactive.function.server.RouterFunction; 6 | 7 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 8 | 9 | @Configuration("bookRouterV1") 10 | public class BookRouter { 11 | @Bean 12 | public RouterFunction routeBookV1(BookHandler handler) { 13 | return route() 14 | .POST("/v1/books", handler::createBook) 15 | .PATCH("/v1/books/{book-id}", handler::updateBook) 16 | .GET("/v1/books", handler::getBooks) 17 | .GET("/v1/books/{book-id}", handler::getBook) 18 | .build(); 19 | // return route(POST("/v1/books"), handler::createBook) 20 | // .andRoute(GET("/v1/books/{book-id}"), handler::getBook) 21 | // .andRoute(PATCH("/v1/books/{book-id}"), handler::patchBook) 22 | // .andRoute(GET("/v1/books"), handler::getBooks); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/java/com/itvillage/book/v2/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v2; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | @Getter 9 | @AllArgsConstructor 10 | public class Book { 11 | private long bookId; 12 | private String titleKorean; 13 | private String titleEnglish; 14 | private String description; 15 | private String author; 16 | private String isbn; 17 | private String publishDate; 18 | private LocalDateTime createdAt; 19 | private LocalDateTime modifiedAt; 20 | } 21 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/java/com/itvillage/book/v2/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v2; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.MappingConstants; 5 | 6 | import java.util.List; 7 | 8 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING, 9 | implementationName = "bookMapperV2") 10 | public interface BookMapper { 11 | Book bookPostToBook(BookDto.Post requestBody); 12 | Book bookPatchToBook(BookDto.Patch requestBody); 13 | BookDto.Response bookToResponse(Book book); 14 | List booksToResponse(List books); 15 | } 16 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/java/com/itvillage/book/v2/BookRouter.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v2; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.reactive.function.server.RouterFunction; 6 | 7 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 8 | 9 | @Configuration("bookRouterV2") 10 | public class BookRouter { 11 | @Bean 12 | public RouterFunction routeBookV2(BookHandler handler) { 13 | return route() 14 | .POST("/v2/books", handler::createBook) 15 | .GET("/v2/books", handler::getBooks) 16 | .GET("/v2/books/{book-id}", handler::getBook) 17 | .PATCH("/v2/books/{book-id}", handler::patchBook) 18 | .build(); 19 | // return route(POST("/v1/books"), handler::createBook) 20 | // .andRoute(GET("/v1/books/{book-id}"), handler::getBook) 21 | // .andRoute(PATCH("/v1/books/{book-id}"), handler::patchBook) 22 | // .andRoute(GET("/v1/books"), handler::getBooks); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/java/com/itvillage/book/v2/BookValidator.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v2; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.springframework.validation.Errors; 5 | import org.springframework.validation.ValidationUtils; 6 | import org.springframework.validation.Validator; 7 | 8 | @Component("bookValidatorV2") 9 | public class BookValidator implements Validator { 10 | @Override 11 | public boolean supports(Class clazz) { 12 | return BookDto.Post.class.isAssignableFrom(clazz); 13 | } 14 | 15 | @Override 16 | public void validate(Object target, Errors errors) { 17 | BookDto.Post post = (BookDto.Post) target; 18 | 19 | ValidationUtils.rejectIfEmptyOrWhitespace( 20 | errors, "titleKorean", "field.required"); 21 | 22 | ValidationUtils.rejectIfEmptyOrWhitespace( 23 | errors, "titleEnglish", "field.required"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/java/com/itvillage/book/v3/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v3; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | @Getter 9 | @AllArgsConstructor 10 | public class Book { 11 | private long bookId; 12 | private String titleKorean; 13 | private String titleEnglish; 14 | private String description; 15 | private String author; 16 | private String isbn; 17 | private String publishDate; 18 | private LocalDateTime createdAt; 19 | private LocalDateTime modifiedAt; 20 | } 21 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/java/com/itvillage/book/v3/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v3; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.MappingConstants; 5 | 6 | import java.util.List; 7 | 8 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING, 9 | implementationName = "bookMapperV3") 10 | public interface BookMapper { 11 | Book bookPostToBook(BookDto.Post requestBody); 12 | Book bookPatchToBook(BookDto.Patch requestBody); 13 | com.itvillage.book.v1.BookDto.Response bookToResponse(Book book); 14 | List booksToResponse(List books); 15 | } 16 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/java/com/itvillage/book/v3/BookRouter.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v3; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.validation.Validator; 6 | import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 7 | import org.springframework.web.reactive.function.server.RouterFunction; 8 | 9 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 10 | 11 | @Configuration("bookRouterV3") 12 | public class BookRouter { 13 | @Bean 14 | public RouterFunction routeBookV3(BookHandler handler) { 15 | return route() 16 | .POST("/v3/books", handler::createBook) 17 | .PATCH("/v3/books/{book-id}", handler::updateBook) 18 | .GET("/v3/books", handler::getBooks) 19 | .GET("/v3/books/{book-id}", handler::getBook) 20 | .build(); 21 | } 22 | 23 | @Bean 24 | public Validator springValidator() { 25 | return new LocalValidatorFactoryBean(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/java/com/itvillage/book/v4/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v4; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | @Getter 9 | @AllArgsConstructor 10 | public class Book { 11 | private long bookId; 12 | private String titleKorean; 13 | private String titleEnglish; 14 | private String description; 15 | private String author; 16 | private String isbn; 17 | private String publishDate; 18 | private LocalDateTime createdAt; 19 | private LocalDateTime modifiedAt; 20 | } 21 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/java/com/itvillage/book/v4/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v4; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.MappingConstants; 5 | 6 | import java.util.List; 7 | 8 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING, 9 | implementationName = "bookMapperV4") 10 | public interface BookMapper { 11 | Book bookPostToBook(BookDto.Post requestBody); 12 | Book bookPatchToBook(BookDto.Patch requestBody); 13 | com.itvillage.book.v1.BookDto.Response bookToResponse(Book book); 14 | List booksToResponse(List books); 15 | } 16 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/java/com/itvillage/book/v4/BookRouter.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v4; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 6 | import org.springframework.web.reactive.function.server.RouterFunction; 7 | 8 | import javax.validation.Validator; 9 | 10 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 11 | 12 | @Configuration("bookRouterV4") 13 | public class BookRouter { 14 | @Bean 15 | public RouterFunction routeBookV4(BookHandler handler) { 16 | return route() 17 | .POST("/v4/books", handler::createBook) 18 | .PATCH("/v4/books/{book-id}", handler::updateBook) 19 | .GET("/v4/books", handler::getBooks) 20 | .GET("/v4/books/{book-id}", handler::getBook) 21 | .build(); 22 | } 23 | 24 | @Bean 25 | public Validator javaxValidator() { 26 | return new LocalValidatorFactoryBean(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /part3/chapter17/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /part3/chapter17/src/test/java/com/itvillage/Chapter17ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class Chapter17ApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part3/chapter18/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /part3/chapter18/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.2' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.itvillage' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '11' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 17 | implementation 'org.springframework.boot:spring-boot-starter-validation' 18 | implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc' 19 | compileOnly 'org.projectlombok:lombok' 20 | annotationProcessor 'org.projectlombok:lombok' 21 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 22 | testImplementation 'io.projectreactor:reactor-test' 23 | implementation 'org.mapstruct:mapstruct:1.5.1.Final' 24 | annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.1.Final' 25 | runtimeOnly 'io.r2dbc:r2dbc-h2' 26 | } 27 | 28 | tasks.named('test') { 29 | useJUnitPlatform() 30 | } 31 | -------------------------------------------------------------------------------- /part3/chapter18/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part3/chapter18/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part3/chapter18/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part3/chapter18/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'chapter18' 2 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/Chapter18Application.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.data.r2dbc.config.EnableR2dbcAuditing; 6 | import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; 7 | 8 | @EnableR2dbcRepositories 9 | @EnableR2dbcAuditing 10 | @SpringBootApplication 11 | public class Chapter18Application { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(Chapter18Application.class, args); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/config/RouterConfig.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 6 | 7 | import javax.validation.Validator; 8 | 9 | @Configuration 10 | public class RouterConfig { 11 | @Bean 12 | public Validator javaxValidator() { 13 | return new LocalValidatorFactoryBean(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/v5/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v5; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.springframework.data.annotation.CreatedDate; 8 | import org.springframework.data.annotation.Id; 9 | import org.springframework.data.annotation.LastModifiedDate; 10 | import org.springframework.data.relational.core.mapping.Column; 11 | import java.time.LocalDateTime; 12 | 13 | @Getter 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | @Setter 17 | public class Book { 18 | @Id 19 | private long bookId; 20 | private String titleKorean; 21 | private String titleEnglish; 22 | private String description; 23 | private String author; 24 | private String isbn; 25 | private String publishDate; 26 | 27 | @CreatedDate 28 | private LocalDateTime createdAt; 29 | 30 | @LastModifiedDate 31 | @Column("last_modified_at") 32 | private LocalDateTime modifiedAt; 33 | } 34 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/v5/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v5; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.MappingConstants; 5 | 6 | import java.util.List; 7 | 8 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING, 9 | implementationName = "bookMapperV5") 10 | public interface BookMapper { 11 | Book bookPostToBook(BookDto.Post requestBody); 12 | Book bookPatchToBook(BookDto.Patch requestBody); 13 | BookDto.Response bookToResponse(Book book); 14 | List booksToResponse(List books); 15 | } 16 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/v5/BookRepository.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v5; 2 | 3 | import org.springframework.data.repository.reactive.ReactiveCrudRepository; 4 | import org.springframework.stereotype.Repository; 5 | import reactor.core.publisher.Mono; 6 | 7 | @Repository("bookRepositoryV5") 8 | public interface BookRepository extends ReactiveCrudRepository { 9 | Mono findByIsbn(String isbn); 10 | } 11 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/v5/BookRouter.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v5; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.reactive.function.server.RouterFunction; 6 | 7 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 8 | 9 | @Configuration("bookRouterV5") 10 | public class BookRouter { 11 | @Bean 12 | public RouterFunction routeBookV5(BookHandler handler) { 13 | return route() 14 | .POST("/v5/books", handler::createBook) 15 | .PATCH("/v5/books/{book-id}", handler::updateBook) 16 | .GET("/v5/books", handler::getBooks) 17 | .GET("/v5/books/{book-id}", handler::getBook) 18 | .build(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/v6/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v6; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.springframework.data.annotation.CreatedDate; 8 | import org.springframework.data.annotation.Id; 9 | import org.springframework.data.annotation.LastModifiedDate; 10 | import org.springframework.data.relational.core.mapping.Column; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | @Getter 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | @Setter 18 | public class Book { 19 | @Id 20 | private long bookId; 21 | private String titleKorean; 22 | private String titleEnglish; 23 | private String description; 24 | private String author; 25 | private String isbn; 26 | private String publishDate; 27 | 28 | @CreatedDate 29 | private LocalDateTime createdAt; 30 | 31 | @LastModifiedDate 32 | @Column("last_modified_at") 33 | private LocalDateTime modifiedAt; 34 | } 35 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/v6/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v6; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.MappingConstants; 5 | 6 | import java.util.List; 7 | 8 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING, 9 | implementationName = "bookMapperV6") 10 | public interface BookMapper { 11 | Book bookPostToBook(BookDto.Post requestBody); 12 | Book bookPatchToBook(BookDto.Patch requestBody); 13 | BookDto.Response bookToResponse(Book book); 14 | List booksToResponse(List books); 15 | } 16 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/v6/BookRouter.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v6; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.reactive.function.server.RouterFunction; 6 | 7 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 8 | 9 | @Configuration("bookRouterV6") 10 | public class BookRouter { 11 | @Bean 12 | public RouterFunction routeBookV6(BookHandler handler) { 13 | return route() 14 | .POST("/v6/books", handler::createBook) 15 | .PATCH("/v6/books/{book-id}", handler::updateBook) 16 | .GET("/v6/books", handler::getBooks) 17 | .GET("/v6/books/{book-id}", handler::getBook) 18 | .build(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/v7/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v7; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.springframework.data.annotation.CreatedDate; 8 | import org.springframework.data.annotation.Id; 9 | import org.springframework.data.annotation.LastModifiedDate; 10 | import org.springframework.data.relational.core.mapping.Column; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | @Getter 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | @Setter 18 | public class Book { 19 | @Id 20 | private long bookId; 21 | private String titleKorean; 22 | private String titleEnglish; 23 | private String description; 24 | private String author; 25 | private String isbn; 26 | private String publishDate; 27 | 28 | @CreatedDate 29 | private LocalDateTime createdAt; 30 | 31 | @LastModifiedDate 32 | @Column("last_modified_at") 33 | private LocalDateTime modifiedAt; 34 | } 35 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/v7/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v7; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.MappingConstants; 5 | 6 | import java.util.List; 7 | 8 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING, 9 | implementationName = "bookMapperV7") 10 | public interface BookMapper { 11 | Book bookPostToBook(BookDto.Post requestBody); 12 | Book bookPatchToBook(BookDto.Patch requestBody); 13 | BookDto.Response bookToResponse(Book book); 14 | List booksToResponse(List books); 15 | } 16 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/v7/BookRepository.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v7; 2 | 3 | import org.springframework.data.domain.Pageable; 4 | import org.springframework.data.repository.reactive.ReactiveCrudRepository; 5 | import org.springframework.stereotype.Repository; 6 | import reactor.core.publisher.Flux; 7 | import reactor.core.publisher.Mono; 8 | 9 | @Repository("bookRepositoryV7") 10 | public interface BookRepository extends ReactiveCrudRepository { 11 | Mono findByIsbn(String isbn); 12 | Flux findAllBy(Pageable pageable); 13 | } 14 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/v7/BookRouter.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v7; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.reactive.function.server.RouterFunction; 6 | 7 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 8 | 9 | @Configuration("bookRouterV7") 10 | public class BookRouter { 11 | @Bean 12 | public RouterFunction routeBookV7(BookHandler handler) { 13 | return route() 14 | .POST("/v7/books", handler::createBook) 15 | .PATCH("/v7/books/{book-id}", handler::updateBook) 16 | .GET("/v7/books", handler::getBooks) 17 | .GET("/v7/books/{book-id}", handler::getBook) 18 | .build(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/v8/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v8; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.springframework.data.annotation.CreatedDate; 8 | import org.springframework.data.annotation.Id; 9 | import org.springframework.data.annotation.LastModifiedDate; 10 | import org.springframework.data.relational.core.mapping.Column; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | @Getter 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | @Setter 18 | public class Book { 19 | @Id 20 | private long bookId; 21 | private String titleKorean; 22 | private String titleEnglish; 23 | private String description; 24 | private String author; 25 | private String isbn; 26 | private String publishDate; 27 | 28 | @CreatedDate 29 | private LocalDateTime createdAt; 30 | 31 | @LastModifiedDate 32 | @Column("last_modified_at") 33 | private LocalDateTime modifiedAt; 34 | } 35 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/v8/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v8; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.MappingConstants; 5 | 6 | import java.util.List; 7 | 8 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING, 9 | implementationName = "bookMapperV8") 10 | public interface BookMapper { 11 | Book bookPostToBook(BookDto.Post requestBody); 12 | Book bookPatchToBook(BookDto.Patch requestBody); 13 | BookDto.Response bookToResponse(Book book); 14 | List booksToResponse(List books); 15 | } 16 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/book/v8/BookRouter.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v8; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.reactive.function.server.RouterFunction; 6 | 7 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 8 | 9 | @Configuration("bookRouterV8") 10 | public class BookRouter { 11 | @Bean 12 | public RouterFunction routeBookV8(BookHandler handler) { 13 | return route() 14 | .POST("/v8/books", handler::createBook) 15 | .PATCH("/v8/books/{book-id}", handler::updateBook) 16 | .GET("/v8/books", handler::getBooks) 17 | .GET("/v8/books/{book-id}", handler::getBook) 18 | .build(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/exception/BusinessLogicException.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.exception; 2 | 3 | import lombok.Getter; 4 | 5 | public class BusinessLogicException extends RuntimeException { 6 | @Getter 7 | private ExceptionCode exceptionCode; 8 | 9 | public BusinessLogicException(ExceptionCode exceptionCode) { 10 | super(exceptionCode.getMessage()); 11 | this.exceptionCode = exceptionCode; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/java/com/itvillage/exception/ExceptionCode.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.exception; 2 | 3 | import lombok.Getter; 4 | 5 | public enum ExceptionCode { 6 | BOOK_NOT_FOUND(404, "Book not found"), 7 | BOOK_EXISTS(409, "Book exists"); 8 | 9 | @Getter 10 | private int status; 11 | 12 | @Getter 13 | private String message; 14 | 15 | ExceptionCode(int code, String message) { 16 | this.status = code; 17 | this.message = message; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part3/chapter18/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | sql: 3 | init: 4 | schema-locations: classpath*:db/h2/schema.sql 5 | data-locations: classpath*:db/h2/data.sql 6 | logging: 7 | level: 8 | org: 9 | springframework: 10 | r2dbc: DEBUG -------------------------------------------------------------------------------- /part3/chapter18/src/main/resources/db/h2/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS BOOK ( 2 | BOOK_ID bigint NOT NULL AUTO_INCREMENT, 3 | TITLE_KOREAN varchar(100) NOT NULL, 4 | TITLE_ENGLISH varchar(100) NOT NULL, 5 | DESCRIPTION varchar(100) NOT NULL, 6 | AUTHOR varchar(100) NOT NULL, 7 | ISBN varchar(100) NOT NULL UNIQUE, 8 | PUBLISH_DATE varchar(100) NOT NULL, 9 | CREATED_AT datetime NOT NULL, 10 | LAST_MODIFIED_AT datetime NOT NULL, 11 | PRIMARY KEY (BOOK_ID) 12 | ); 13 | -------------------------------------------------------------------------------- /part3/chapter18/src/test/java/com/itvillage/Chapter18ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class Chapter18ApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part3/chapter19/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /part3/chapter19/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.2' 3 | id 'io.spring.dependency-management' version '1.0.12.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.itvillage' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '11' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 17 | implementation 'org.springframework.boot:spring-boot-starter-validation' 18 | implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc' 19 | compileOnly 'org.projectlombok:lombok' 20 | annotationProcessor 'org.projectlombok:lombok' 21 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 22 | testImplementation 'io.projectreactor:reactor-test' 23 | implementation 'org.mapstruct:mapstruct:1.5.1.Final' 24 | annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.1.Final' 25 | runtimeOnly 'io.r2dbc:r2dbc-h2' 26 | implementation 'com.google.code.gson:gson' 27 | } 28 | 29 | tasks.named('test') { 30 | useJUnitPlatform() 31 | } 32 | -------------------------------------------------------------------------------- /part3/chapter19/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part3/chapter19/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part3/chapter19/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part3/chapter19/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'chapter19' 2 | -------------------------------------------------------------------------------- /part3/chapter19/src/main/java/com/itvillage/Chapter19Application.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.data.r2dbc.config.EnableR2dbcAuditing; 6 | import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; 7 | 8 | @EnableR2dbcRepositories 9 | @EnableR2dbcAuditing 10 | @SpringBootApplication 11 | public class Chapter19Application { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(Chapter19Application.class, args); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /part3/chapter19/src/main/java/com/itvillage/book/config/RouterConfig.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.config; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 7 | 8 | import javax.validation.Validator; 9 | 10 | @Configuration 11 | public class RouterConfig { 12 | @Bean 13 | public Validator javaxValidator() { 14 | return new LocalValidatorFactoryBean(); 15 | } 16 | 17 | @Bean 18 | public ObjectMapper objectMapper() { 19 | return new ObjectMapper(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part3/chapter19/src/main/java/com/itvillage/book/v10/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v10; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.springframework.data.annotation.CreatedDate; 8 | import org.springframework.data.annotation.Id; 9 | import org.springframework.data.annotation.LastModifiedDate; 10 | import org.springframework.data.relational.core.mapping.Column; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | @Getter 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | @Setter 18 | public class Book { 19 | @Id 20 | private long bookId; 21 | private String titleKorean; 22 | private String titleEnglish; 23 | private String description; 24 | private String author; 25 | private String isbn; 26 | private String publishDate; 27 | 28 | @CreatedDate 29 | private LocalDateTime createdAt; 30 | 31 | @LastModifiedDate 32 | @Column("last_modified_at") 33 | private LocalDateTime modifiedAt; 34 | } 35 | -------------------------------------------------------------------------------- /part3/chapter19/src/main/java/com/itvillage/book/v10/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v10; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.MappingConstants; 5 | 6 | import java.util.List; 7 | 8 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING, 9 | implementationName = "bookMapperV10") 10 | public interface BookMapper { 11 | Book bookPostToBook(BookDto.Post requestBody); 12 | Book bookPatchToBook(BookDto.Patch requestBody); 13 | BookDto.Response bookToResponse(Book book); 14 | List booksToResponse(List books); 15 | } 16 | -------------------------------------------------------------------------------- /part3/chapter19/src/main/java/com/itvillage/book/v10/BookRouter.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v10; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.reactive.function.server.RouterFunction; 6 | 7 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 8 | 9 | @Configuration("bookRouterV10") 10 | public class BookRouter { 11 | @Bean 12 | public RouterFunction routeBookV10(BookHandler handler) { 13 | return route() 14 | .POST("/v10/books", handler::createBook) 15 | .PATCH("/v10/books/{book-id}", handler::updateBook) 16 | .GET("/v10/books", handler::getBooks) 17 | .GET("/v10/books/{book-id}", handler::getBook) 18 | .build(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /part3/chapter19/src/main/java/com/itvillage/book/v10/ErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v10; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class ErrorResponse { 7 | private int status; 8 | private String message; 9 | 10 | private ErrorResponse(int status, String message) { 11 | this.status = status; 12 | this.message = message; 13 | } 14 | 15 | public static ErrorResponse of(int status, String message) { 16 | return new ErrorResponse(status, message); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /part3/chapter19/src/main/java/com/itvillage/book/v9/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v9; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.springframework.data.annotation.CreatedDate; 8 | import org.springframework.data.annotation.Id; 9 | import org.springframework.data.annotation.LastModifiedDate; 10 | import org.springframework.data.relational.core.mapping.Column; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | @Getter 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | @Setter 18 | public class Book { 19 | @Id 20 | private long bookId; 21 | private String titleKorean; 22 | private String titleEnglish; 23 | private String description; 24 | private String author; 25 | private String isbn; 26 | private String publishDate; 27 | 28 | @CreatedDate 29 | private LocalDateTime createdAt; 30 | 31 | @LastModifiedDate 32 | @Column("last_modified_at") 33 | private LocalDateTime modifiedAt; 34 | } 35 | -------------------------------------------------------------------------------- /part3/chapter19/src/main/java/com/itvillage/book/v9/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v9; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.MappingConstants; 5 | 6 | import java.util.List; 7 | 8 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING, 9 | implementationName = "bookMapperV9") 10 | public interface BookMapper { 11 | Book bookPostToBook(BookDto.Post requestBody); 12 | Book bookPatchToBook(BookDto.Patch requestBody); 13 | BookDto.Response bookToResponse(Book book); 14 | List booksToResponse(List books); 15 | } 16 | -------------------------------------------------------------------------------- /part3/chapter19/src/main/java/com/itvillage/book/v9/BookRouter.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v9; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.reactive.function.server.RouterFunction; 6 | 7 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 8 | 9 | @Configuration("bookRouterV9") 10 | public class BookRouter { 11 | @Bean 12 | public RouterFunction routeBookV9(BookHandler handler) { 13 | return route() 14 | .POST("/v9/books", handler::createBook) 15 | .PATCH("/v9/books/{book-id}", handler::updateBook) 16 | .GET("/v9/books", handler::getBooks) 17 | .GET("/v9/books/{book-id}", handler::getBook) 18 | .build(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /part3/chapter19/src/main/java/com/itvillage/book/v9/ErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.book.v9; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import org.springframework.http.HttpStatus; 6 | 7 | @Getter 8 | @AllArgsConstructor 9 | public class ErrorResponse { 10 | private HttpStatus status; 11 | private String message; 12 | } 13 | -------------------------------------------------------------------------------- /part3/chapter19/src/main/java/com/itvillage/exception/BusinessLogicException.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.exception; 2 | 3 | import lombok.Getter; 4 | 5 | public class BusinessLogicException extends RuntimeException { 6 | @Getter 7 | private ExceptionCode exceptionCode; 8 | 9 | public BusinessLogicException(ExceptionCode exceptionCode) { 10 | super(exceptionCode.getMessage()); 11 | this.exceptionCode = exceptionCode; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /part3/chapter19/src/main/java/com/itvillage/exception/ExceptionCode.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.exception; 2 | 3 | import lombok.Getter; 4 | 5 | public enum ExceptionCode { 6 | BOOK_NOT_FOUND(404, "Book not found"), 7 | BOOK_EXISTS(409, "Book exists"); 8 | 9 | @Getter 10 | private int status; 11 | 12 | @Getter 13 | private String message; 14 | 15 | ExceptionCode(int code, String message) { 16 | this.status = code; 17 | this.message = message; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part3/chapter19/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | sql: 3 | init: 4 | schema-locations: classpath*:db/h2/schema.sql 5 | data-locations: classpath*:db/h2/data.sql 6 | logging: 7 | level: 8 | org: 9 | springframework: 10 | r2dbc: DEBUG -------------------------------------------------------------------------------- /part3/chapter19/src/main/resources/db/h2/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS BOOK ( 2 | BOOK_ID bigint NOT NULL AUTO_INCREMENT, 3 | TITLE_KOREAN varchar(100) NOT NULL, 4 | TITLE_ENGLISH varchar(100) NOT NULL, 5 | DESCRIPTION varchar(100) NOT NULL, 6 | AUTHOR varchar(100) NOT NULL, 7 | ISBN varchar(100) NOT NULL UNIQUE, 8 | PUBLISH_DATE varchar(100) NOT NULL, 9 | CREATED_AT datetime NOT NULL, 10 | LAST_MODIFIED_AT datetime NOT NULL, 11 | PRIMARY KEY (BOOK_ID) 12 | ); 13 | -------------------------------------------------------------------------------- /part3/chapter19/src/test/java/com/itvillage/Chapter19ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class Chapter19ApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part3/chapter20/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /part3/chapter20/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part3/chapter20/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part3/chapter20/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part3/chapter20/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'chapter20' 2 | -------------------------------------------------------------------------------- /part3/chapter20/src/main/java/com/itvillage/Chapter20Application.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.data.r2dbc.config.EnableR2dbcAuditing; 6 | import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; 7 | 8 | 9 | @EnableR2dbcRepositories 10 | @EnableR2dbcAuditing 11 | @SpringBootApplication 12 | public class Chapter20Application { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(Chapter20Application.class, args); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /part3/chapter20/src/main/java/com/itvillage/config/RouterConfig.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.config; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 7 | 8 | import javax.validation.Validator; 9 | 10 | @Configuration 11 | public class RouterConfig { 12 | @Bean 13 | public Validator javaxValidator() { 14 | return new LocalValidatorFactoryBean(); 15 | } 16 | 17 | @Bean 18 | public ObjectMapper objectMapper() { 19 | return new ObjectMapper(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part3/chapter20/src/main/java/com/itvillage/exception/BusinessLogicException.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.exception; 2 | 3 | import lombok.Getter; 4 | 5 | public class BusinessLogicException extends RuntimeException { 6 | @Getter 7 | private ExceptionCode exceptionCode; 8 | 9 | public BusinessLogicException(ExceptionCode exceptionCode) { 10 | super(exceptionCode.getMessage()); 11 | this.exceptionCode = exceptionCode; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /part3/chapter20/src/main/java/com/itvillage/exception/ExceptionCode.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.exception; 2 | 3 | import lombok.Getter; 4 | 5 | public enum ExceptionCode { 6 | BOOK_NOT_FOUND(404, "Book not found"), 7 | BOOK_EXISTS(409, "Book exists"); 8 | 9 | @Getter 10 | private int status; 11 | 12 | @Getter 13 | private String message; 14 | 15 | ExceptionCode(int code, String message) { 16 | this.status = code; 17 | this.message = message; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part3/chapter20/src/main/java/com/itvillage/v10/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.v10; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.springframework.data.annotation.CreatedDate; 8 | import org.springframework.data.annotation.Id; 9 | import org.springframework.data.annotation.LastModifiedDate; 10 | import org.springframework.data.relational.core.mapping.Column; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | @Getter 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | @Setter 18 | public class Book { 19 | @Id 20 | private long bookId; 21 | private String titleKorean; 22 | private String titleEnglish; 23 | private String description; 24 | private String author; 25 | private String isbn; 26 | private String publishDate; 27 | 28 | @CreatedDate 29 | private LocalDateTime createdAt; 30 | 31 | @LastModifiedDate 32 | @Column("last_modified_at") 33 | private LocalDateTime modifiedAt; 34 | } 35 | -------------------------------------------------------------------------------- /part3/chapter20/src/main/java/com/itvillage/v10/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.v10; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.MappingConstants; 5 | 6 | import java.util.List; 7 | 8 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING, 9 | implementationName = "bookMapperV10") 10 | public interface BookMapper { 11 | Book bookPostToBook(BookDto.Post requestBody); 12 | Book bookPatchToBook(BookDto.Patch requestBody); 13 | BookDto.Response bookToResponse(Book book); 14 | List booksToResponse(List books); 15 | } 16 | -------------------------------------------------------------------------------- /part3/chapter20/src/main/java/com/itvillage/v10/BookRouter.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.v10; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.reactive.function.server.RouterFunction; 6 | 7 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 8 | 9 | @Configuration("bookRouterV10") 10 | public class BookRouter { 11 | @Bean 12 | public RouterFunction routeBookV10(BookHandler handler) { 13 | return route() 14 | .POST("/v10/books", handler::createBook) 15 | .PATCH("/v10/books/{book-id}", handler::updateBook) 16 | .GET("/v10/books", handler::getBooks) 17 | .GET("/v10/books/{book-id}", handler::getBook) 18 | .build(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /part3/chapter20/src/main/java/com/itvillage/v10/ErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.v10; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class ErrorResponse { 7 | private int status; 8 | private String message; 9 | 10 | private ErrorResponse(int status, String message) { 11 | this.status = status; 12 | this.message = message; 13 | } 14 | 15 | public static ErrorResponse of(int status, String message) { 16 | return new ErrorResponse(status, message); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /part3/chapter20/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | sql: 3 | init: 4 | schema-locations: classpath*:db/h2/schema.sql 5 | data-locations: classpath*:db/h2/data.sql 6 | logging: 7 | level: 8 | org: 9 | springframework: 10 | r2dbc: DEBUG -------------------------------------------------------------------------------- /part3/chapter20/src/main/resources/db/h2/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS BOOK ( 2 | BOOK_ID bigint NOT NULL AUTO_INCREMENT, 3 | TITLE_KOREAN varchar(100) NOT NULL, 4 | TITLE_ENGLISH varchar(100) NOT NULL, 5 | DESCRIPTION varchar(100) NOT NULL, 6 | AUTHOR varchar(100) NOT NULL, 7 | ISBN varchar(100) NOT NULL UNIQUE, 8 | PUBLISH_DATE varchar(100) NOT NULL, 9 | CREATED_AT datetime NOT NULL, 10 | LAST_MODIFIED_AT datetime NOT NULL, 11 | PRIMARY KEY (BOOK_ID) 12 | ); 13 | 14 | CREATE TABLE IF NOT EXISTS SPEED_LIMIT_ENFORCEMENT ( 15 | ID bigint NOT NULL AUTO_INCREMENT, 16 | CAR_NUMBER varchar(30) NOT NULL, 17 | SHOT_SPEED smallint NOT NULL, 18 | SHOT_AT datetime NOT NULL, 19 | CAMERA_ID smallint NOT NULL, 20 | SPEED_LIMIT smallint NOT NULL, 21 | CREATED_AT datetime NOT NULL, 22 | LAST_MODIFIED_AT datetime NOT NULL, 23 | PRIMARY KEY (ID) 24 | ); -------------------------------------------------------------------------------- /part3/chapter20/src/test/java/com/itvillage/Chapter20ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class Chapter20ApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part3/chapter21/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /part3/chapter21/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part3/chapter21/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part3/chapter21/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part3/chapter21/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'chapter21' 2 | -------------------------------------------------------------------------------- /part3/chapter21/src/main/java/com/itvillage/Chapter21Application.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.data.r2dbc.config.EnableR2dbcAuditing; 6 | import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; 7 | 8 | @EnableR2dbcRepositories 9 | @EnableR2dbcAuditing 10 | @SpringBootApplication 11 | public class Chapter21Application { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(Chapter21Application.class, args); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /part3/chapter21/src/main/java/com/itvillage/config/RouterConfig.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.config; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 7 | 8 | import javax.validation.Validator; 9 | 10 | @Configuration 11 | public class RouterConfig { 12 | @Bean 13 | public Validator javaxValidator() { 14 | return new LocalValidatorFactoryBean(); 15 | } 16 | 17 | @Bean 18 | public ObjectMapper objectMapper() { 19 | return new ObjectMapper(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part3/chapter21/src/main/java/com/itvillage/exception/BusinessLogicException.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.exception; 2 | 3 | import lombok.Getter; 4 | 5 | public class BusinessLogicException extends RuntimeException { 6 | @Getter 7 | private ExceptionCode exceptionCode; 8 | 9 | public BusinessLogicException(ExceptionCode exceptionCode) { 10 | super(exceptionCode.getMessage()); 11 | this.exceptionCode = exceptionCode; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /part3/chapter21/src/main/java/com/itvillage/exception/ExceptionCode.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.exception; 2 | 3 | import lombok.Getter; 4 | 5 | public enum ExceptionCode { 6 | BOOK_NOT_FOUND(404, "Book not found"), 7 | BOOK_EXISTS(409, "Book exists"); 8 | 9 | @Getter 10 | private int status; 11 | 12 | @Getter 13 | private String message; 14 | 15 | ExceptionCode(int code, String message) { 16 | this.status = code; 17 | this.message = message; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /part3/chapter21/src/main/java/com/itvillage/v11/Book.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.v11; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.springframework.data.annotation.CreatedDate; 8 | import org.springframework.data.annotation.Id; 9 | import org.springframework.data.annotation.LastModifiedDate; 10 | import org.springframework.data.relational.core.mapping.Column; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | @Getter 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | @Setter 18 | public class Book { 19 | @Id 20 | private long bookId; 21 | private String titleKorean; 22 | private String titleEnglish; 23 | private String description; 24 | private String author; 25 | private String isbn; 26 | private String publishDate; 27 | 28 | @CreatedDate 29 | private LocalDateTime createdAt; 30 | 31 | @LastModifiedDate 32 | @Column("last_modified_at") 33 | private LocalDateTime modifiedAt; 34 | } 35 | -------------------------------------------------------------------------------- /part3/chapter21/src/main/java/com/itvillage/v11/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.v11; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.MappingConstants; 5 | 6 | import java.util.List; 7 | 8 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING) 9 | public interface BookMapper { 10 | Book bookPostToBook(BookDto.Post requestBody); 11 | Book bookPatchToBook(BookDto.Patch requestBody); 12 | BookDto.Response bookToResponse(Book book); 13 | List booksToResponse(List books); 14 | } 15 | -------------------------------------------------------------------------------- /part3/chapter21/src/main/java/com/itvillage/v11/ErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.itvillage.v11; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class ErrorResponse { 7 | private int status; 8 | private String message; 9 | 10 | private ErrorResponse(int status, String message) { 11 | this.status = status; 12 | this.message = message; 13 | } 14 | 15 | public static ErrorResponse of(int status, String message) { 16 | return new ErrorResponse(status, message); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /part3/chapter21/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | sql: 3 | init: 4 | schema-locations: classpath*:db/h2/schema.sql 5 | data-locations: classpath*:db/h2/data.sql 6 | logging: 7 | level: 8 | org: 9 | springframework: 10 | r2dbc: DEBUG 11 | pattern: 12 | console: "%d{HH:mm:ss.SSS} [%thread] %-5level- %msg%n" -------------------------------------------------------------------------------- /part3/chapter21/src/main/resources/db/h2/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS BOOK ( 2 | BOOK_ID bigint NOT NULL AUTO_INCREMENT, 3 | TITLE_KOREAN varchar(100) NOT NULL, 4 | TITLE_ENGLISH varchar(100) NOT NULL, 5 | DESCRIPTION varchar(100) NOT NULL, 6 | AUTHOR varchar(100) NOT NULL, 7 | ISBN varchar(100) NOT NULL UNIQUE, 8 | PUBLISH_DATE varchar(100) NOT NULL, 9 | CREATED_AT datetime NOT NULL, 10 | LAST_MODIFIED_AT datetime NOT NULL, 11 | PRIMARY KEY (BOOK_ID) 12 | ); 13 | 14 | CREATE TABLE IF NOT EXISTS SPEED_LIMIT_ENFORCEMENT ( 15 | ID bigint NOT NULL AUTO_INCREMENT, 16 | CAR_NUMBER varchar(30) NOT NULL, 17 | SHOT_SPEED smallint NOT NULL, 18 | SHOT_AT datetime NOT NULL, 19 | CAMERA_ID smallint NOT NULL, 20 | SPEED_LIMIT smallint NOT NULL, 21 | CREATED_AT datetime NOT NULL, 22 | LAST_MODIFIED_AT datetime NOT NULL, 23 | PRIMARY KEY (ID) 24 | ); -------------------------------------------------------------------------------- /part3/chapter21/src/test/java/com/itvillage/Chapter21ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.itvillage; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class Chapter21ApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part3/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjpublic/Spring-Reactive/17544144fd396c4957483a4c42c2eeaa2cbd86f2/part3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part3/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part3/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'part3' 2 | 3 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'bjpublic' 2 | 3 | --------------------------------------------------------------------------------