这是我的学透Spring Boot的第13篇文章,更多文章请移步我的专栏
学透 Spring Boot_postnull咖啡的博客-CSDN博客
目录
传统风格的Spring MVC
函数式编程风格的Spring MVC
引入WebFlux依赖
定义Handler类
定义Router类
WebFlux不生效
灵魂拷问
Spring Web MVC框架,简称Spring MVC,是一种MVC的Web框架
- model:模型
- view:视图
- controller:控制器
传统风格的Spring MVC
一般情况,我们都是通过@Controller或者@RestController标注一个类,用来绑定进来的HTTP请求。方法中使用@RequestMapping注解来映射HTTP请求。
@RestController
@RequestMapping("/test")
public class TestUserController {private final TypiUserRestService typiUserRestService;public TestUserController(TypiUserRestService typiUserRestService) {this.typiUserRestService = typiUserRestService;}@GetMapping("/user/{id}")public TypiUser getById(@PathVariable Integer id) {return typiUserRestService.getUser(id);}
}
http://localhost:8080/test/user/1
函数式编程风格的Spring MVC
上面的传统风格的好处是简单,我们把业务代码和路由配置都写在一个类里面。我们也可以把它们分离开。
引入WebFlux依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
特别注意,如果我们项目中已经引入Spring-web,webflux会被自动忽略!!!
所以我们需要去掉pom.xml中spring-boot-starter-web
定义Handler类
类似于Controller,只是这里没有HTTP的绑定和路径的映射
@Component
public class MyUserHandler {final private TypiUserRestService typiUserRestService;public MyUserHandler(TypiUserRestService typiUserRestService) {this.typiUserRestService = typiUserRestService;}public Mono<ServerResponse> getUser(ServerRequest request){TypiUser user = typiUserRestService.getUser(Integer.valueOf(request.pathVariable("id")));return ServerResponse.ok().bodyValue(user);}
}
定义Router类
然后我们定义路由,再路由中绑定HTTP请求和handler
@Configuration
public class MyRoutingConfiguration {private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON);@Beanpublic RouterFunction<ServerResponse> routerFunction(MyUserHandler myUserHandler) {return route().GET("/testabc/user/{id}", ACCEPT_JSON, myUserHandler::getUser).build();}
}
说明:
- 路由通过 RouterFunctions.route() 构建,不再使用注解。
- 请求处理逻辑集中在 Handler 类中,实现更清晰的职责划分。
- 处理方法返回的是 Mono<ServerResponse>,这是 WebFlux 的响应式风格。
WebFlux不生效
用了函数式路由后,启动项目,但是访问一直提示404 怎么回事呢
原来是因为WebFlux和Web冲突了,如果项目中同时存在两种环境(Spring MVC和Spring WebFlux),则会优先使用Spring MVC。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
我们来一探究竟。
毫无疑问,肯定是Spring Boot的自动配置搞的鬼。
我们在自动配置的包下,找到自动配置类
看看自动配置类的
@AutoConfiguration(after = { ReactiveWebServerFactoryAutoConfiguration.class, CodecsAutoConfiguration.class,ReactiveMultipartAutoConfiguration.class, ValidationAutoConfiguration.class,WebSessionIdResolverAutoConfiguration.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnClass(WebFluxConfigurer.class)
@ConditionalOnMissingBean({ WebFluxConfigurationSupport.class })
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@ImportRuntimeHints(WebResourcesRuntimeHints.class)
public class WebFluxAutoConfiguration {
我们来看看其中一个生效条件
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
仅当应用是响应式 Web 应用时才生效(判断条件是有没有 WebFlux 类、是否排除了 Servlet 环境等)
如果你用了 spring-boot-starter-web 就不会满足这个条件 → 不生效
我们可以把debug日志打开 application.properties
debug=true
启动应用后,我们可以看到配置类的情况
Did not match: 告诉我们Spring Boot 判断我们的项目不是一个响应式 Web 应用(WebFlux),所以它 没有启用 WebFluxAutoConfiguration 自动配置类。
Matched是告诉我们,找到了WebFlux的类,因为我们的pom中引入了依赖!
是不是WebFlux环境,我们可以看这个类
可以看到是一个Servlet容器
综上所述,我们引入了Web-flux就需要移除Spring MVC
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency>
灵魂拷问
Spring MVC提供的Controller模式,已经非常简便了,大部分开发也是熟知这种模式,那为什么还要搞另外一套呢?
一句话,Router和Handler分离,可以实现更灵活的控制。
有时候,像Spring Cloud Gateway 一样,只是做简单的转发
.route(POST("/api/merge"), handler::mergeApis)
.route(GET("/health"), handler::probe)
另外,Handler和Router分离后,类的职责更单一了。
所以,我们也不是非要用函数式路由,对大部分场景,直接使用注解路由更简单!