Java内存马之WebFlux基础知识补充
Java内存马之WebFlux基础知识补充
Natro92Spring WebFlux
Spring WebFlux
是Spring Framework 5.0
中引入的一个新的响应式框架,旨在为在Spring中构建响应式应用程序提供支持。Spring WebFlux
基于Reactor
项目,是一个异步、非阻塞的框架,专门用于处理并发性较高且需要非阻塞I/O操作的应用程序,例如实时数据处理、高性能API服务等。与传统的Spring MVC
不同,WebFlux
旨在充分利用多核心和现代硬件的优势,提高应用程序的吞吐量和伸缩性。
该框架队接口的返回类型进行控制,使用Mono<T>
和Flux<T>
,这里简单介绍一下什么是Mono
和Flux
什么是 Mono
Mono
代表的是一个或零个元素的异步序列。你可以将其视作响应式编程中的Future或Optional,但提供了更丰富的操作和组合性。Mono是用来处理异步任务的,尤其是那些最多只返回单个值的操作,例如一个数据库查询可能返回零个或一个结果。
1 | Mono<String> noData = Mono.empty(); // 表示空的异步序列 |
什么是 Flux
Flux
代表的是零到多个元素的异步序列。Flux可以发出任意数量的数据项。它用于表示可能包含多个值的异步操作,例如获取所有用户的列表或从数据流中获取数据。
1 | Flux<String> emptyFlux = Flux.empty(); // 空的数据流 |
Spring WebFlux 启动过程分析
使用之前的项目,按照要求在run
这里下断点。
直接跳到org.springframework.boot.SpringApplication#createApplicationContext
这里。
创建上下文工厂类管理,再进。
根据 webapplicationType
创建context
,进入 create 方法:
这个工厂类的create
方法根据传入的WebApplicationType
参数来决定是否创建一个新的AnnotationConfigReactiveWebServerApplicationContext
实例。如果传入的webApplicationType
不是REACTIVE
,则方法返回null
,表示这个工厂仅支持反应式Web应用。如果是REACTIVE
,则实例化并返回一个新的AnnotationConfigReactiveWebServerApplicationContext
对象。
继续看下去,能注意到REACTIVE
所对应的是org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext
步过出这个方法,能回到run
方法。
分别调用prepareContext
、refreshContext
、afterRefresh
进到refreshContext
其中,再进refresh
,再进refresh
。发现调用父类的refresh
,进入看看:
进入到onRefresh
,跳到了org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext#onRefresh
能在 org.springframework.boot.web.reactive.context.WebServerManager
中看到有三个常量:
applicationContext
:这是ReactiveWebServerApplicationContext
类型的一个实例变量,它在WebServerManager
的构造函数中通过参数传入,并在整个WebServerManager
类中被使用。这个上下文对象代表了Spring
应用的反应式Web应用上下文,用于管理Spring Bean
的生命周期和提供配置信息。webServer
:这是WebServer
类型的一个实例变量,它在WebServerManager
的构造函数中通过ReactiveWebServerFactory
的getWebServer
方法初始化。webServer
代表了应用的Web服务器,负责处理HTTP请求和响应。WebServerManager
类中的start
、stop
和shutDownGracefully
方法都是围绕这个webServer实例进行操作的。handler
:这是DelayedInitializationHttpHandler
类型的一个实例变量,它也是在WebServerManager
的构造函数中初始化的。这个handler对象是一个HTTP处理器,负责处理进入Web服务器的HTTP请求。它的初始化可能会延迟,直到第一个HTTP请求到达,这取决于lazyInit
标志的值。
而启动 webserver
的位置在finishRefresh
。
进入onRefresh
方法,调用了 StartBeans
方法。
这里开启了 autoStartUpOnly
根据字面意思能知道是自启动,然后获取生命周期bean:通过调用getLifecycleBeans
获取所有实现了Lifecycle
接口的bean。这些bean可能包括SmartLifecycle
和普通的Lifecycle
实现。
然后再分组,方便后面低级和高级按序进行操作。然后就是 start
方法启动,并给 phases
赋值。
检测 Debug,排序,再进入 doStart
方法。再进到 start
方法:
调用this.handler.initializeHandler()来初始化HttpHandler。这一步非常重要,因为HttpHandler负责处理HTTP请求。如果在创建WebServerManager时设置了lazyInit为true,那么处理器的实际初始化将延迟到收到第一个HTTP请求时。再启动 web 服务器,最后发布一个 Event
,这个事件表明web服务器已经完全初始化并准备就绪。
再进start
方法,会进到:org.springframework.boot.web.embedded.netty.NettyWebServer#start
直接就能看出来是靠org.springframework.boot.web.embedded.netty.NettyWebServer#startHttpServer
方法执行起webServer
服务。
Spring WebFlux 请求处理过程分析
我们回到最开始处理的com.natro92.webfluxdemo.hello.GreetingHandler#hello
打上断点:
访问/hello
路由,然后步过一次,进到org.springframework.web.reactive.function.server.support.HandlerFunctionAdapter#handle
一直步过直到找到 invokeHandler
方法:
进入到 handle
方法中
眼熟,分析下这段代码:
- 检查
handler
映射。 - 检测是否是
CORS
的预请求,调用其他请求方法。 - 然后就是这一大堆 return 的内容,创建一个
Flux
流,遍历handlerMappings
,然后concatMap
字面意思就是拼接,然后next
选择第一个匹配的处理器,如果是空,则返回一个NotFoundError
(这里应该指的是 404)。后面的flatMap
里面处理handler
,再将结果返回。
这里的调用栈中有一个org.springframework.web.reactive.handler.AbstractHandlerMapping#getHandler
我们回到刚才的调用栈分析,这个方法在org.springframework.web.reactive.function.server.support.RouterFunctionMapping#getHandlerInternal
中呗继承。
点进 create
方法中:
里面创建了 org.springframework.web.reactive.function.server.DefaultServerRequest
对象,传入的参数有:HttpMessageReader
,可以提供解析参数的能力。
回到 org.springframework.web.reactive.function.server.support.RouterFunctionMapping#getHandlerInternal
方法中,发现调用了 route
函数
根据意思返回匹配请求的 handler
函数this.predicate.test
检查了请求是否符合路由要求,如果匹配到了处理方法,就返回保存的 handlerFunction
,如果没有就返回空的 Mono
。
再进去 test
方法,是个接口。根据前面的文件命名方法,应该在RequestPredicates
中。
断点调试。这里已经拿到了 pattern
信息
将 left
和 right
分别赋值。这里是调用了org.springframework.web.reactive.function.server.RequestPredicates.AndRequestPredicate#AndRequestPredicate
的方法,如下。
而我们可以发现,如果给这里断点的话,不需要访问路由就可以到这里,就说明这里是预先就进行了准备。
然后我们再回到org.springframework.web.reactive.DispatcherHandler#invokeHandler
的断点。
能注意到这里的this.handlerAdapters
已经有了四个值。
并不是所有的handlerAdapter
都会触发handle
方法,只有当支持我们给定的handler
的handlerAdapter
才可以调用。
WebFilter 过程分析
对于 Spring WebFlux
而言,没有拦截器和监听器。需要对权限验证和访问控制,需要用Filter
。
通过实现org.springframework.web.server.WebFilter
接口,来定义全局的过滤器,在路由到handler
的前后执行逻辑;或者通过是实现HandlerFilterFunction
也可以。
新建一个GreetingFilter.java
类。
1 | package com.natro92.webfluxdemo.hello; |
我们在这个 filter 方法处下断点。
进入 return 里的 filter
函数。defer
方法延迟执行,判断过滤器和链子是不是空,如果是空则调用 filter 方法,进行实现,如果不为空,那个直接给 handler 的 handle 方法实现。
进入 org.springframework.web.server.handler.DefaultWebFilterChain#invokeFilter
方法中
而这个类有三个构造方法,一个是公共的构造函数,一个是私有构造,还有个一个已被弃用的。
其中注释中写了,我最开始以为是代表着一个对 chain 的连接,后来看了网上的说法是代表的链中的 link。无法通过修改chain.allFilters
实现新增Filter
。
这里这个 allFilters
存储着我们之前编写的Filter
。
这个类中有个initChain
方法通过实例化来调用了这个类,而且使用的是注释中说明的私有化调用。
我们找一下公共调用Ctrl + Alt + F7
进去第四个也就是在org.springframework.web.server.handler.FilteringWebHandler#FilteringWebHandler
调用的。
也就是说我们可以构造一个DefaultWebFilterChain
对象,通过反射来写入到FilteringWebHandler
的 chain
属性。
然后需要搞清楚handler
和filters
这两个参数。而 handler
这个参数已经在 chain
中。
这个 filters
,可以通过先获取到原本的 filters
,然后再把原先的恶意filter
放进去。放到第一个就可以了。
然后就是再内存中找到DefaultWebFilterChain
,然后反射就行。
这里使用这个工具:
先 clone 下来,再放到 idea 中,mvn clean install
:
这里我遇到点问题,用 8 环境把编译版本改了还一直报错找不到一个类,直接在 cmd 临时用 7 编译一下就好了。
1 | set JAVA_HOME=D:/java/jdk1.8 |
然后添加进库就行。
然后修改GreetingFilter.java
代码:
1 | package com.natro92.webfluxdemo.hello; |
但是我这里运行出的结果没找到含关键词的这个。。。
1 | TargetObject = {reactor.netty.resources.DefaultLoopResources$EventLoop} |