SpringWebflux是SpringFramework5.0添加的新功能,WebFlux本身追随当下最火的Reactive Programming而诞生的框架,那么本篇就来简述一下这个框架到底是做什么的

一、关于WebFlux

  我们知道传统的Web框架,比如说:struts2,springmvc等都是基于Servlet API与Servlet容器基础之上运行的,在Servlet3.1之后才有了异步非阻塞的支持。而WebFlux是一个典型非阻塞异步的框架,它的核心是基于Reactor的相关API实现的。相对于传统的web框架来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上,因此它的运行环境的可选择行要比传统web框架多的多。

  根据官方的说法,webflux主要在如下两方面体现出独有的优势:

  1)非阻塞式

    其实在servlet3.1提供了非阻塞的API,WebFlux提供了一种比其更完美的解决方案。使用非阻塞的方式可以利用较小的线程或硬件资源来处理并发进而提高其可伸缩性

  2) 函数式编程端点

    老生常谈的编程方式了,Spring5必须让你使用java8,那么函数式编程就是java8重要的特点之一,而WebFlux支持函数式编程来定义路由端点处理请求。

二、SpringMVC与SpringWebFlux

我们先来看官网的一张图:

  它们都可以用注解式编程模型,都可以运行在tomcat,jetty,undertow等servlet容器当中。但是SpringMVC采用命令式编程方式,代码一句一句的执行,这样更有利于理解与调试,而WebFlux则是基于异步响应式编程,对于初次接触的码农们来说会不习惯。对于这两种框架官方给出的建议是:

  1)如果原先使用用SpringMVC好好的话,则没必要迁移。因为命令式编程是编写、理解和调试代码的最简单方法。因为老项目的类库与代码都是基于阻塞式的。

  2)如果你的团队打算使用非阻塞式web框架,WebFlux确实是一个可考虑的技术路线,而且它支持类似于SpringMvc的Annotation的方式实现编程模式,也可以在微服务架构中让WebMvc与WebFlux共用Controller,切换使用的成本相当小

  3)在SpringMVC项目里如果需要调用远程服务的话,你不妨考虑一下使用WebClient,而且方法的返回值可以考虑使用Reactive Type类型的,当每个调用的延迟时间越长,或者调用之间的相互依赖程度越高,其好处就越大

  我个人意见是:官网明确指出,SpringWebFlux并不是让你的程序运行的更快(相对于SpringMVC来说),而是在有限的资源下提高系统的伸缩性,因此当你对响应式编程非常熟练的情况下并将其应用于新的系统中,还是值得考虑的,否则还是老老实实的使用WebMVC吧

三、Reactive Spring Web

  在这里定义了最基本的服务端接口:HttpHandler和WebHandler

  HttpHandler

  HttpHandler定义了最基本的处理Http请求行为,这个接口主要作用是处理Http请求并将结果做出响应,下面这个表格是说明了Server API的使用方式及何种方式进行响应式流支持的:

Server name Server API used Reactive Streams support

Netty

Netty API

Reactor Netty

Undertow

Undertow API

spring-web: Undertow to Reactive Streams bridge

Tomcat

Servlet 3.1 non-blocking I/O; Tomcat API to read and write ByteBuffers vs byte[]

spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge

Jetty

Servlet 3.1 non-blocking I/O; Jetty API to write ByteBuffers vs byte[]

spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge

Servlet 3.1 container

Servlet 3.1 non-blocking I/O

spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge

  WebHandler

  WebHandler定义了Web请求必要一些处理行为,大家不妨想想看:WebFlux已经脱离了Servlet API,那么使用WebFlux时遇到会话机制怎么办,想要对请求过滤处理怎么办或者想要处理静态资源怎么办等等,那么WebHandler就是做这个事情的。其实在HttpHandler的基本实现类通过适配器模式及装饰模式也间接的实现了WebHandler接口:

  WebHandler常见的实现类,我在这里列举一下:

   WebHandlerDecorator:WebHandler的装饰器,利用装饰模式实现相关功能的扩展

   HttpWebHandlerAdapter: 进行Http请求处理,同时也是HttpHandler的实现类

   FilteringWebHandler:通过WebFilter进行过滤处理的类,类似于Servlet中的Filter

   ExceptionHandlingWebHandler: 针对于异常的处理类

   ResourceWebHandler:用于静态资源请求的处理类

   DispatcherHandler:请求的总控制器,类似于WebMVC中的DispatcherServlet

  

  

四、实现WebFlux示例

建立SpringBoot项目,注意SpringBoot版本必须为2.0.0+,在build.gradle配置文件里写:

 compile('org.springframework.boot:spring-boot-starter-webflux')

基于Annotated Controller方式实现

WebFluxConfig配置:

package com.hzgj.framework.study.springwebflux.web.reactive;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.WebFluxConfigurer; @Configuration
@ComponentScan
@EnableWebFlux
public class WebFluxConfig implements WebFluxConfigurer { @Bean
public WebHandler webHandler(ApplicationContext applicationContext) {
DispatcherHandler dispatcherHandler = new DispatcherHandler(applicationContext);
return dispatcherHandler;
} }

在这里我们创建一个WebHandler并使用@EnableWebFlux打开相关功能。我们可以发现与SpringMVC的WebConfig配置真的好像

Controller:

package com.hzgj.framework.study.springwebflux.web.reactive.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; @RestController
public class IndexController { @GetMapping("/index")
public String index() {
return "index";
}
}

  在这里与SpringMVC定义的Controller无异

Main方法:

package com.hzgj.framework.study.springwebflux.web.reactive;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import reactor.ipc.netty.http.server.HttpServer; import java.io.IOException; /**
* 基于Reactor Netty实现WebFlux服务
* @author chen.nie
* @date 2018/7/13
**/
public class SpringWebfluxApplication { public static void main(String[] args) throws IOException { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(WebFluxConfig.class);
//通过ApplicationContext创建HttpHandler
HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(applicationContext).build();
ReactorHttpHandlerAdapter httpHandlerAdapter = new ReactorHttpHandlerAdapter(httpHandler);
HttpServer.create("localhost",8080).newHandler(httpHandlerAdapter).block();
System.in.read();
}
}

  程序启动成功后即可通过http://localhost:8080/index拿到对应结果

函数式编程方式

  使用这种方式请先了解Java8提供的函数式编程特性。那么我们先编写一个简单的Handler

package com.hzgj.framework.study.springwebflux.web.reactive.handler;

import org.springframework.beans.BeanUtils;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono; import static org.springframework.http.MediaType.*;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.server.ServerResponse.ok; /**
* 类似于Controller,处理用户请求的真实逻辑
*/
public class StudentHandler { public static Mono<ServerResponse> selectStudent(ServerRequest request) {
Student studentBody = new Student();
request.bodyToMono(Student.class).subscribe(student -> BeanUtils.copyProperties(student, studentBody));
return ok().contentType(APPLICATION_JSON_UTF8).body(fromObject(studentBody));
} public static Mono<ServerResponse> insertStudent(ServerRequest request){
return ok().contentType(TEXT_PLAIN).body(fromObject("success")); }
private static class Student {
private Integer id;
private String name; public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}
}

  这个Handler类似于Controller的作用,在这里的返回值均为Mono类型,其中ServerRequest和ServerResponse,大家可以先理解为WebFlux替代ServletRequest与ServletResponse对象的,而且这些类能够支持异步。

  Main方法里我们创建的HttpHandler的方式需要进行改变一下,同样用函数式方式进行编写:

package com.hzgj.framework.study.springwebflux.web.reactive;

import com.hzgj.framework.study.springwebflux.web.reactive.handler.StudentHandler;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import reactor.ipc.netty.http.server.HttpServer; import java.io.IOException; import static org.springframework.web.reactive.function.server.RequestPredicates.*;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler; public class FunctionRouteApplication { public static void main(String[] args) throws IOException { HttpHandler httpHandler = toHttpHandler(
route(POST("/selectStudent").and(accept(MediaType.APPLICATION_JSON_UTF8)), StudentHandler::selectStudent).
and(route(GET("/saveStudent"), StudentHandler::insertStudent)));
ReactorHttpHandlerAdapter httpHandlerAdapter = new ReactorHttpHandlerAdapter(httpHandler);
HttpServer.create("localhost", 8080).newHandler(httpHandlerAdapter).block();
System.in.read();
}
}

启动成功后,我们用postman测试一下

此时我们可以看到使用函数式编程创建路由端点,也可以实现同样的功能。

集成Thymeleaf

@Configuration
@EnableWebFlux
@ComponentScan
public class WebFluxConfig implements WebFluxConfigurer, ApplicationContextAware { private ApplicationContext applicationContext; @Bean
public SpringResourceTemplateResolver templateResolver() { SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(applicationContext);
templateResolver.setPrefix("classpath:/templates/");
templateResolver.setSuffix(".html");
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
} @Bean
public SpringWebFluxTemplateEngine templateEngine() {
SpringWebFluxTemplateEngine templateEngine = new SpringWebFluxTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
} @Bean
public ThymeleafReactiveViewResolver viewResolver() {
ThymeleafReactiveViewResolver viewResolver = new ThymeleafReactiveViewResolver();
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
} @Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.viewResolver(viewResolver());
} @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

在这里注意以下,在webflux中不支持直接创建Bean的方式配置视图解析器,我们可以参考下面的源码:WebFluxConfiguraitonSupport

@Bean
public ViewResolutionResultHandler viewResolutionResultHandler() {
ViewResolverRegistry registry = getViewResolverRegistry();
List<ViewResolver> resolvers = registry.getViewResolvers();
ViewResolutionResultHandler handler = new ViewResolutionResultHandler(
resolvers, webFluxContentTypeResolver(), webFluxAdapterRegistry());
handler.setDefaultViews(registry.getDefaultViews());
handler.setOrder(registry.getOrder());
return handler;
}

我们在这里可以明确看到这个是通过ViewResolverRegistry来取的,因此我们只能通过 configureViewResolvers(ViewResolverRegistry registry) 来进行配置

Webflux快速入门的更多相关文章

  1. Spring Boot WebFlux 快速入门实践

    02:WebFlux 快速入门实践 Spring Boot 2.0 spring.io 官网有句醒目的话是: BUILD ANYTHING WITH SPRING BOOT Spring Boot ( ...

  2. Spring Boot 2 快速教程:WebFlux 快速入门(二)

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 02:WebFlux 快速入门实践 文章工程: JDK 1.8 ...

  3. Spring Boot WebFlux-01——WebFlux 快速入门实践

    第01课:WebFlux 快速入门实践 Spring Boot 2.0 spring.io 官网有句醒目的话是: BUILD ANYTHING WITH SPRING BOOT Spring Boot ...

  4. Spring Boot (十四): 响应式编程以及 Spring Boot Webflux 快速入门

    1. 什么是响应式编程 在计算机中,响应式编程或反应式编程(英语:Reactive programming)是一种面向数据流和变化传播的编程范式.这意味着可以在编程语言中很方便地表达静态或动态的数据流 ...

  5. Spring Boot 2.0 的快速入门(图文教程)

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! Spring Boot 2.0 的快速入门(图文教程) 大家都 ...

  6. 【Java】Spring快速入门(一)

    Spring介绍 Spring可以轻松创建Java企业应用程序.它提供了在企业环境中使用Java语言所需的一切,支持Groovy和Kotlin作为JVM上的替代语言,并可根据应用程序的需要灵活地创建多 ...

  7. Web Api 入门实战 (快速入门+工具使用+不依赖IIS)

    平台之大势何人能挡? 带着你的Net飞奔吧!:http://www.cnblogs.com/dunitian/p/4822808.html 屁话我也就不多说了,什么简介的也省了,直接简单概括+demo ...

  8. SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=》提升)

     SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=>提升,5个Demo贯彻全篇,感兴趣的玩才是真的学) 官方demo:http://www.asp.net/si ...

  9. 前端开发小白必学技能—非关系数据库又像关系数据库的MongoDB快速入门命令(2)

    今天给大家道个歉,没有及时更新MongoDB快速入门的下篇,最近有点小忙,在此向博友们致歉.下面我将简单地说一下mongdb的一些基本命令以及我们日常开发过程中的一些问题.mongodb可以为我们提供 ...

随机推荐

  1. Zookeeper C++编程实战之主备切换

    默认zookeeper日志输出到stderr,可以调用zoo_set_log_stream(FILE*)设置输出到文件中还可以调用zoo_set_debug_level(ZooLogLevel)控制日 ...

  2. 【转】Javascript基本类型和引用类型的区别

    根据[转贴]进一步补充 今天明白了一个困扰很久的问题:引用类型和基本类型的区别与联系要明白这个问题,首先需要理解堆栈的概念.那什么又是堆栈,有什么区别和联系呢?堆:首先堆是动态分配的,JVM并不会自动 ...

  3. HTML5表单新属性

    HTML5表单新属性 autofocus  自动聚焦 <input type="text" autofocus> placeholder占位文本 tel         ...

  4. spring之IOC和DI实现

    Spring核心思想 : IOC控制反转 DI依赖注入 AOP切面编程 IOC思想: 场景描述: 在没有加入spring框架之前我们调取业务层时需要创建对象,例如:  接口名   变量名=new  接 ...

  5. 第十四个目标 (fzu)

    http://acm.fzu.edu.cn/contest/problem.php?cid=151&sortid=8 Problem Description 目暮警官.妃英里.阿笠博士等人接连 ...

  6. web-day11

    第11章WEB11-Cookie&Session篇 今日任务 显示用户的上次访问时间 登录系统以后显示商品浏览记录 购买商品将商品添加到购物车 系统登录的验证码的校验 教学导航 教学目标 了解 ...

  7. Android之TextView灵活使用

    Android之TextView灵活使用 在项目中有无遇到过这样一种程况,例如文字"王明今年10岁了", 但是数字10是从网络返回的数据, 而你又想把这个文字写在xml中, 过往我 ...

  8. 12.Scrapy与mongodb交互和设置中间键

    反反爬虫相关机制 Some websites implement certain measures to prevent bots from crawling them, with varying d ...

  9. 6.python3爬虫之urllib库

    # 导入urllib.request import urllib.request # 向指定的url发送请求,并返回服务器响应的类文件对象 response = urllib.request.urlo ...

  10. cxGrid用法-最新

    cxGrid用法-最新 在做AdoHelper实用程序的时候,我用了DevExpress的cxGrid控件.在此之前用的是dbgrid,考虑到不能把所有的数据都拉到本地,我用了动态生成的select ...