spring webflux视图forward
Spring5基于Spring Reactor3支持反应式编程(reactive programing),在其spring-webflux模块中提供反应式web开发,它又可分为两种风格的编程api:类似于spring-webmvc基于注解的controller风格和函数式风格。
我们知道,对视图forward,在spring-webmvc controller的处理器方法中返回类似“forward:xx”的字符串即可将请求forward到xx url上。目前在webflux中如果要forward http请求,目前只有采用webfilter,在前面的文章spring webflux过滤器中有演示如何在过滤器中forward。在webflux controller中forward目前是不支持的,webflux支持controller方法返回“redirect:xx”进行重定向,但直到目前最新的spring 5.0.4 ,spring-webflux controller仍不支持类似spring-mvc的forward功能,相关jira见Reactive HTTP request forwarding,结合thymeleaf测试了视图forward,确实不支持,forward时报错。在thymeleaf-spring5-3.0.9.RELEASE.jar中org.thymeleaf.spring5.view.reactive.ThymeleafReactiveViewResolver.resolveViewName(String, Locale)方法有如下的TODO提示:
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
vrlogger.trace("[THYMELEAF] View \"{}\" is a forward, and will not be handled directly by ThymeleafReactiveViewResolver.", viewName);
// TODO * No view forwarding in Spring WebFlux yet. See https://jira.spring.io/browse/SPR-14537
return Mono.error(new UnsupportedOperationException("Forwards are not currently supported by ThymeleafReactiveViewResolver"));
}
spring5仍支持spring-webmvc模块,使用webflux的原因是其全栈支持异步非阻塞,几乎从前端控制层--》服务层--》数据层均可异步(mongo/redis等有异步,JDBC目前还不支持异步,有些缺失,不过异步JDBC估计在19年会出现),不像spring-webmvc中的高并发场景下的异步是靠一个或多个线程池中的众多线程处理同步任务来达到伪异步,webflux也有线程池,但它的线程池容量较少,它推荐使用异步api编写异步任务,这些异步任务进行io时不会阻塞线程,这样只要不多的线程即可处理众多的请求。异步servlet和异步servlet io是在原来servlet阻塞api中打补丁的临时方案,适合做没有多少业务逻辑的应用,特别是可能有较高延迟(high latency)同时又有高并发的场景的io型应用,例如api网关这种需要进行高效网络io转发的应用,当然全新设计的webflux比异步servlet更适合做这种应用,并且它的适用面更广,还适合做业务逻辑复杂的应用。
webflux模块抛弃了servlet编程模型,它没有servlet、servlet filter、context path、HttpServletRequest这些概念和类,它提供有webfilter(它是一个接口,不要和spring-webmvc中的WebFilter注解混淆)和前面说的两种风格的api。webflux controller中的view forward功能可通过webflux webfilter来实现。
view forwarding实现
fork了github上最新的spring-framework,见https://github.com/wangguobo/spring-framework
修改涉及到两个模块spring-web和spring-webflux。代码在master分支上,我另开了个分支v5.0.2.RELEASE.patch 也有相应的功能,可以下载编译该分支,使用v5.0.2.RELEASE.patch 版本的spring-web和spring-webflux。
webflux controller 视图forward演示
项目使用的pom文件包含如下依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.M7</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<!-- 要使用该版本-->
<version>5.0.2.RELEASE.patch</versio>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<!-- 要使用该版本-->
<version>5.0.2.RELEASE.patch</version>
</dependency>
...
</dependencies>
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Controller
public class WebfluxForwardDemoController {
@GetMapping("/")
public String home(ServerWebExchange exchange) {
System.out.println("/");
//forward:index为相对路径,forward:/index为绝对路径,和servlet forward类似
return "forward:index";
}
@GetMapping("index")
public String index(Model model,ServerWebExchange exchange) {
System.out.println("index");
model.addAttribute("content", "hello");
return "index";
}
@GetMapping("webflux")
public Mono<String> webflux(ServerWebExchange exchange) {
System.out.println("webflux");
return Mono.just("forward:demo");
}
@GetMapping("demo")
public String index(Model model,ServerWebExchange exchange) {
System.out.println("forward-view");
model.addAttribute("content", "webflux forward view");
return "view1";
}
}
forward会重放请求,请求会重新经过所有webfilter最后到controller(如果根据请求mapping能找到控制器的情况下),如果forward时希望跳过所有webfilter直接转到另一个controller方法,可以返回带前缀through:的字符串,例如 "through:index"。这个through功能也是这次一并实现的。
Test:
curl http://127.0.0.1:8080/
curl http://127.0.0.1:8080/webflux
上一篇: java并发之同步辅助类Phaser
下一篇: dubbo注解使用详解