程序员必知:Webflux快速入门

简介: 程序员必知:Webflux快速入门

  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 nameServer API usedReactive 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 //代码效果参考:http://www.zidongmutanji.com/zsjx/492955.html

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 {

//代码效果参考:http://www.zidongmutanji.com/bxxx/11836.html

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();

}

}

  程序启动成功后即可通过拿到对应结果

函数式编程方式

  使用这种方式请先了解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.//代码效果参考:http://www.zidongmutanji.com/bxxx/433190.html

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 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 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 resolvers = registry.getViewResolvers();

ViewResolutionResultHandler handler = new ViewResolutionResultHandler(

resolvers, webFluxContentTypeResolver(), webFluxAdapterRegistry());

handler.setDefaultViews(registry.getDefaultViews());

handler.setOrder(registry.getOrder());

return handler;

}

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

相关文章
|
缓存 算法 NoSQL
红包场景的系统设计和实践
红包场景的系统设计和实践
464 0
红包场景的系统设计和实践
|
缓存 Java Nacos
一文带你理解@RefreshScope注解实现动态刷新原理
一文带你理解@RefreshScope注解实现动态刷新原理
1085 0
一文带你理解@RefreshScope注解实现动态刷新原理
|
10月前
|
XML Java 数据格式
🌱 深入Spring的心脏:Bean配置的艺术与实践 🌟
本文深入探讨了Spring框架中Bean配置的奥秘,从基本概念到XML配置文件的使用,再到静态工厂方式实例化Bean的详细步骤,通过实际代码示例帮助读者更好地理解和应用Spring的Bean配置。希望对你的Spring开发之旅有所助益。
427 4
|
NoSQL 安全 测试技术
接口测试用例设计的关键步骤与技巧解析
该文介绍了接口测试的设计和实施,包括测试流程、质量目标和用例设计方法。接口测试在需求分析后进行,关注功能、性能、安全等六项质量目标。流程包括网络监听(如TcpDump, WireShark)和代理工具(Charles, BurpSuite, mitmproxy, Fiddler, AnyProxy)。设计用例时,需考虑基本功能流程、输入域测试(如边界值、特殊字符、参数类型、组合参数、幂等性)、线程安全(并发和分布式测试)以及故障注入。接口测试用例要素包括模块、标题、优先级、前置条件、请求方法等。文章强调了保证接口的幂等性和系统健壮性的测试重要性。
|
12月前
|
人工智能
青否数字人直播间五大互动,承诺抖音封号即退款!
青否数字人直播系统通过五大互动策略实现真正的无人直播,包括AI智能互动、关键词互动、氛围引导、弹幕文字互动和真人接管。系统支持独立部署,一次买断终身使用,具备无限量声音和形象克隆功能,确保直播过程中的实时互动和高质量内容输出。
|
11月前
|
机器学习/深度学习 编解码 自然语言处理
文生图大模型
DALL·E 是由 OpenAI 开发的基于深度学习的图像生成模型,能够根据文本描述生成原创图像。从 2021 年初的 DALL·E 到 2022 年的 DALL·E 2,再到最新的 DALL·E 3,其功能不断升级,包括生成、扩展、修改图像及生成变体图像。DALL·E 3 在提示优化、清晰度和多风格支持方面进行了增强,广泛应用于定制图像生成、虚拟设定、产品设计和广告营销等领域。
|
SQL Java 数据库连接
深入探索MyBatis Dynamic SQL:发展、原理与应用
深入探索MyBatis Dynamic SQL:发展、原理与应用
|
机器学习/深度学习 人工智能 自然语言处理
自动化办公:AI如何改变工作方式
【7月更文第19天】随着人工智能技术的飞速发展,我们的工作方式正经历着前所未有的转型。从繁琐的文档处理到高效的会议安排,再到个性化的邮件回复,AI正逐步成为现代办公不可或缺的一部分。本文将深入探讨AI如何在这些核心办公场景中发挥作用,提升工作效率,优化工作流程,从而推动工作方式的全面革新。
1149 3
|
机器学习/深度学习 JSON 数据库
Python每循环一次保存一次结果
Python每循环一次保存一次结果
392 1
|
机器学习/深度学习 数据采集 人工智能
【机器学习】CLIP模型在有限计算资源下的性能探究:从数据、架构到训练策略
【机器学习】CLIP模型在有限计算资源下的性能探究:从数据、架构到训练策略
706 0