MatrixVariable:矩阵变量
# 220.MatrixVariable:矩阵变量
矩阵变量常用来传递 Cookie
# 前言
假设系统目前有一个查询用的路径:/cars/{path}?xxx=xxx&yyyy=yyyy
,我们称为 queryString,查询字符串,我们通过@RequestParam 注解来获取。
而使用矩阵变量的话,是可以这样子的:/cars/path;low=34,brand=byd,baoma
,这种用分号的我们就称为矩阵变量。
这种矩阵变量有什么用呢?比如我们目前在做 web 开发,但是 Cookie 被禁用了,Session 的内容如何获取?此时我们就可以用矩阵变量的方式来带上 jsessionid,例如 /abc;jsessionid=xxxx
,我们称之为 URL 重写。如果用请求参数的方式来传递 Cookie,那就跟其他请求参数无法区分了;而矩阵变量的方式经常用来传递 Cookie
一些语法规则:
- 矩阵变量应当绑定在路径变量中(RFC3986 的规范)
- 分号前面的请求路径,后面的内容则是矩阵变量
- 若是一个矩阵变量有多个值,应当使用英文逗号进行分隔,或命名多个重复的 key。如:
/cars/sell;low=34;brand=byd,audi,yd
,等价于/cars/sell;low=34;brand=byd;brand=audi;brand=yd
- 在 SpringBoot 中,矩阵变量需要手动开启
# 底层配置原理
我们来看看源码中是如何配置矩阵变量的。首先,WebMvcAutoConfiguration
中,有一个方法:
@Override
@SuppressWarnings("deprecation")
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseSuffixPattern());
configurer.setUseRegisteredSuffixPatternMatch(
this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern());
this.dispatcherServletPath.ifAvailable((dispatcherPath) -> {
String servletUrlMapping = dispatcherPath.getServletUrlMapping();
if (servletUrlMapping.equals("/") && singleDispatcherServlet()) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setAlwaysUseFullPath(true);
configurer.setUrlPathHelper(urlPathHelper);
}
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
其中,第 10 行有个变量叫 UrlPathHelper,点进去看源码:
public class UrlPathHelper {
//......
private boolean removeSemicolonContent = true;
//......
/**
* Set if ";" (semicolon) content should be stripped from the request URI.
* <p>Default is "true".
*/
public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
checkReadOnly();
this.removeSemicolonContent = removeSemicolonContent;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
里面有个变量叫 removeSemicolonContent,其含义是移除分号内容,默认是 true;
还有个 set 方法,上面的注释说明,会将分号后面的内容截取掉(stripped),因此矩阵变量不生效。
除此之外,该类还有很多有用的方法:例如编解码矩阵变量,路径变量,请求字符串等
具体怎么配置呢?我们可以回顾下之前讲 Web 开发简介 的时候,讲过我们可以使用 @Configuration
+ WebMvcConfigurer
自定义规则
WebMvcConfigurer
其实是一个接口,里面定义了一个方法 configurePathMatch
;
public interface WebMvcConfigurer {
default void configurePathMatch(PathMatchConfigurer configurer) {
}
}
2
3
4
而在 WebMvcAutoConfiguration
中,定义了一个静态类,来实现 WebMvcConfigurer
接口:
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer
2
3
4
5
因此,我们也只需定义一个 WebMvcConfigurer 实现类,并放入容器中,就能自定义了。
第一种写法:通过方法返回一个 WebMvcConfigurer 实现类
@Configuration(proxyBeanMethods = false)
public class WebConfig {
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
HiddenHttpMethodFilter filter = new HiddenHttpMethodFilter();
filter.setMethodParam("_m");
return filter;
}
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
//配置路径参数的分隔符
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
//不移除;后面的内容。矩阵变量功能就可以生效
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
第二种写法:定义一个类,实现 WebMvcConfigurer 接口。
@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
HiddenHttpMethodFilter filter = new HiddenHttpMethodFilter();
filter.setMethodParam("_m");
return filter;
}
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
// 不移除分号后面的内容。矩阵变量功能就可以生效
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 前端代码
在 parameter.html 中添加超链接:
<a href="/cars/sell;low=34;brand=byd,audi,yd">测试 @MatrixVariable</a>
<a href="/cars/sell;low=34;brand=byd;brand=audi;brand=yd">测试 @MatrixVariable</a>
<a href="/boss/1;age=20/2;age=10">测试 @MatrixVariable</a>
2
3
# 后端代码
在 ParameterTestController.java 中添加方法:
@GetMapping("/cars/sell")
public Map<String, Object> carsSell(@MatrixVariable("low") Integer low,
@MatrixVariable("brand") List<String > brand) {
Map<String, Object> map = new HashMap<>();
map.put("low", low);
map.put("brand", brand);
return map;
}
2
3
4
5
6
7
8
注意:矩阵变量得是使用路径变量的方式,因此得写成路径变量 /cars/{path}
的形式,然后使用 @MatrixVariable
获取矩阵变量。
同时,我们使用了 @PathVariable("path")
来输出访问路径
运行结果:
那如果有 2 个矩阵变量同名了怎么办?可以指定路径:
@GetMapping("/boss/{bossId}/{empId}")
public Map<String, Object> boss(
@MatrixVariable(value = "age", pathVar = "bossId") Integer bossAge,
@MatrixVariable(value = "age", pathVar = "empId") Integer empAge) {
Map<String, Object> map = new HashMap<>();
map.put("bossAge", bossAge);
map.put("empAge", empAge);
return map;
}
2
3
4
5
6
7
8
9
10
测试效果:
# 源码
已将本文源码上传到 Gitee (opens new window) 或 GitHub (opens new window) 的分支 demo13,读者可以通过切换分支来查看本文的示例代码