Spring Boot - 函数式 Web
# 1. 前言
我们现在常用的 Spring Boot Web 开发是使用注解 @Controller
+ @RequestMapping
耦合式(路由、业务耦合)以及衍生的 xxMapping
来接受请求。
现在详细介绍一下 Web 访问的另一种形式,通过 函数式 Web(路由、业务分离)实现一个 Restful 风格的 Http 请求案例。
函数式Web框架(Functional Web Framework),引入于Spring Framework 5中,并随Spring Boot得到广泛应用,提供了一种与传统注解驱动开发(如使用@Controller
和@RequestMapping
注解)相辅相成的开发模式。这种函数式方法重点在于路由和业务逻辑的解耦,通过显式函数调用而不是依赖注解的自动映射来定义Web路由,使得代码结构更加清晰、模块化程度更高。
# 2. 介绍
函数式 Web 的四大核心对象:
RouterFunction: 代替传统的
@RequestMapping
注解,用于声明式地定义路由到处理函数的映射。它允许开发者以编程的方式精确控制请求的处理流程。RequestPredicates: 用于定义路由的匹配条件,比如HTTP方法、路径模式等。这使得对请求的筛选更加灵活和细粒度。
ServerRequest: 封装了HTTP请求的详细信息,包括请求参数、路径变量等,与
HttpServletRequest
相似但更适合函数式编程。ServerResponse: 封装了HTTP响应,提供了构建响应的各种方法,比如设置状态码、响应体等,类似于
HttpServletResponse
,但使用起来更简洁方便。
函数式Web与传统注解驱动开发的区别
- 解耦度: 函数式Web通过显式路由定义,将路由和业务逻辑分开,提高了代码的模块化和可维护性。而传统注解方式将路由和处理逻辑紧密耦合在控制器类中。
- 灵活性: 函数式Web提供了更多的灵活性,开发者可以更容易地组合、重用和测试路由和处理逻辑。而注解方式在处理复杂路由时可能会变得笨重。
- 性能: 在某些场景下,函数式Web因为其简洁的路由和处理逻辑,可能提供比传统注解方式更好的性能。
# 3. 使用
# 3.1 MyUserHandler 类
UserBizHandler
类作为业务处理器,定义了如何处理各种HTTP请求。每个公开的方法都对应一个特定的请求操作,例如获取用户信息、创建用户等。通过操作ServerRequest
对象来访问请求数据,并返回ServerResponse
对象以构建响应。这种方式使得每个操作都是自包含的,易于理解和维护。
@Slf4j
@Service
public class UserBizHandler {
// 处理获取单个用户的请求
public ServerResponse getUser(ServerRequest request) {
// 模拟从数据库或其他服务获取用户信息
User user = new User("张三", "12345678");
// 返回状态码200,并且将user对象作为响应体返回
return ServerResponse.ok().body(user);
}
// 处理获取所有用户的请求
public ServerResponse getUsers(ServerRequest request) {
// 模拟从数据库获取用户列表
List<User> users = new ArrayList<>();
users.add(new User("张三","1234"));
users.add(new User("李四","abc"));
users.add(new User("王五","hello"));
// 返回状态码200,并将用户列表作为响应体返回
return ServerResponse.ok().body(users);
}
// 处理创建用户的请求
public ServerResponse createUser(ServerRequest request) throws ServletException, IOException {
// 从请求体中解析出User对象
User user = request.body(User.class);
log.info("创建用户:{}", user);
// 实际应用中这里可以进行数据库操作等业务逻辑
// 返回状态码200
return ServerResponse.ok().build();
}
// 处理删除用户的请求
public ServerResponse deleteUser(ServerRequest request) {
// 从路径变量中获取用户ID
String id = request.pathVariable("id");
log.info("删除用户:{}", id);
// 实际应用中这里可以进行数据库操作等业务逻辑
// 返回状态码200
return ServerResponse.ok().build();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 3.2 函数式Web配置类
这个配置类定义了路由信息,将HTTP请求映射到具体的处理器方法上。
- GET /user/1 获取1号用户
- GET /users 获取所有用户
- POST /user 请求体携带JSON,新增一个用户
- PUT /user/1 请求体携带JSON,修改1号用户
- DELETE /user/1 删除1号用户
@Configuration
public class WebFunctionConfig {
@Bean
public RouterFunction<ServerResponse> router(UserBizHandler userBizHandler) {
return RouterFunctions.route()
// 定义处理GET请求的路由,路径为/user/{id}
.GET("/user/{id}", RequestPredicates.accept(MediaType.ALL), userBizHandler::getUser)
// 定义处理GET请求的路由,路径为/users
.GET("/users", RequestPredicates.accept(MediaType.ALL), userBizHandler::getUsers)
// 定义处理POST请求的路由,路径为/user,仅接受JSON格式的请求体
.POST("/user", RequestPredicates.accept(MediaType.APPLICATION_JSON), userBizHandler::createUser)
// 定义处理DELETE请求的路由,路径为/user/{id}
.DELETE("/user/{id}", RequestPredicates.accept(MediaType.ALL), userBizHandler::deleteUser)
.build();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
可以知道使用函数式 Web,接收的参数是 ServerRequest,可以从 ServerRequest 里取出参数或者请求头。返回值必须是 ServerResponse,该类是 Spring Boot 针对函数式 Web 封装好的响应类。
请求 /user/1
,则走入 /user/{id}
的处理方式,即进入 userBizHandler 的 getUser()
方法里。
RequestPredicates.accept(MediaType.APPLICATION_JSON)
代表该请求只接受 JSON 数据,如果不是 JSON 数据,则不会进入对应的方法里。
函数式 Web工作的细节
- 路由接收参数:在配置类中,我们定义了路由规则,这包括请求的类型(如GET、POST)、请求的路径(如
/user/{id}
),以及请求的处理函数。我们还可以指定请求谓词,比如期望的媒体类型(RequestPredicates.accept(MediaType.APPLICATION_JSON)
),来进一步限制哪些请求应该被映射到处理函数。 - 封装在ServerRequest:当一个请求到达并匹配到一个路由规则时,Spring Framework会自动封装该请求到一个
ServerRequest
对象中。ServerRequest
对象提供了许多便利的方法来访问请求中的数据,例如路径变量、查询参数、请求体等。 - 处理函数处理请求:路由规则指定的处理函数接收封装了请求数据的
ServerRequest
对象作为参数。这个处理函数负责执行实际的业务逻辑,比如查询数据库、执行业务计算等。处理完成后,处理函数会构造一个ServerResponse
对象来封装响应数据,并返回这个对象。 - 发送响应:最后,Spring Framework将
ServerResponse
对象中的数据发送回客户端,完成响应的发送过程。