This article will focus on implementing an annotation based Spring WebFlux reactive programming example. Introductory concepts around reactive programming and Spring WebFlux is explained in Reactive Programming with Spring WebFlux.
LoanInfoService example
Spring Boot dependencies
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.22</version> <scope>provided</scope> </dependency> </dependencies>
Configure application.properties
spring.data.mongodb.host=localhost spring.data.mongodb.port=27017 spring.data.mongodb.database=loans
Define the data object
package com.stackstalk; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor @Document public class LoanInfo { @Id private String loanId; private String loanType; private Float interestRate; private Integer maxTermInMonths; private Float processingFee; }
Define the Controller
package com.stackstalk; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @RestController public class LoanInfoController { @Autowired private LoanInfoService loanInfoService; @GetMapping("/loans") public Flux<LoanInfo> getLoans() { return loanInfoService.getAllLoans().log(); } @PostMapping("/loans") public Mono<LoanInfo> addLoan(@RequestBody LoanInfo loanInfo) { return loanInfoService.addLoanInfo(loanInfo).log(); } @PutMapping("/loans") public Mono<LoanInfo> updateLoan(@RequestBody LoanInfo loanInfo) { return loanInfoService.updateLoadInfo(loanInfo).log(); } @DeleteMapping("/loans/{loanId}") public Mono<Void> deleteLoan(@PathVariable String loanId) { return loanInfoService.deleteLoanInfo(loanId).log(); } }
Define the repository
package com.stackstalk; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; public interface LoanInfoRepository extends ReactiveMongoRepository<LoanInfo, String> { }
Define the service
package com.stackstalk; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @Service public class LoanInfoService { @Autowired private LoanInfoRepository loanRepository; public Mono<LoanInfo> addLoanInfo(LoanInfo loadInfo) { return loanRepository.save(loadInfo); } public Flux<LoanInfo> getAllLoans() { return loanRepository.findAll(); } public Mono<LoanInfo> updateLoadInfo(LoanInfo loanInfo) { return loanRepository.save(loanInfo); } public Mono<Void> deleteLoanInfo(String loanId) { return loanRepository.deleteById(loanId); } }
Define the application
package com.stackstalk; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class LoanInfoServiceApplication { public static void main(String[] args) { SpringApplication.run(LoanInfoServiceApplication.class, args); } }
Putting it all together and testing
Now, let us test the application and examine. On starting the application observe that Netty server is being started on port 8080. Also see the usage of reactive MongoDB repository./\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ [32m :: Spring Boot :: [39m [2m (v2.6.2)[0;39m [2m2022-01-17 18:41:14.149[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.stackstalk.LoanInfoServiceApplication [0;39m [2m:[0;39m Starting LoanInfoServiceApplication using Java 16.0.2 on MYHOST-M-ABCD with PID 13172 (/Users/itsme/Documents/workspace-spring-tool-suite-4-4.12.0.RELEASE/BookInfoService/target/classes started by itsme in /Users/itsme/Documents/workspace-spring-tool-suite-4-4.12.0.RELEASE/LoanInfoService) [2m2022-01-17 18:41:14.151[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.stackstalk.LoanInfoServiceApplication [0;39m [2m:[0;39m No active profile set, falling back to default profiles: default [2m2022-01-17 18:41:14.565[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ main][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Bootstrapping Spring Data Reactive MongoDB repositories in DEFAULT mode. [2m2022-01-17 18:41:14.689[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ main][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Finished Spring Data repository scanning in 119 ms. Found 1 Reactive MongoDB repository interfaces. [2m2022-01-17 18:41:15.136[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ main][0;39m [36morg.mongodb.driver.cluster [0;39m [2m:[0;39m Cluster created with settings {hosts=[localhost:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms'} [2m2022-01-17 18:41:15.492[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[localhost:27017][0;39m [36morg.mongodb.driver.connection [0;39m [2m:[0;39m Opened connection [connectionId{localValue:2, serverValue:96}] to localhost:27017 [2m2022-01-17 18:41:15.492[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[localhost:27017][0;39m [36morg.mongodb.driver.connection [0;39m [2m:[0;39m Opened connection [connectionId{localValue:1, serverValue:95}] to localhost:27017 [2m2022-01-17 18:41:15.492[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[localhost:27017][0;39m [36morg.mongodb.driver.cluster [0;39m [2m:[0;39m Monitor thread successfully connected to server with description ServerDescription{address=localhost:27017, type=STANDALONE, state=CONNECTED, ok=true, minWireVersion=0, maxWireVersion=7, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=235134928} [2m2022-01-17 18:41:15.667[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ main][0;39m [36mo.s.b.web.embedded.netty.NettyWebServer [0;39m [2m:[0;39m Netty started on port 8080 [2m2022-01-17 18:41:15.675[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ main][0;39m [36mc.stackstalk.LoanInfoServiceApplication [0;39m [2m:[0;39m Started LoanInfoServiceApplication in 1.848 seconds (JVM running for 2.549)
Add a loan type with POST on /loans
This API is used to add a new loan type.curl --location --request POST 'http://localhost:8080/loans' \ > --header 'Content-Type: application/json' \ > --data-raw '{ > "loanId": "4", > "loanType": "Education Loan", > "interestRate": 5.5, > "maxTermInMonths": 60, > "processingFee": 20 > }'Returns the loan type added.
{"loanId":"4","loanType":"Education Loan","interestRate":5.5,"maxTermInMonths":60,"processingFee":20.0}In the logs observe usage of reactive signals on Mono with onSubscribe, request, onNext (once) and onComplete.
[2m2022-01-17 18:45:38.648[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ctor-http-nio-2][0;39m [36mreactor.Mono.UsingWhen.1 [0;39m [2m:[0;39m onSubscribe(MonoUsingWhen.MonoUsingWhenSubscriber) [2m2022-01-17 18:45:38.650[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ctor-http-nio-2][0;39m [36mreactor.Mono.UsingWhen.1 [0;39m [2m:[0;39m request(unbounded) [2m2022-01-17 18:45:38.728[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ntLoopGroup-3-3][0;39m [36morg.mongodb.driver.connection [0;39m [2m:[0;39m Opened connection [connectionId{localValue:3, serverValue:97}] to localhost:27017 [2m2022-01-17 18:45:38.771[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ntLoopGroup-3-3][0;39m [36mreactor.Mono.UsingWhen.1 [0;39m [2m:[0;39m onNext(LoanInfo(loanId=4, loanType=Education Loan, interestRate=5.5, maxTermInMonths=60, processingFee=20.0)) [2m2022-01-17 18:45:38.777[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ntLoopGroup-3-3][0;39m [36mreactor.Mono.UsingWhen.1 [0;39m [2m:[0;39m onComplete()
Query all loan types using GET on /loans
This API is used to query all added loan types.curl --location --request GET 'http://localhost:8080/loans'Returns the loan type added.
[{"loanId":"3","loanType":"Education Loan","interestRate":5.6,"maxTermInMonths":60,"processingFee":20.0}, {"loanId":"4","loanType":"Education Loan","interestRate":5.5,"maxTermInMonths":60,"processingFee":20.0}]In the logs observe usage of reactive signals on Flux with onSubscribe, request, onNext (multiple) and onComplete.
[2m2022-01-17 18:51:11.830[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ctor-http-nio-5][0;39m [36mreactor.Flux.UsingWhen.3 [0;39m [2m:[0;39m onSubscribe(FluxUsingWhen.UsingWhenSubscriber) [2m2022-01-17 18:51:11.830[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ctor-http-nio-5][0;39m [36mreactor.Flux.UsingWhen.3 [0;39m [2m:[0;39m request(unbounded) [2m2022-01-17 18:51:11.832[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ntLoopGroup-3-3][0;39m [36mreactor.Flux.UsingWhen.3 [0;39m [2m:[0;39m onNext(LoanInfo(loanId=2, loanType=Education Loan, interestRate=5.6, maxTermInMonths=60, processingFee=20.0)) [2m2022-01-17 18:51:11.833[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ntLoopGroup-3-3][0;39m [36mreactor.Flux.UsingWhen.3 [0;39m [2m:[0;39m onNext(LoanInfo(loanId=3, loanType=Education Loan, interestRate=5.5, maxTermInMonths=60, processingFee=20.0)) [2m2022-01-17 18:51:11.833[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ntLoopGroup-3-3][0;39m [36mreactor.Flux.UsingWhen.3 [0;39m [2m:[0;39m onNext(LoanInfo(loanId=4, loanType=Education Loan, interestRate=5.5, maxTermInMonths=60, processingFee=20.0)) [2m2022-01-17 18:51:11.833[0;39m [32m INFO[0;39m [35m13172[0;39m [2m---[0;39m [2m[ntLoopGroup-3-3][0;39m [36mreactor.Flux.UsingWhen.3 [0;39m [2m:[0;39m onComplete()Get access to the full source code of this example in GitHub.
0 comments:
Post a Comment