早在Java流行之前就引入了场景,因为它擅长开发和处理网络应用。Java有一个爱好,就是喜欢制定规范和标准,但不擅长实现。相反,一些服务提供商利用其规范标准制造应用服务器并大赚一笔。商业用户向供应商支付大量资金来使用这些应用服务器,而且它们并不是特别好用。一位青年才俊正在努力打破这种局面。自我介绍很明显,这位青年才俊就是后来的春天。因为大部分企业应用都是和web相关的,所以Javaweb标准的核心部分其实就是JavaEE中的Servlet。Spring和Servlet“相亲相爱”后,来到了这个世界。我的全名是SpringMVC。这里的春天既是我的姓氏,也是我的“爸爸”。那么Servlet就是我的“妈妈”。就叫我MVC吧。那时候社会很落后,条件也不好。反正我们的要求不高,只求温饱。所以我妈的Servlet和她最好的朋友Filter天生就是同步阻塞的,包括他们的同事HttpServletRequest的getParameter,getPart等方法也被阻塞。虽然我爸Spring给了我23条染色体让我完善,但是不要忘了我也是从我Servlet老妈那里继承了23条,所以我也是同步阻塞的。不过我的“颜值”已经好了很多,因为春爸爸知道,以后的日子,除了拼实力,颜值也是很重要的。因为我妈的Servlet是一个规范,而我爸的Spring是一个框架,所以我和他们一样,不能独立运行。所以当我们要运行的时候,就必须要找到一个专门的“家”,这个家通常叫做Servlet容器,比如tomcat就是一个非常有名的。Servlet容器知道自己很有可能阻塞当前执行线程,所以特意剪裁。它为我准备了一个非常大的线程池,里面有很多线程。每次有请求过来,就给我丢个跟帖,说我自己玩,随便“折腾”。幸运的是,当时美国那个叫乔布斯的家伙被他的公司赶出去“流浪”在外面。Servlet容器为我量身定做的方法,完全可以胜任日常生活,关键也很简单。这种发财安康的生活就这样过着。兄弟生生不息,不断变化。随着乔布斯推出iPhone,智能手机瞬间普及,全民进入移动互联网时代。网民数量的激增给现有的软件架构带来了巨大的挑战。一般来说,社会越发达,分工越细,对单一工种的要求就越高。软件也是如此。当传统的“大”软件越来越格格不入时,微服务就像一股春风吹来,按照其指导原则,将大软件按照一定的方式拆分成小项目。小项目规模小,易于管理,流动性好,功能聚合性好。它应该承担更高的并发性。有人认为过去使用的tomcat等Web服务器与微服务相比,有点笨重,不够轻便。也有人说,tomcat中一请求一线程的阻塞执行方式,太耗线程,不容易支持超高并发。不管怎样,总之,一个新时代已经到来。此时我们需要一个更轻量级的Web应用,它使用的硬件资源和线程更少,但更容易处理高并发。那么它必须是异步和非阻塞的。这样的使命自然属于响应式编程的范畴。于是我爸Spring看清了情况,5.0之后很快就把我推出去了。没错,我是SpringWebFlux,这里的Spring既是我的姓,也是我的父亲。你可以叫我WebFlux。我是新来的,很多人不熟悉我,请允许我介绍一下。首先,这个反应到底是什么意思?响应式一词是指围绕反映变化而构建的编程模型。例如,网络组件用于响应I/O事件,UI控制器用于响应鼠标事件等。按照这种认识,非阻塞就是响应式的,一种响应操作完成或数据可用的通知事件的方式。另一种响应机制是非阻塞背压。在命令式代码中,同步阻塞调用有一个自然的后压缩,迫使调用者等待。在异步代码中,控制事件发生率变得非常重要,这样快速的事件源就不会淹没其响应者。也就是说,响应者可以控制事件源发出事件的速度。因为响应式编程是非阻塞的,我也是非阻塞的,所以我一般都是跑在非阻塞的web服务器上,比如Netty、Undertow等。由于我不阻塞线程执行,因此使用少量固定数量的线程池(事件循环工作者)来处理请求。通常,线程数与CPU的核心数相同。我还要感谢我的祖父Java8,他引入了lambda表达式来创建函数式编程API。这对于非阻塞应用程序和顺序API来说是一件好事,允许以声明方式组合异步逻辑。我觉得我爸Spring已经超越了一个框架,变成了一个平台。所以他并没有自己实现响应式处理,而是选择了Reactor作为我的响应式库。Reactor提供了Flux和Mono类型,拥有丰富的算子,支持非阻塞背压,使用函数式API结合异步逻辑。Reactor非常专注于Java服务器端。它是与DadSpring密切合作开发的。老爸说,我也支持RxJava等其他库,不过我好像更喜欢Reactor。这就是我,WebFlux,一个天时地利的幸运儿。但是你已经头晕了吗?没关系,慢慢来。我想每个人都可以看到我爸爸Spring的野心。他不仅要成为一个平台,还要建立自己的生态系统,竖起壁垒。所以他的核心业务是抽象组合组装,然后包罗万象。说白了,哪个技术好,我们就集成哪个。为了抹平不同底层Web服务器之间的差异,父亲抽象出一个最底层的契约接口HttpHandler,用于处理响应式HTTP请求。Monohandle(ServerHttpRequestrequest,ServerHttpResponseresponse);它是跨越不同运行时的通用接口。它有意做到最小化,只有一种方法,其主要目的是对各种HTTP服务器API进行最小化抽象。如果要使用Netty服务器,可以基于Netty来实现。同样的,也可以基于Undertow等实现,以后只要有新的服务器,都可以加入。显然,HttpHandler的目标是抽象出不同HTTP服务器的使用。说白了就是和底层服务器连接。但是因为太底层,所以没有使用上层的代码。为此,父亲抽象出一个稍微高级一点的契约接口WebHandler,用于处理Web请求。显然,WebHandler的目标是提供在Web应用中广泛使用的通用特性,如Session、表单数据和附件等,同时也为了更容易与上层代码对接。自然地,WebHandler是建立在HttpHandler之上的。也就是说,WebHandler的处理最终会通过一个适配器HttpWebHandlerAdapter委托给HttpHandler。WebHandler接口也只有一个方法:Monohandle(ServerWebExchangeexchange);参数类型为ServerWebExchange。可以这样理解,如果你发送一个请求,给你一个响应,就相当于用请求交换了一个响应,是在服务端交换的。其实整个web请求的处理过程是一个链条,最后是一个WebHandler。前面可以插入多个错误处理程序、WebExceptionHandler、多个过滤器和WebFilter。这是错误处理程序接口:Monohandle(ServerWebExchangeexchange,java.lang.Throwableex);这是过滤器接口:Monofilter(ServerWebExchangeexchange,WebFilterChainchain);可见,我这个Spring之父的抽象能力是非常强的。它抽象出一个接口来消除不同服务器之间的差异。抽象一个接口,可以用来支持不同的编程模型。有哪些编程模型,请继续阅读。皮囊之下,我自我介绍时用的是美颜,所以你很难看到我的“真面目”。现在让我们做一个自我剖析,看看真正的我。我包括一个轻量级函数式编程模型,其中函数用于参与处理请求。它是基于注释的编程模型的替代方案。这种编程模型称为功能端点。功能端点是建立在上面提到的WebHandler之上的。我使用HandlerFunction来处理HTTP请求。这是一个功能接口,也称为处理功能:@FunctionalInterfacepublicinterfaceHandlerFunction{reactor.core.publisher.Monohandle(ServerRequestrequest);}带有一个ServerRequest参数,返回一个Mono,其中请求和响应对象是不可变的,HandlerFunction相当于Controller中标记为@RequestMapping的方法。在实践中,请求很多,处理函数也很多。我怎么知道哪个处理函数应该处理请求?当然,我需要使用我的另一个功能接口RouterFunction,称为路由功能:@FunctionalInterfacepublicinterfaceRouterFunction{reactor.core.publisher.Mono>route(ServerRequestrequest);}接受一个ServerRequest参数并返回单声道。也就是说,它将请求路由到HandlerFunction。当路由函数匹配时,它返回一个处理函数,否则返回一个空的Mono。RouterFunction相当于@RequestMapping注解,但主要区别在于路由函数提供的不仅仅是数据,还有行为。下面通过一些例子来帮助大家更直观地理解这两个功能接口。因为处理函数是函数式接口,所以可以直接使用lambda表达式来处理请求,如下:HandlerFunctionhandler=request->Response.ok().body("HelloWorld");这意味着当任何请求到来时,它都会返回HelloWorld作为响应。在实际应用中,处理逻辑一般都很复杂,一定不能用lambda表达式来处理。这时候就希望把处理方法写在一个类里,叫处理器类,和MVC中的Controller差不多。.下面是一个Person处理类:publicclassPersonH??andler{publicMonolistPeople(ServerRequestrequest){//...}publicMonocreatePerson(ServerRequestrequest){//...}publicMonogetPerson(ServerRequestrequest){//.../...}}此时,可以通过handler函数来引用这些handler方法,如下::createPerson;HandlerFunctionget=handler::getPerson;要使请求能够被正确路由,首先要定义路由函数,如下:RouterFunctionroute=RouterFunctions.route().GET("/person/{id}",get).GET("/person",list).POST("/person",create).build();意思是当用GET方法请求/person/{id}时,最终会被getPerson方法处理执行。当用GET方法请求/person时,最后由listPeople方法处理。同样,当用POST方法请求/person时,会被createPerson方法处理。可见,一个路由函数可以包含多个路由规则。在实践中,可以定义多个路由函数,并将这些路由函数组合在一起。路由函数按顺序进行评估,如果第一条路由不匹配,则评估第二条,依此类推。因此,将更具体的路线放在一般路线之前是有意义的。请注意,这与基于注释的不同。怎么样,关掉滤镜我是不是更真实?相信你能看懂,至少记住这是一种基于函数的编程模型,称为函数端点。玉露君湛是我这样幸运的人,你一定觉得春的父亲很宠我吧,告诉你,确实如此。不过,考虑到大佬们一路都是跟Spring在一起的,老爸也设身处地为你着想。为此,除了支持函数式端点的编程模型外,我还支持一种叫做annotation-basedcontrollers的编程模型,annotatedcontrollers,没错,就是MVC中的那个。说白了就是大家已经非常熟悉的SpringMVC一套东西。我100%完全支持它,而且使用起来很安全。但是,并不是所有的controller方法参数都支持响应式类型,只有一些,比如WebSession、java.security.Principal、@RequestBody、HttpEntity、@RequestPart等。我们来看一个例子:@PostMapping("/")publicStringhandle(@RequestBodyMono>parts){//...}@PostMapping("/")publicStringhandle(@RequestBodyFluxparts){//...}@PostMapping("/accounts")publicvoidhandle(@RequestBodyMonoaccount){//...}但是controller方法的所有返回值都支持响应式。每个人都有自己的长处。SpringMVC和SpringWebFlux可以一起使用。从设计的角度来看,它们相互延续并保持一致。他们的关系请看下图。既有公共部分,也有独立部分。SpringMVC的特点是命令式编程,代码非常容易编写,易于理解和调试。但是它是同步的,有些人会认为它的性能不好。但我要说的是,响应式和非阻塞通常也不会使应用程序运行得更快。相比之下,非阻塞方法需要做更多的工作,并略微增加了必要的处理时间。或者说,可能会稍微慢一点,什么,为什么要用呢?反应式和非阻塞的主要好处是能够在使用少量固定线程数和更少内存的同时进行扩展。这使得应用程序在负载下更具弹性,因为它们以更可预测的方式扩展。为了能够观察到这些好处,你需要有一些延迟,比如一个不可靠和缓慢的网络I/O,这就是响应性开始发挥作用的地方,差异(惊喜)可能是巨大的哦.其实技术没有好坏之分,各有各的适用场景。