当前位置: 首页 > article >正文

响应式编程库Reactor(二)Spring Webflux

Spring Webflux

  • 0、组件对比
  • 1、WebFlux
    • 1、引入
    • 2、Reactor Core
      • 1、HttpHandler、HttpServer
    • 3、DispatcherHandler
      • 1、请求处理流程
    • 4、注解开发
      • 1、目标方法传参
      • 2、返回值写法
    • 5、文件上传
    • 6、错误处理
    • 7、RequestContext
    • 8、自定义Flux配置
      • WebFluxConfigurer
    • 9、Filter

  • Reactor核心HttpHandler 原生API;
  • DispatcherHandler 原理;
    • DispatcherHandler 组件分析
    • DispatcherHandler 请求处理流程
    • 返回结果处理
    • 异常处理
    • 视图解析
      • 重定向
      • Rendering
  • 注解式 - Controller
    • 兼容老版本方式
    • 新版本变化
      • SSE
      • 文件上传
  • 错误响应
    • @ExceptionHandler
      • ErrorResponse: 自定义 错误响应
      • ProblemDetail:自定义PD返回
  • WebFlux配置
    • @EnableWebFlux
    • WebFluxConfigurer

WebFlux:底层完全基于netty+reactor+springweb 完成一个全异步非阻塞的web响应式框架

底层:异步 + 消息队列(内存) + 事件回调机制 = 整套系统

优点:能使用少量资源处理大量请求;

0、组件对比

API功能Servlet-阻塞式WebWebFlux-响应式Web
前端控制器DispatcherServletDispatcherHandler
处理器ControllerWebHandler/Controller
请求、响应ServletRequestServletResponseServerWebExchange:
ServerHttpRequest、ServerHttpResponse
过滤器Filter(HttpFilter)WebFilter
异常处理器HandlerExceptionResolverDispatchExceptionHandler
Web配置@EnableWebMvc@EnableWebFlux
自定义配置WebMvcConfigurerWebFluxConfigurer
返回结果任意Mono、Flux、任意
发送REST请求RestTemplateWebClient

Mono: 返回0|1 数据流

Flux:返回N数据流

1、WebFlux

底层基于Netty实现的Web容器与请求/响应处理机制

参照:https://docs.spring.io/spring-framework/reference/6.0/web/webflux.html

1、引入

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.6</version>
    </parent>


<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
    </dependencies>

Context 响应式上下文数据传递; 由下游传播给上游;

以前: 浏览器 --> Controller --> Service --> Dao: 阻塞式编程

现在: Dao(数据源查询对象【数据发布者】) --> Service --> Controller --> 浏览器: 响应式

大数据流程: 从一个数据源拿到大量数据进行分析计算;

ProductVistorDao.loadData()

.distinct()

.map()

.filter()

.handle()

.subscribe();

;//加载最新的商品浏览数据

2、Reactor Core

1、HttpHandler、HttpServer

      public static void main(String[] args) throws IOException {
        //快速自己编写一个能处理请求的服务器

        //1、创建一个能处理Http请求的处理器。 参数:请求、响应; 返回值:Mono<Void>:代表处理完成的信号
        HttpHandler handler = (ServerHttpRequest request,
                                   ServerHttpResponse response)->{
            URI uri = request.getURI();
            System.out.println(Thread.currentThread()+"请求进来:"+uri);
            //编写请求处理的业务,给浏览器写一个内容 URL + "Hello~!"
//            response.getHeaders(); //获取响应头
//            response.getCookies(); //获取Cookie
//            response.getStatusCode(); //获取响应状态码;
//            response.bufferFactory(); //buffer工厂
//            response.writeWith() //把xxx写出去
//            response.setComplete(); //响应结束

            //数据的发布者:Mono<DataBuffer>、Flux<DataBuffer>

            //创建 响应数据的 DataBuffer
            DataBufferFactory factory = response.bufferFactory();

            //数据Buffer
            DataBuffer buffer = factory.wrap(new String(uri.toString() + " ==> Hello!").getBytes());


            // 需要一个 DataBuffer 的发布者
            return response.writeWith(Mono.just(buffer));
        };

        //2、启动一个服务器,监听8080端口,接受数据,拿到数据交给 HttpHandler 进行请求处理
        ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);


        //3、启动Netty服务器
        HttpServer.create()
                .host("localhost")
                .port(8080)
                .handle(adapter) //用指定的处理器处理请求
                .bindNow(); //现在就绑定

        System.out.println("服务器启动完成....监听8080,接受请求");
        System.in.read();
        System.out.println("服务器停止....");


    }

3、DispatcherHandler

SpringMVC: DispatcherServlet;

SpringWebFlux: DispatcherHandler

1、请求处理流程

  • HandlerMapping:请求映射处理器; 保存每个请求由哪个方法进行处理
  • HandlerAdapter:处理器适配器;反射执行目标方法
  • HandlerResultHandler:处理器结果处理器;

SpringMVC: DispatcherServlet 有一个 doDispatch() 方法,来处理所有请求;

WebFlux: DispatcherHandler 有一个 handle() 方法,来处理所有请求;

public Mono<Void> handle(ServerWebExchange exchange) { 
		if (this.handlerMappings == null) {
			return createNotFoundError();
		}
		if (CorsUtils.isPreFlightRequest(exchange.getRequest())) {
			return handlePreFlight(exchange);
		}
		return Flux.fromIterable(this.handlerMappings) //拿到所有的 handlerMappings
				.concatMap(mapping -> mapping.getHandler(exchange)) //找每一个mapping看谁能处理请求
				.next() //直接触发获取元素; 拿到流的第一个元素; 找到第一个能处理这个请求的handlerAdapter
				.switchIfEmpty(createNotFoundError()) //如果没拿到这个元素,则响应404错误;
				.onErrorResume(ex -> handleDispatchError(exchange, ex)) //异常处理,一旦前面发生异常,调用处理异常
				.flatMap(handler -> handleRequestWith(exchange, handler)); //调用方法处理请求,得到响应结果
	}
  • 1、请求和响应都封装在 ServerWebExchange 对象中,由handle方法进行处理
  • 2、如果没有任何的请求映射器; 直接返回一个: 创建一个未找到的错误; 404; 返回Mono.error;终结流
  • 3、跨域工具,是否跨域请求,跨域请求检查是否复杂跨域,需要预检请求;
  • 4、Flux流式操作,先找到HandlerMapping,再获取handlerAdapter,再用Adapter处理请求,期间的错误由onErrorResume触发回调进行处理;

源码中的核心两个:

  • handleRequestWith: 编写了handlerAdapter怎么处理请求
  • handleResult: String、User、ServerSendEvent、Mono、Flux …

concatMap: 先挨个元素变,然后把变的结果按照之前元素的顺序拼接成一个完整流

private <R> Mono<R> createNotFoundError() {
		Exception ex = new ResponseStatusException(HttpStatus.NOT_FOUND);
		return Mono.error(ex);
	}
Mono.defer(() -> {
			Exception ex = new ResponseStatusException(HttpStatus.NOT_FOUND);
			return Mono.error(ex);
		}); //有订阅者,且流被激活后就动态调用这个方法; 延迟加载;

4、注解开发

1、目标方法传参

https://docs.spring.io/spring-framework/reference/6.0/web/webflux/controller/ann-methods/arguments.html

Controller method argumentDescription
ServerWebExchange封装了请求和响应对象的对象; 自定义获取数据、自定义响应
ServerHttpRequest, ServerHttpResponse请求、响应
WebSession访问Session对象
java.security.Principal
org.springframework.http.HttpMethod请求方式
java.util.Locale国际化
java.util.TimeZone + java.time.ZoneId时区
@PathVariable路径变量
@MatrixVariable矩阵变量
@RequestParam请求参数
@RequestHeader请求头;
@CookieValue获取Cookie
@RequestBody获取请求体,Post、文件上传
HttpEntity封装后的请求对象
@RequestPart获取文件上传的数据 multipart/form-data.
java.util.Map, org.springframework.ui.Model, and org.springframework.ui.ModelMap.Map、Model、ModelMap
@ModelAttribute
Errors, BindingResult数据校验,封装错误
SessionStatus + class-level @SessionAttributes
UriComponentsBuilderFor preparing a URL relative to the current request’s host, port, scheme, and context path. See URI Links.
@SessionAttribute
@RequestAttribute转发请求的请求域数据
Any other argument所有对象都能作为参数:
1、基本类型 ,等于标注@RequestParam
2、对象类型,等于标注 @ModelAttribute

2、返回值写法

sse和websocket区别:

  • SSE:单工;请求过去以后,等待服务端源源不断的数据
  • websocket:双工: 连接建立后,可以任何交互;
Controller method return valueDescription
@ResponseBody把响应数据写出去,如果是对象,可以自动转为json
HttpEntity, ResponseEntityResponseEntity:支持快捷自定义响应内容
HttpHeaders没有响应内容,只有响应头
ErrorResponse快速构建错误响应
ProblemDetailSpringBoot3;
String就是和以前的使用规则一样;
forward: 转发到一个地址
redirect: 重定向到一个地址
配合模板引擎
View直接返回视图对象
java.util.Map, org.springframework.ui.Model以前一样
@ModelAttribute以前一样
Rendering新版的页面跳转API; 不能标注 @ResponseBody 注解
void仅代表响应完成信号
Flux, Observable, or other reactive type使用 text/event-stream 完成SSE效果
Other return values未在上述列表的其他返回值,都会当成给页面的数据;

5、文件上传

https://docs.spring.io/spring-framework/reference/6.0/web/webflux/controller/ann-methods/multipart-forms.html

class MyForm {

	private String name;

	private MultipartFile file;

	// ...

}

@Controller
public class FileUploadController {

	@PostMapping("/form")
	public String handleFormUpload(MyForm form, BindingResult errors) {
		// ...
	}

}

现在

@PostMapping("/")
public String handle(@RequestPart("meta-data") Part metadata, 
		@RequestPart("file-data") FilePart file) { 
	// ...
}

6、错误处理

    @ExceptionHandler(ArithmeticException.class)
    public String error(ArithmeticException exception){
        System.out.println("发生了数学运算异常"+exception);

        //返回这些进行错误处理;
//        ProblemDetail:  建造者:声明式编程、链式调用
//        ErrorResponse : 

        return "炸了,哈哈...";
    }

7、RequestContext

8、自定义Flux配置

WebFluxConfigurer

容器中注入这个类型的组件,重写底层逻辑

@Configuration
public class MyWebConfiguration {

    //配置底层
    @Bean
    public WebFluxConfigurer webFluxConfigurer(){

        return new WebFluxConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowedHeaders("*")
                        .allowedMethods("*")
                        .allowedOrigins("localhost");
            }
        };
    }
}

9、Filter

@Component
public class MyWebFilter implements WebFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        System.out.println("请求处理放行到目标方法之前...");
        Mono<Void> filter = chain.filter(exchange); //放行


        //流一旦经过某个操作就会变成新流

        Mono<Void> voidMono = filter.doOnError(err -> {
                    System.out.println("目标方法异常以后...");
                }) // 目标方法发生异常后做事
                .doFinally(signalType -> {
                    System.out.println("目标方法执行以后...");
                });// 目标方法执行之后

        //上面执行不花时间。
        return voidMono; //看清楚返回的是谁!
        

    }
}

http://www.kler.cn/a/537853.html

相关文章:

  • 四、OSG学习笔记-基础图元
  • 【C语言标准库函数】指数与对数函数:exp(), log(), log10()
  • 蓝桥杯---力扣题库第38题目解析
  • 解锁 DeepSeek 模型高效部署密码:蓝耘平台全解析
  • 如何导入第三方sdk | 引入第三方jar 包
  • 赛博算命之 ”梅花易数“ 的 “JAVA“ 实现 ——从玄学到科学的探索
  • HarmonyOS 5.0应用开发——全局自定义弹出框openCustomDialog
  • 蓝桥与力扣刷题(19 删除链表的倒数第N个结点)
  • 刚发布的nodejs 23提供了哪些新能力
  • 高效Android Studio编程:快捷键汇总
  • IDEA编写SpringBoot项目时使用Lombok报错“找不到符号”的原因和解决
  • I2C协议—读写EEPROM(24Cxx为例)
  • VSCode 换行符问题
  • Deepseek的起源与发展
  • (六)QT——布局&常用控件——基本的用户输入界面
  • HTTP报文格式
  • 蓝桥杯---力扣题库第38题目解析
  • html css网页制作成品——HTML+CSS爷爷不泡茶的茶网页设计(7页)附源码
  • IDEA安装离线插件(目前提供了MavenHelper安装包)
  • npm中央仓库
  • pycharm ai插件
  • element-plus 解决el-dialog背后的页面滚动问题,及其内容有下拉框出现错位问题
  • 21.[前端开发]Day21-HTML5新增内容-CSS函数-BFC-媒体查询
  • < 评论 > 阿里云 与 腾讯云 国内的轻量应用服务器(VPS)产品对比
  • 【韩顺平linux】部分上课笔记整理
  • 星网锐捷 DMB-BS LED屏信息发布系统taskexport接口处存在敏感信息泄露