2. Spring WebFlux
新建应用,信息如下:
Group:top.wisely
Artifact:learning-webflux
Dependencies:Spring Reactive Web
、Lombok
build.gradle
文件中的依赖如下:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
//...
}
2.1 WebFlux基础
Spring WebFlux不依赖于Servlet API,它可以运行在非Servlet容器Netty、Undertow和任何Servlet 3.1+的Servlet容器(Tomcat,Jetty)之上。虽然Servlet 3.1提供了非阻碍I/O的API,但是有很多其它的API依然是同步或阻碍式的;这使得Spring需要重新构建完全基于异步和非阻碍式的运行环境。
Spring WebFlux最底层的组件是HttpHandler
,它用来适配不同的服务器引擎(Netty、Undertow、Tomcat、Jetty)。
在通过HttpHandler
消除了服务器引擎的异构行后,Spring WebFlux的API设计与Spring MVC是高度一致的。
Spring WebFlux与Spring MVC在概念上有对应的关系:
Spring WebFlux | Spring Web MVC |
---|---|
DispatcherHandler |
DispatcherServlet |
WebFilter |
Filter |
HttpMessageWriter HttpMessageReader |
HttpMessageConverter |
HandlerMapping |
HandlerMapping |
HandlerAdapter |
HandlerAdapter |
ServerHttpRequest ServerHttpResponse |
ServletRequest ServletResponse |
Spring WebFlux支持两种编程模型:
-
注解控制器:和Spring MVC使用的注解式保持一致。
RequestMappingHandlerMapping
:映射请求与@RequestMapping
控制器类和方法;RequestMappingHandlerAdapter
:调用@RequestMapping
注解的方法。
Sping MVC的两个类与这两个类名称一致,在不同的包里,有不同的实现,但功能保持一致。
-
函数式端点:基于Lambada表达式、轻量级的函数式编程模型。
RouterFunctionMapping
:用来支持RouterFunction
;HandlerFunctionAdapter
:用来支持HandlerFunctions
。
从Spring 5.2或Spring 2.2.x以后Spring MVC也支持这种编程模型。
2.2 Spring Boot的自动配置
Spring Boot提供的自动配置主要有:
-
CodecsAutoConfiguration
:Spring WebFlux使用HttpMessageReader
和HttpMessageWriter
接口来转换HTTP请求和返回。本配置类为我们注册了CodecCustomizer
的Bean,默认使用Jackson2JsonEncoder
和Jackson2JsonDecoder
。 -
ReactiveWebServerFactoryAutoConfiguration
:为响应式Web服务器进行自动配置。 -
WebFluxAutoConfiguration
:使用等同于@EnableWebFlux
的配置开启WebFlux的支持。可通过WebFluxProperties
使用spring.webflux.*
来对WebFlux进行配置:spring: webflux: date-format: yyyy-MM-dd # 日期格式 static-path-pattern: /resouces/static/** # 静态资源目录
-
WebClientAutoConfiguration
:为WebClient
进行自动配置。
2.2 注解控制器
2.2.1 示例
我们演示一个简单的例子,先定义简单的领域模型:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private Long id;
private String name;
private Integer age;
}
@RestController
@RequestMapping("/people")
public class PersonController {
PersonRepository personRepository; //1
public PersonController(PersonRepository personRepository) {
this.personRepository = personRepository;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<Person> add(@RequestBody Person person){
//2
return Mono.just(personRepository.save(person));
}
@GetMapping("/{id}")
public ResponseEntity<Mono<Person>> getById(@PathVariable Long id){
//3
return ResponseEntity.ok()
.body(Mono.just(personRepository.findOne(id)));
}
@GetMapping
public Flux<Person> list(){
//4
return Flux.fromIterable(personRepository.list());
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpS