孙卫琴《精通Spring》的学习笔记:WebFlux框架的函数式开发模式
本文选自孙的《掌握Spring:Java Web开发技术详解》,清华大学出版社出版。
技术支持网站是:www.javathinker.net/spring.jsp
这本书对应的直播和录音类:www.javathinker.net/zhibo.jsp
孙的QQ学习答疑群:915851077
所谓功能开发模式,是与面向对象开发模式相对的一个概念。在面向对象的开发模式中,对象是程序中的主角。程序运行时会创建各种对象,这些对象会产生各种行为,相互配合,最终产生运行结果。在功能开发模式中,实现特定功能的各种方法是程序中的主角。一个又一个方法被调用,环环相扣,最终产生运算结果,就像生产线一样。至于哪些对象提供了这些方法,可以忽略。
例如,下面代码中的Lambda表达式体现了函数式编程的思想。Lambda表达式定义了处理接收数据的函数,但至于这些函数属于哪个匿名对象,这里忽略不计:
//发送方发送“Hello”字符串mono mono = mono . just(" Hello ");//接收方接收“Hello”字符串mono . subscribe(value->;System.out.println(value),error- gt;error . printstacktrace());
因为Java语言本质上是一种面向对象的开发语言,在程序中引入函数式开发模式时,实际上是把面向对象开发模式和函数式开发模式混合在一起了。
在WebFlux框架的功能开发模式下,控制器类是任何由@Component组件标识的类。例程1的以下DataHandler类是控制器类。
1例行DataHandler.java
……导入静态org . spring framework . web . reactive. function . server . server response . ok;@ Componentpublic class data handler {public Mono greet(server request请求){string current time = " Now is "+newSimpleDateFormat(" HH:mm:ss ")。format(new Date());returnok()。contentType(MediaType。TEXT_PLAIN)。body(Mono.just(currentTime),string . class);}public mono count(server request请求){List scores = new ArrayList();for(inti = 0;我 lt100000;i++)scores . add(I);Flux data = Flux . fromiterable(分数);returnok()。contentType(MediaType。APPLICATION_STREAM_JSON)。body(data,integer . class);}public mono push(server request请求){List scores = new ArrayList();for(inti = 0;我 lt100000;i++)scores . add(I);//每5秒发送一次数据 flux data = flux.interval(持续时间。秒(5)) 。from iterable(分数);returnok()。contentType(MediaType。TEXT_EVENT_STREAM)。body(data,integer . class);}}
DataHandler类的请求处理方法有一个ServerRequest类型的请求参数,返回类型是Mono类型。
DataHandler类的greet()、count()和push()方法分别返回不同类型的响应结果:
以DataHandler类的greet()方法为例。它的以下代码体现了函数式编程的思想:
导入静态org . spring framework . web . reactive. function . server . server response . ok;……return ok()。contentType(MediaType。TEXT_PLAIN)。body(Mono.just(currentTime),string . class);
上面的ok()方法是静态引入的,具有返回正常响应结果的功能。然后,调用contentType()和body()方法来设置响应结果的数据类型和正文内容。整个运算结果由一系列方法调用生成,削弱了对象在程序中的主导地位,体现了函数式编程的思想。
DataHandler类不是由@Controller批注标识的,因此不可能用@RequestMapping之类的批注为其请求处理方法设置映射路径。那么在这种情况下,如何为请求处理方法设置映射路径呢?用RouterFunction路由函数接口设置映射路径也叫设置路由。
在下面例程2的DataRouter类中,为DataHandler类的所有三个请求处理方法设置了路由。
2例行DataRouter.java
@ Configurationpublic class data router {@ Beanpublic router functionroute(data handler data handler){returnRouterFunctions。路由(请求谓词。GET("/greet")。和(request predicates . accept(media type。TEXT_PLAIN)),dataHandler::greet)。andRoute(请求谓词。GET("/push")。和(request predicates . accept(media type。TEXT_EVENT_STREAM)),dataHandler::push)。andRoute(请求谓词。GET("/count")。和(request predicates . accept(media type。APPLICATION_STREAM_JSON)),data handler::count);}}
DataRouter类由@Configuration注释标识,这表明DataRouter类属于Configuration类,因此Spring框架会在启动时将DataRouter类设置的路由加载到内存中。root()方法由@Bean注释标识,这表明root()方法返回的RouterFunction对象将在Spring框架中注册为Bean组件。
在Intellij IDEA中运行这个示例程序,DataRouter类为DataHandler类的greet()、push()和count()方法设置的映射路径是:
http://localhost:8080/greet
http://本地主机:8080/push
http://本地主机:8080/count