├── .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 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
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 |
--------------------------------------------------------------------------------