1. 概述
Spring 5 引入了全新的 PathPatternParser
用于解析 URI 模板,这是对传统 AntPathMatcher
的升级。
✅ 核心改进点:
AntPathMatcher
采用 Ant 风格路径匹配PathPatternParser
将路径拆分为PathElements
链表结构PathPattern
类基于此实现高效模式匹配- 新增 URI 变量语法支持
本文将系统讲解 Spring 5.0 WebFlux 中新增的 URL 模式匹配器,以及历史版本中延续的经典匹配规则。
2. Spring 5.0 新增 URL 模式匹配器
Spring 5.0 新增了极其便捷的 URI 变量语法:{*foo}
,可捕获路径末尾的任意数量路径段。
2.1 使用 Handler 方法实现 {*foo}
语法
通过 @GetMapping
和 Handler 方法演示 *id
模式:路径中 /spring5
之后的所有内容都会被存入 id
变量。
@GetMapping("/spring5/{*id}")
public String URIVariableHandler(@PathVariable String id) {
return id;
}
测试用例验证多级路径捕获:
@Test
public void givenHandlerMethod_whenMultipleURIVariablePattern_then200() {
client.get()
.uri("/spring5/baeldung/tutorial")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.equals("/baeldung/tutorial");
client.get()
.uri("/spring5/baeldung")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.equals("/baeldung");
}
2.2 使用 RouterFunction 实现 {*foo}
语法
通过 RouterFunction
演示新语法:/test
后的完整路径会被捕获到 id
变量。
private RouterFunction<ServerResponse> routingFunction() {
return route(GET("/test/{*id}"),
serverRequest -> ok().body(fromValue(serverRequest.pathVariable("id"))));
}
测试用例验证多级路径捕获:
@Test
public void givenRouter_whenMultipleURIVariablePattern_thenGotPathVariable()
throws Exception {
client.get()
.uri("/test/ab/cd")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("/ab/cd");
}
2.3 使用 {*foo}
语法访问资源
资源访问场景下,路径模式 /files/{*filepaths}
的匹配规则:
- 路径
/files/hello.txt
→filepaths
=/hello.txt
- 路径
/files/test/test.txt
→filepaths
=/test/test.txt
资源访问的 RouterFunction 实现:
private RouterFunction<ServerResponse> routingFunction() {
return RouterFunctions.resources(
"/files/{*filepaths}",
new ClassPathResource("files/")));
}
测试用例验证资源访问(假设 hello.txt
内容为 "hello",test.txt
内容为 "test"):
@Test
public void givenResources_whenAccess_thenGot()
throws Exception {
client.get()
.uri("/files/test/test.txt")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("test");
client.get()
.uri("/files/hello.txt")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("hello");
}
3. 历史版本中的经典 URL 模式
以下模式在 Spring 历史版本中已支持,且同时适用于 RouterFunction
和 @GetMapping
。
3.1 ?
匹配单个字符
模式 /t?st
的匹配规则:
- ✅ 匹配:
/test
、/tast
- ❌ 不匹配:
/tst
、/teest
RouterFunction 实现与测试:
private RouterFunction<ServerResponse> routingFunction() {
return route(GET("/t?st"),
serverRequest -> ok().body(fromValue("Path /t?st is accessed")));
}
@Test
public void givenRouter_whenGetPathWithSingleCharWildcard_thenGotPathPattern()
throws Exception {
client.get()
.uri("/test")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("Path /t?st is accessed");
}
3.2 *
匹配路径段内 0 或多个字符
模式 /baeldung/*Id
的匹配示例:
- ✅ 匹配:
/baeldung/Id
、/baeldung/tutorialId
、/baeldung/articleId
RouterFunction 实现与测试:
private RouterFunction<ServerResponse> routingFunction() {
return route(
GET("/baeldung/*Id"),
serverRequest -> ok().body(fromValue("/baeldung/*Id path was accessed")));
}
@Test
public void givenRouter_whenGetMultipleCharWildcard_thenGotPathPattern()
throws Exception {
client.get()
.uri("/baeldung/tutorialId")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("/baeldung/*Id path was accessed");
}
3.3 **
匹配 0 或多个完整路径段
模式 /resources/**
可匹配 /resources/
后的任意路径:
private RouterFunction<ServerResponse> routingFunction() {
return RouterFunctions.resources(
"/resources/**",
new ClassPathResource("resources/")));
}
@Test
public void givenRouter_whenAccess_thenGot() throws Exception {
client.get()
.uri("/resources/test/test.txt")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("content of file test.txt");
}
3.4 路径变量中使用正则 {baeldung:[a-z]+}
模式 /{baeldung:[a-z]+}
要求变量值匹配正则表达式:
private RouterFunction<ServerResponse> routingFunction() {
return route(GET("/{baeldung:[a-z]+}"),
serverRequest -> ok()
.body(fromValue("/{baeldung:[a-z]+} was accessed and "
+ "baeldung=" + serverRequest.pathVariable("baeldung"))));
}
@Test
public void givenRouter_whenGetRegexInPathVarible_thenGotPathVariable()
throws Exception {
client.get()
.uri("/abcd")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("/{baeldung:[a-z]+} was accessed and "
+ "baeldung=abcd");
}
3.5 单路径段多变量 {var1}_{var2}
⚠️ 注意:Spring 5 要求多变量必须用分隔符区分,否则无法正确解析:
private RouterFunction<ServerResponse> routingFunction() {
return route(
GET("/{var1}_{var2}"),
serverRequest -> ok()
.body(fromValue( serverRequest.pathVariable("var1") + " , "
+ serverRequest.pathVariable("var2"))));
}
@Test
public void givenRouter_whenGetMultiplePathVaribleInSameSegment_thenGotPathVariables()
throws Exception {
client.get()
.uri("/baeldung_tutorial")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("baeldung , tutorial");
}
4. 总结
本文系统梳理了 Spring 5 新增的 URL 匹配特性及历史版本中的经典模式。实际开发中踩坑提示:
- 新语法
{*foo}
在路径捕获场景下非常高效 - 正则匹配需注意性能开销
- 多变量分隔符是必选项,避免解析错误
完整示例代码可参考 GitHub 仓库。