springboot系列学习笔记全部文章请移步值博主专栏**: spring boot 2.X/spring cloud Greenwich。
由于是一系列文章,所以后面的文章可能会使用到前面文章的项目。springboot系列代码全部上传至GitHub:https://github.com/liubenlong/springboot2_demo
本系列环境:Java11;springboot 2.1.1.RELEASE;springcloud Greenwich.RELEASE;MySQL 8.0.5;
Spring WebFlux是随Spring 5推出的响应式Web框架。Spring Boot 2.X 提供了对 WebFlux的支持。
Spring WebFlux是基于响应式流的,因此可以用来建立异步的、非阻塞的、事件驱动的服务。它采用Reactor
作为首选的响应式流的实现库,不过也提供了对RxJava
的支持。
Spring Boot 2.0 包括一个新的 spring-webflux 模块。该模块包含对响应式 HTTP 和 WebSocket 客户端的支持,以及对 REST,HTML 和 WebSocket 交互等程序的支持。一般来说,Spring MVC 用于同步处理,Spring Webflux 用于异步处理。
由于响应式编程的特性,Spring WebFlux和Reactor底层需要支持异步的运行环境,比如Netty和Undertow;也可以运行在支持异步I/O的Servlet 3.1的容器之上,比如Tomcat(8.0.23及以上)和Jetty(9.0.4及以上)。
Spring Boot Webflux 有两种编程模型实现,一种类似 Spring MVC 注解方式,另一种是使用其功能性端点方式。
常用的 Spring Boot 2.0 WebFlux 生产的特性如下:
还有对日志、Web、消息、测试及扩展等支持。
spring mvc 和 spring webflux 是有交集的,两者可以混合使用,建议IO密集型采用webflux。
Spring Boot WebFlux 官方提供了很多 Starter 组件,每个模块会有多种技术实现选型支持,来实现各种复杂的业务需求:
Web:Spring WebFlux
模板引擎:Thymeleaf
存储:Redis、MongoDB、Cassandra。不支持 MySQL
内嵌容器:Tomcat、Jetty、Undertow
我们从springmvc转为webflux。
一个最简单的程序:
引入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
编写主程序
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
编写测试controller
@RestController
public class MyController {
//spring mvc
@GetMapping("/hello")
public String hello() {
return "Welcome to reactive world ~";
}
}
在之前基础上,将依赖spring-boot-starter-web
改为spring-boot-starter-webflux
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webfluxartifactId>
dependency>
将controller改为
@RestController
public class MyController {
@GetMapping("/hello")
public Mono<String> hello1() {
return Mono.just("Welcome to reactive world ~");
}
}
spring 对webflux进行了很好的封装,完全兼容springmvc, 使得迁移非常方便。不过底层架构已经完全不同。
整个技术栈从命令式的、同步阻塞的【spring-webmvc + servlet + Tomcat】变成了响应式的、异步非阻塞的【spring-webflux + Reactor + Netty】
Reactor 是异步非阻塞的框架,其相关内容后续会介绍
实体类Stu
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Builder
public class Stu implements Serializable {
private String name;
private int age;
private Date date;
private BigDecimal b;
private Duration d;
}
编写handle
@Component
@Slf4j
public class TimeHandler {
public Mono<ServerResponse> getTime(ServerRequest serverRequest) {
return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
.body(Mono.just("Now is " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())), String.class);
}
/**
* 模拟DB获取数据
* @param serverRequest
* @return
*/
public Mono<ServerResponse> getStus(ServerRequest serverRequest) {
List<Stu> stus = Arrays.asList(Stu.builder().age(10).name("zhangsan").build(),
Stu.builder().age(20).name("lisi").build());
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)//这里改为json
.body(Flux.just(stus), List.class);
}
}
编写路由器
import com.example.handle.TimeHandler;
import org.springframework.beans.factory.annotation.Autowired;
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.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
@Configuration
public class RouterConfig {
@Autowired
private TimeHandler timeHandler;
/**
* RouterFunction: 路由
* 相当于@RequestMapping。将不同的请求路由到不同的方法服务上
*
* 可以存在多个router和handler。但是bean的名称需不同(这里bean的名称使用的是方法名)
* @return
*/
@Bean
public RouterFunction<ServerResponse> timerRouter() {
return RouterFunctions.route(GET("/time"), req -> timeHandler.getTime(req))
.andRoute(GET("/getStus"), timeHandler::getStus);
}
}
通常类似于聊天室之类的技术都会用到Websocket、Socket.IO(nodejs或者netty)。
响应式编程是一种基于数据流的编程范式,天然适合在服务器推送。
在handle添加一个方法,模拟一个每秒想客户端推送时间的例子
/**
* 服务器端推送,
* 每秒推送当前服务器时间
*
* @param serverRequest
* @return
*/
public Mono<ServerResponse> getTimePerSecond(ServerRequest serverRequest) {
return ServerResponse.ok().contentType(MediaType.TEXT_EVENT_STREAM)
.body(Flux.interval(Duration.ofSeconds(1))
.map(a -> "Now is " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())),
String.class);
}
路由中添加:
@Bean
public RouterFunction<ServerResponse> timerRouter() {
return RouterFunctions.route(GET("/time"), req -> timeHandler.getTime(req))
.andRoute(GET("/getStus"), timeHandler::getStus)
.andRoute(GET("/getTimePerSecond"), timeHandler::getTimePerSecond);
}
springboot系列学习笔记全部文章请移步值博主专栏**: spring boot 2.X/spring cloud Greenwich。
由于是一系列文章,所以后面的文章可能会使用到前面文章的项目。springboot系列代码全部上传至GitHub:https://github.com/liubenlong/springboot2_demo
本系列环境:Java11;springboot 2.1.1.RELEASE;springcloud Greenwich.RELEASE;MySQL 8.0.5;