Spring Boot 2.1.5(25)---SpringBoot基于WebFlux注解

Spring 5通过引入一个名为Spring WebFlux的全新反应框架,采用了反应式编程范式。

Spring WebFlux是一个自下而上的异步框架。它可以使用Servlet 3.1非阻塞IO API以及其他异步运行时环境(如netty或afow)在Servlet容器上运行。

它将与Spring MVC一起使用。是的,Spring MVC不会去任何地方。它是开发人员长期使用的流行Web框架。

但是您现在可以在新的反应框架和传统的Spring MVC之间进行选择。您可以根据使用情况选择使用其中任何一种。

WebFlux解析实战

小编学习的途径是先直接到官网看看,于是看到了最明显的区别就是下图

图先放这里,先不着急,先看下,有一个印象,后面会说到。

SpringBoot2.0新特性

  • 编程语言Java8+,和当前火爆的Kotlin
  • 底层框架Spring Framwork 5.0.x
  • 全新特性Web Flux(小编认为我们学习SpringBoot2.0就是学习这个)

我们分析下2.0的新特性为什么编程语言要从Java8开始呢?

  • 一个重要的原因就是使用Java8的Lambda表达式和Stream流处理
  • 包括Spring Framwork 5.0.x也是要用到Java8的新特性.
  • SpringBoot1.0是仅支持Servlet Containers->Servlet API属于传统方式
  • SpringBoot2.0在支持1.0的特性上,同时添加了一个新特性就是WebFlux,可以使用Netty及Servlet3.1作为容器,基于 Reactive Streams 流处理。

那么我们在分析Servlet3.0之前和3.0的区别?

  • 3.0之前Servlet 线程会一直阻塞,只有当业务处理完成并返回后时结束 Servlet线程。
  • 3.0规范其中一个新特性是异步处理支持,即是在接收到请求之后,Servlet 线程可以将耗时的操作委派给另一个线程来完成,在不生成响应的情况下返回至容器

举一个例子

  • 那么当有200个线程同时并发在处理,那么当来201个请求的时候,就已经处理不了,因为所有的线程都阻塞了。这是3.0之前的处理情况
  • 而3.0之后异步处理是怎样处理呢?学过Netty通信框架的同学会比较容易理解一点,Servlet3.0类似于Netty一样就一个boss线程池和work线程池,boss线程只负责接收请求,work线程只负责处理逻辑。那么servlet3.0规范中,这200个线程只负责接收请求,然后每个线程将收到的请求,转发到work线程去处理。因为这200个线程只负责接收请求,并不负责处理逻辑,故不会被阻塞,而影响通信,就算处理非常耗时,也只是对work线程形成阻塞,所以当再来请求,同样可以处理,其主要应用场景是针对业务处理较耗时的情况可以减少服务器资源的占用,并且提高并发处理速度。

Spring WebFlux Functional

Spring Framework 5.0支持完全异步和非阻塞的WebFlux,并且不需要Servlet API(与Spring MVC不同)。
Spring WebFlux支持2种不同的编程模型:

  • 基于注释的@Controller
  • 功能与Java 8 lambda风格

在本教程中,我们将介绍带有Functional的 WebFlux 。
对于从WebFlux开始,SpringBoot支持集合依赖:spring-boot-starter-webflux。

使用Spring WebFlux Functional,我们使用{ HandlerFunctions,RouterFunctions}来开发。
1. HandlerFunctions
HandlerFunctions将处理传入的HTTP请求ServerRequest,并返回一个Mono

@Component
public class CustomerHandler {...public Mono<ServerResponse> getAll(ServerRequest request) {...return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(customers, Customer.class);}...public Mono<ServerResponse> putCustomer(ServerRequest request) {...return responseMono.flatMap(cust -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(fromObject(cust)));}...public Mono<ServerResponse> deleteCustomer(ServerRequest request) {...return responseMono.flatMap(strMono -> ServerResponse.ok().contentType(MediaType.TEXT_PLAIN).body(fromObject(strMono)));}
}

2. RouterFunction
RouterFunction处理所有传入的请求。需要一个ServerRequest,并返回一个。如果请求与特定路由匹配,则返回处理函数; 否则它返回一个空的Mono。Mono

@Configuration
public class RoutingConfiguration {@Beanpublic RouterFunction<ServerResponse> monoRouterFunction(CustomerHandler customerHandler) {return route(GET("/api/customer").and(accept(MediaType.APPLICATION_JSON)), customerHandler::getAll).andRoute(GET("/api/customer/{id}").and(accept(MediaType.APPLICATION_JSON)), customerHandler::getCustomer).andRoute(POST("/api/customer/post").and(accept(MediaType.APPLICATION_JSON)), customerHandler::postCustomer).andRoute(PUT("/api/customer/put/{id}").and(accept(MediaType.APPLICATION_JSON)), customerHandler::putCustomer).andRoute(DELETE("/api/customer/delete/{id}").and(accept(MediaType.APPLICATION_JSON)), customerHandler::deleteCustomer);}}

好了,当你已经读到这里,相信已经对SpringBoot1.0和SpringBoot2.0有一个比较清晰的认识了(当用过Netty通信框架类的童鞋一定是非常清晰的,如果还不清晰,就要补补课了),所以我们不得不说SpringBoot2.0的性能一定是比1.0有所提升的。不过各有所爱,企业具体技术选型还要看业务需求,不能盲目追求新技术,毕竟新技术还不太稳定,没有被大规模的实践。好了,理论的知识就先讲到这里,开始实战编码吧。

项目实战

在本教程中,我们创建一个SpringBoot项目,如下所示:

步骤:

  • 创建SpringBoot项目
  • 创建数据模型
  • 实现存储库
  • 实现Spring WebFlux API
  • 运行并检查结果

1.创建SpringBoot项目

使用SpringToolSuite,创建一个具有Reactive Web依赖关系的SpringBoot项目:

创建后检查pom.xml:

<dependencies><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>
</dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins>
</build><repositories><repository><id>spring-snapshots</id><name>Spring Snapshots</name><url>https://repo.spring.io/snapshot</url><snapshots><enabled>true</enabled></snapshots></repository><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository>
</repositories><pluginRepositories><pluginRepository><id>spring-snapshots</id><name>Spring Snapshots</name><url>https://repo.spring.io/snapshot</url><snapshots><enabled>true</enabled></snapshots></pluginRepository><pluginRepository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></pluginRepository>
</pluginRepositories>

2.创建数据模型

创建客户数据模型:

package com.javasampleapproach.webflux.model;public class Customer {private long custId;private String firstname;private String lastname;private int age;public Customer(){}public Customer(long custId, String firstname, String lastname, int age){this.custId = custId;this.firstname = firstname;this.lastname = lastname;this.age = age;}public long getCustId() {return custId;}public void setCustId(Long custId) {this.custId = custId;}public String getFirstname() {return firstname;}public void setFirstname(String firstname) {this.firstname = firstname;}public String getLastname() {return lastname;}public void setLastname(String lastname) {this.lastname = lastname;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {String info = String.format("custId = %d, firstname = %s, lastname = %s, age = %d", custId, firstname, lastname, age);return info;}
}

3.创建存储库

3.1定义接口CustomerRepository


package com.javasampleapproach.webflux.repo;import com.javasampleapproach.webflux.model.Customer;import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;public interface CustomerRepository {public Mono<Customer> getCustomerById(Long id);public Flux<Customer> getAllCustomers();public Mono<Void> saveCustomer(Mono<Customer> customer);public Mono<Customer> putCustomer(Long id, Mono<Customer> customer);public Mono<String> deleteCustomer(Long id);
}

3.2创建CustomerRepository

package com.javasampleapproach.webflux.repo.impl;import java.util.HashMap;
import java.util.Map;import javax.annotation.PostConstruct;import org.springframework.stereotype.Repository;import com.javasampleapproach.webflux.model.Customer;
import com.javasampleapproach.webflux.repo.CustomerRepository;import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;@Repository
public class CustomerRepositoryImpl implements CustomerRepository{private Map<Long, Customer> custStores = new HashMap<Long, Customer>();@PostConstructpublic void initIt() throws Exception {custStores.put(Long.valueOf(1), new Customer(1, "Jack", "Smith", 20));custStores.put(Long.valueOf(2), new Customer(2, "Peter", "Johnson", 25));}@Overridepublic Mono<Customer> getCustomerById(Long id) {return Mono.just(custStores.get(id));}@Overridepublic Flux<Customer> getAllCustomers() {return Flux.fromIterable(this.custStores.values());}@Overridepublic Mono<Void> saveCustomer(Mono<Customer> monoCustomer) {Mono<Customer> customerMono =  monoCustomer.doOnNext(customer -> {// do postcustStores.put(customer.getCustId(), customer);// log on consoleSystem.out.println("########### POST:" + customer);});return customerMono.then();}@Overridepublic Mono<Customer> putCustomer(Long id, Mono<Customer> monoCustomer) {Mono<Customer> customerMono =  monoCustomer.doOnNext(customer -> {// reset customer.Idcustomer.setCustId(id);// do putcustStores.put(id, customer);// log on consoleSystem.out.println("########### PUT:" + customer);});return customerMono;}@Overridepublic Mono<String> deleteCustomer(Long id) {// delete processingcustStores.remove(id);return Mono.just("Delete Succesfully!");}
}

4.实现Spring WebFlux API

4.1 RouterFunction

package com.javasampleapproach.webflux.functional.router;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;import com.javasampleapproach.webflux.functional.handler.CustomerHandler;import static org.springframework.web.reactive.function.server.RequestPredicates.*;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;import org.springframework.http.MediaType;@Configuration
public class RoutingConfiguration {@Beanpublic RouterFunction<ServerResponse> monoRouterFunction(CustomerHandler customerHandler) {return route(GET("/api/customer").and(accept(MediaType.APPLICATION_JSON)), customerHandler::getAll).andRoute(GET("/api/customer/{id}").and(accept(MediaType.APPLICATION_JSON)), customerHandler::getCustomer).andRoute(POST("/api/customer/post").and(accept(MediaType.APPLICATION_JSON)), customerHandler::postCustomer).andRoute(PUT("/api/customer/put/{id}").and(accept(MediaType.APPLICATION_JSON)), customerHandler::putCustomer).andRoute(DELETE("/api/customer/delete/{id}").and(accept(MediaType.APPLICATION_JSON)), customerHandler::deleteCustomer);}

4.2 CustomerHandler

package com.javasampleapproach.webflux.functional.handler;import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;import org.springframework.http.MediaType;import com.javasampleapproach.webflux.model.Customer;
import com.javasampleapproach.webflux.repo.CustomerRepository;import static org.springframework.web.reactive.function.BodyInserters.fromObject;import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;@Component
public class CustomerHandler {private final CustomerRepository customerRepository;public CustomerHandler(CustomerRepository repository) {this.customerRepository = repository;}/*** GET ALL Customers*/public Mono<ServerResponse> getAll(ServerRequest request) {// fetch all customers from repositoryFlux<Customer> customers = customerRepository.getAllCustomers();// build responsereturn ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(customers, Customer.class);}/*** GET a Customer by ID */public Mono<ServerResponse> getCustomer(ServerRequest request) {// parse path-variablelong customerId = Long.valueOf(request.pathVariable("id"));// build notFound response Mono<ServerResponse> notFound = ServerResponse.notFound().build();// get customer from repository Mono<Customer> customerMono = customerRepository.getCustomerById(customerId);// build responsereturn customerMono.flatMap(customer -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(fromObject(customer))).switchIfEmpty(notFound);}/*** POST a Customer*/public Mono<ServerResponse> postCustomer(ServerRequest request) {Mono<Customer> customer = request.bodyToMono(Customer.class);return ServerResponse.ok().build(customerRepository.saveCustomer(customer));}/***    PUT a Customer*/public Mono<ServerResponse> putCustomer(ServerRequest request) {// parse id from path-variablelong customerId = Long.valueOf(request.pathVariable("id"));// get customer data from request objectMono<Customer> customer = request.bodyToMono(Customer.class);// get customer from repository Mono<Customer> responseMono = customerRepository.putCustomer(customerId, customer);// build responsereturn responseMono.flatMap(cust -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(fromObject(cust)));}/***   DELETE a Customer*/public Mono<ServerResponse> deleteCustomer(ServerRequest request) {// parse id from path-variablelong customerId = Long.valueOf(request.pathVariable("id"));// get customer from repository Mono<String> responseMono = customerRepository.deleteCustomer(customerId);// build responsereturn responseMono.flatMap(strMono -> ServerResponse.ok().contentType(MediaType.TEXT_PLAIN).body(fromObject(strMono)));}
}

5.运行并检查结果

使用命令行构建和运行SpringBoot项目:{ mvn clean install,mvn spring-boot:run}。

  • 获取所有客户要求: http://localhost:8080/api/customer

  • 提出GET客户要求: http://localhost:8080/api/customer/1

  • 发出POST请求: http://localhost:8080/api/customer/post

  • 提出PUT请求: http://localhost:8080/api/customer/put/3

  • 发出删除请求: http://localhost:8080/api/customer/delete/1

  • 向所有客户提出要求: http://localhost:8080/api/customer

源代码

SpringBoot基于WebFlux注解的RestAPIs示例源码 githup

Spring Boot 2.1.5(25)---SpringBoot基于WebFlux注解相关推荐

  1. springboot异步注解_Spring Boot 2 :Spring Boot 中的响应式编程和 WebFlux 入门

    [小宅按]Spring 5.0 中发布了重量级组件 Webflux,拉起了响应式编程的规模使用序幕. WebFlux 使用的场景是异步非阻塞的,使用 Webflux 作为系统解决方案,在大多数场景下可 ...

  2. (转)Spring Boot 2 (十):Spring Boot 中的响应式编程和 WebFlux 入门

    http://www.ityouknow.com/springboot/2019/02/12/spring-boot-webflux.html Spring 5.0 中发布了重量级组件 Webflux ...

  3. (十三)java版spring cloud+spring boot+redis社交电子商务平台-springboot集成spring cache...

    电子商务社交平台源码请加企鹅求求:一零三八七七四六二六.本文介绍如何在springboot中使用默认的spring cache, 声明式缓存 Spring 定义 CacheManager 和 Cach ...

  4. Spring Boot基础学习笔记25:RabbitMQ - 发布/订阅工作模式

    文章目录 零.学习目标 一.准备工作 (一)创建Spring Boot项目 - PublishSubscribeDemo (二)在应用属性文件里配置RabbitMQ 二.基于API进行消息发布和订阅 ...

  5. spring boot集成Elasticsearch-SpringBoot(25)

    1. Elasticsearch-搜索应用服务器   1.1 什么是搜索引擎   搜索引擎(search engine )通常意义上是指:根据特定策略,运用特定的爬虫程序从互联网上搜集信息,然后对信息 ...

  6. Spring Boot 学习(一) 快速搭建SpringBoot 项目

    快速搭建一个 Spring Boot 项目 部分参考于<深入实践Spring Boot>.<Spring实战 第四版>与程序猿DD的有关博客. 参考(嘟嘟独立博客):http: ...

  7. spring boot入门(九) springboot的过滤器filter。最完整、简单易懂、详细的spring boot教程。

    关于过滤器和拦截器的区别,已经spring boot入门(七)中说明.下面举个过滤器的应用场景,比如用户信息页只有再用户登录后才可以进入,没有登录的用户是无法进入的,此时便可以采用过滤器来讲没有登录的 ...

  8. Spring Boot中的缓存支持(一)注解配置与EhCache使用

    随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决这一问题非常好的手段之一.Spring 3开始提供了强大的基于注解的缓 ...

  9. Spring Boot基础学习笔记03:Spring Boot两种全局配置和两种注解

    文章目录 零.学习目标 1.掌握application.properties配置文件 2.掌握application.yaml配置文件 3.掌握使用@ConfigurationProperties注入 ...

最新文章

  1. Java--对象与类(三)
  2. Uva 11400 - Lighting System Design (DP)
  3. NGINX发布支持动态配置的开源Web服务器
  4. Android 利用方向传感器获得手机的相对角度
  5. conda环境管理介绍
  6. Oracle学习笔记:blank_trimming的含义
  7. 闰秒导致MySQL服务器的CPU sys过高
  8. 通过一个IT管理服务提供商攻陷190个澳大利亚组织机构的邮件供应链
  9. 浅谈UWB室内定位(三)_vortex_新浪博客
  10. 动态规划实战1-leetcode 983.Minimum Cost For Tickets
  11. 出席华盛顿大学以人为本用户体验设计领导力活动 探讨区块链的用户体验 | ArcBlock 活动...
  12. 【Mybatisplus】创建Spring Boot工程打包错误解决方法
  13. 不奋发,则心日颓靡;不检束,则心日恣肆 —— 北宋·朱熹
  14. 赵栋 java_赵栋 201771010137 《面向对象程序设计(java)》
  15. 东方博宜OJ——1.整数运算题解
  16. pr值高的域名对网站有什么价值?
  17. c# 解析 逗号分隔的字符串 转 list
  18. 各大网站前端web服务器汇总-nginx居首
  19. 【micropython】两块microbit实现无线温度检测
  20. Epson机械手程序开发(3)机械手报错时的处理

热门文章

  1. [读书笔记] -《C++ API设计》第7章 性能
  2. Java学习日报—SQL基础—2021/11/29
  3. mysql 添加最高权限设置_mysql 添加用户并设置权限
  4. 菜鸟学习笔记:Java提升篇8(线程2——线程的基本信息、线程安全、死锁、生产者消费者模式、任务调度)
  5. linux登录后自动打开终端,linux登录信息/打开终端信息
  6. memcache面试
  7. Android sdk 搭建
  8. PowerDesigner逆向工程从现有数据库生成PDM
  9. 美国回应朝鲜会谈提议:朝方须履行国际义务
  10. java 基础知识面试题(持续更新),java基础面试笔试题