SpringBoot之响应式编程
⼀ Spring WebFlux Framework说明
Spring WebFlux 是 Spring Framework 5.0 中引⼊的新 reactive web framework。与 Spring MVC 不同,它不需要 Servlet API,完全异步和 non-blocking,并通过实现规范。
Spring WebFlux 有两种版本:功能和 annotation-based。 annotation-based ⼀个⾮常接近 Spring MVC model,如下⾯的⽰例所⽰:
@RestController
@RequestMapping("/users")
public class MyRestController {
@GetMapping("/{user}")
public Mono<User> getUser(@PathVariable Long user) {
// ...
}
@GetMapping("/{user}/customers")
public Flux<Customer> getUserCustomers(@PathVariable Long user) {
// ...
}
@DeleteMapping("/{user}")
public Mono<User> deleteUser(@PathVariable Long user) {
// ...
}
}
函数变量“WebFlux.fn”将路由配置与请求的实际处理分开,如下⾯的⽰例所⽰:
@Configuration
public class RoutingConfiguration {
@Bean
public RouterFunction<ServerResponse> monoRouterFunction(UserHandler userHandler) {
return route(GET("/{user}").and(accept(APPLICATION_JSON)), userHandler::getUser)
.andRoute(GET("/{user}/customers").and(accept(APPLICATION_JSON)), userHandler::getUserCustomers)
.andRoute(DELETE("/{user}").and(accept(APPLICATION_JSON)), userHandler::deleteUser);
}
}
@Component
public class UserHandler {
public Mono<ServerResponse> getUser(ServerRequest request) {
// ...
}
public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
// ...
}
public Mono<ServerResponse> deleteUser(ServerRequest request) {
// ...
}
}
WebFlux 是 Spring Framework 的⼀部分,其详细信息可在中到。
您可以根据需要定义尽可能多的RouterFunction beans 来模块化 router 的定义。如果需要应⽤优先级,可以订购 Beans。
要开始,请将spring-boot-starter-webflux模块添加到 application。
在 application 中添加spring-boot-starter-web和spring-boot-starter-webflux模块会导致 Spring Boot auto-configuring Spring MVC,⽽不是 WebFlux。
选择此⾏为是因为许多 Spring 开发⼈员将spring-boot-starter-webflux添加到他们的 Spring MVC application 以使⽤ reactive WebClient。您仍然可以通过将所选的 application 类型设置为SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE)来强制执⾏您的选择。
Spring WebFlux Auto-configuration
Spring Boot 为 Spring WebFlux 提供 auto-configuration,适⽤于⼤多数 applications。
auto-configuration 在 Spring 的默认值之上添加以下 features:
为HttpMessageReader和HttpMessageWriter实例配置编解码器(描述为)。
⽀持提供静态资源,包括对 WebJars 的⽀持(描述为)。
如果你想保留 Spring Boot WebFlux features 并且想要添加额外的,你可以添加⾃⼰的@Configuration class 类型为WebFluxConfigurer但⽽不是 @EnableWebFlux。
如果要完全控制 Spring WebFlux,可以添加⾃⼰的@Configuration注释@EnableWebFlux。
带有 HttpMessageReaders 和 HttpMessageWriters 的 HTTP 编解码器
Spring WebFlux 使⽤HttpMessageReader和HttpMessageWriter接⼝来转换 HTTP 请求和响应。通过查看 classpath 中可⽤的 libraries,它们配置为CodecConfigurer以具有合理的默认值。
Spring Boot 通过使⽤CodecCustomizer实例进⼀步⾃定义。例如,spring.jackson.* configuration 键应⽤于 Jackson 编解码器。
如果需要添加或⾃定义编解码器,可以创建⾃定义CodecCustomizer component,如下⾯的⽰例所⽰:
import org.springframework.dec.CodecCustomizer;
@Configuration
public class MyConfiguration {
@Bean
public CodecCustomizer myCodecCustomizer() {
return codecConfigurer -> {
// ...
}
}
}
你也可以利⽤。
静态内容
默认情况下,Spring Boot 为 classpath 中名为/static(或/public或/resources或/META-INF/resources)的⽬录提供静态内容。它使⽤来⾃ Spring WebFlux
的ResourceWebHandler,以便您可以通过添加⾃⼰的WebFluxConfigurer并覆盖addResourceHandlers⽅法来修改该⾏为。
默认情况下,资源映射到/**,但您可以通过设置spring.webflux.static-path-pattern property 来调整它。例如,将所有资源重新定位到/resources/**可以实现如下:
spring.webflux.static-path-pattern=/resources/**
您还可以使⽤sources.static-locations⾃定义静态资源位置。这样做会将默认值替换为⽬录位置列表。如果这样做,默认的欢迎页⾯检测将切换到您的⾃定义位置。因此,如果您在启动时的任何位置都有index.html,那么它就是 application 的主页。
除了前⾯列出的“标准”静态资源位置之外,还为做了⼀个特例。如果 jar files 包含在 Webjars 格式中,则中包含路径的所有资源都将从 jar files 提供。
Spring WebFlux applications 并不严格依赖于 Servlet API,因此不能将它们部署为 war files 并且不要使⽤src/main/webapp⽬录。
模板引擎
与 REST web services ⼀样,您也可以使⽤ Spring WebFlux 来提供动态 HTML 内容。 Spring WebFlux ⽀持各种模板技术,包括 Thymeleaf,FreeMarker 和 Mustache。Spring Boot 包括对以下模板引擎的 auto-configuration ⽀持:
当您使⽤其中⼀个模板引擎和默认的 configuration 时,您的模板将从src/main/resources/templates⾃动获取。
错误处理
Spring Boot 提供WebExceptionHandler,以合理的⽅式处理所有错误。它在处理 order 中的位置紧接在 WebFlux 提供的处理程序之前,这被认为是最后的。对于机器客户端,它会⽣成⼀个 JSON 响应,其中包含错误,HTTP 状态和 exception 消息的详细信息。对于浏览器客户端,有⼀个“whitelabel”错误处理程序,它以 HTML 格式呈现相同的数据。您还可以提供⾃⼰的 HTML 模板来显⽰错误(请参阅)。
⾃定义此 feature 的第⼀个步骤通常涉及使⽤现有机制,但替换或扩充错误内容。为此,您可以添加ErrorAttributes类型的 bean。
要更改错误处理⾏为,可以实现ErrorWebExceptionHandler并注册该类型的 bean 定义。因为WebExceptionHandler⾮常 low-level,所以 Spring Boot 还提供了⼀个⽅便
的AbstractErrorWebExceptionHandler来让你以 WebFlux 的⽅式处理错误,如下⾯的例⼦所⽰:
public class CustomErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
// Define constructor here
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions
.route(aPredicate, aHandler)
.andRoute(anotherPredicate, anotherHandler);
}
}
要获得更完整的图⽚,您还可以直接⼦类化DefaultErrorWebExceptionHandler并覆盖特定⽅法。
⾃定义错误页⾯
如果要为给定状态 code 显⽰⾃定义 HTML 错误页⾯,可以将⽂件添加到/error⽂件夹。错误页⾯可以是静态 HTML(即,添加到任何静态资源⽂件夹下)或使⽤模板构建。⽂件的name 应该是确切的状态 code 或系列掩码。
例如,要 map 404到静态 HTML ⽂件,您的⽂件夹结构如下:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- public/
+- error/
| +- 404.html
+- <other public assets>
要使⽤ Mustache 模板 map 所有5xx错误,您的⽂件夹结构如下:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- templates/
+- error/
| +- 5xx.mustache
+- <other templates>
Web 过滤器
Spring WebFlux 提供了⼀个WebFilter接⼝,可以实现过滤 HTTP request-response 交换。在 application context 中到的WebFilter beans 将⾃动⽤于过滤每个交换。
如果过滤器的 order 很重要,则可以实现Ordered或使⽤@Order进⾏注释。 Spring Boot auto-configuration 可以为您配置 web 过滤器。执⾏此操作时,将使⽤以下 table 中显⽰的订单:
Web 过滤器订购
MetricsWebFilter Ordered.HIGHEST_PRECEDENCE + 1
WebFilterChainProxy(Spring Security)-100
HttpTraceWebFilter Ordered.LOWEST_PRECEDENCE - 10
⼆ WebClient
Spring Boot 将 auto-detect ⽤于驱动WebClient,具体取决于 application classpath 上可⽤的 libraries。
⽬前,⽀持 Reactor Netty 和 Jetty RS client。
spring-boot-starter-webflux starter 默认依赖于io.projectreactorty:reactor-netty,它带来了 server 和 client implementations。如果您选择使⽤ Jetty 作为 reactive 服务器,则应该在 Jetty Reactive HTTP client library,lipse.jetty:jetty-reactive-httpclient上添加依赖项。对服务器和 client 使⽤相同的技术具有优势,因为它将⾃动在 client 和服务器之间共享 HTTP 资源。
开发⼈员可以通过提供⾃定义ReactorResourceFactory或JettyResourceFactory bean 来覆盖 Jetty 和 Reactor Netty 的资源 configuration - 这将应⽤于 clients 和服务器。
如果您希望覆盖 client 的该选项,您可以定义⾃⼰的ClientHttpConnector bean 并完全控制 client configuration。
您可以了解有关的更多信息。
WebClient ⾃定义
WebClient⾃定义有三种主要⽅法,具体取决于您希望⾃定义应⽤的⼴泛程度。
要使任何⾃定义的范围尽可能窄,请 inject auto-configured WebClient.Builder然后根据需要调⽤其⽅
法。 WebClient.Builder实例是有状态的:构建器上的任何更改都会反映在随后使⽤它创建的所有 client 中。如果要使⽤相同的构建器创建多个 client,还可以考虑使⽤WebClient.Builder other = builder.clone();克隆构建器。
要对所有WebClient.Builder实例进⾏ application-wide 添加⾃定义,可以声明WebClientCustomizer beans 并在注⼊点本地更改WebClient.Builder。
最后,您可以回退到原始 API 并使⽤ate()。在这种情况下,不应⽤ auto-configuration 或WebClientCustomizer
三代码演⽰
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
注:webflux不能和web共存,webflux启动的是netty。
实体类
public class User {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(String id, String name) {
super();
this.id = id;
this.name = name;
}
}
Service层
@Service
public class UserService {
private static final Map<String, User> dataMap = new HashMap<>();
static{
dataMap.put("1", new User("1", "⼩X⽼师"));
dataMap.put("2", new User("2", "⼩D⽼师"));
spring mvc和boot区别dataMap.put("3", new User("3", "⼩C⽼师"));
dataMap.put("4", new User("4", "⼩L⽼师"));
dataMap.put("5", new User("5", "⼩A⽼师"));
dataMap.put("6", new User("6", "⼩S⽼师"));
dataMap.put("7", new User("7", "⼩S⽼师"));
}
/**
* 功能描述:返回⽤户列表
* @return
*/
public Flux<User> list(){
Collection<User> list = UserService.dataMap.values();
return Flux.fromIterable(list);
}
/**
* 功能描述:根据id查⽤户
* @param id
* @return
*/
public Mono<User> getById(final String id){
return Mono.justOrEmpty((id));
}
/**
* 功能描述:根据id删除⽤户
* @param id
* @return
*/
public Mono<User> del(final String id){
return Mono.justOrEmpty(ve(id));
}
}
web层
@RestController
@RequestMapping("/api/v1/user")
public class UserController {
/
/@Autowired
//private UserService userService;
private final UserService userService;
public UserController(final UserService userService) {
this.userService = userService;
}
@GetMapping("/test")
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论