Spring5新宠:PathPattern,AntPathMatcher:那我走?(上)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Spring5新宠:PathPattern,AntPathMatcher:那我走?(上)

前言


你好,我是YourBatman。


依稀记得3年前的在“玩”Spring WebFlux的时候,看到PathPattern在AbstractHandlerMapping中起到了重要作用:用于URL的匹配。当时就很好奇:这一直不都是AntPathMatcher的活吗?


于是乎我就拿出了自己更为熟悉的Spring WebMvc对于类进行功能比对,发现PathPattern扮演的角色和AntPathMatcher一毛一样,所以当时也就没去深入研究啦。


正所谓念念不忘必有回响。时隔3年最近又回到搞WebFlux了,欠下的债总归要还呀,有必要把PathPattern深入解读,毕竟它是Spring5在路径解析器方面的新宠,贯穿WebFlux上下。重点是号称比AntPathMatcher拥有更好的使用体验以及更快的匹配效率,咦,勾起了兴趣了解一下~


正值周末,说干就干。


所属专栏


点拨-Spring技术栈


本文提纲


image.png


版本约定


  • JDK:8
  • Spring Framework:5.3.x


正文


PathPattern是Spring5新增的API,所在包:org.springframework.web.util.pattern.PathPattern,所属模块为spring-web。可见它专为Web设计的“工具”。


不同于AntPathMatcher是一个“上帝类”把所有活都干了,新的路径匹配器围绕着PathPattern拥有一套体系,在设计上更具模块化、更加面向对象,从而拥有了更好的可读性和可扩展性。


image.png


下面深入了解下该技术体系下的核心元素。 主要有:


  • PathElement:路径元素。一个URL模板根据/可以拆分成N多个路径元素对象
  • PathContainer:URL的结构化表示。一个URL对应一个PathContainer对象实例
  • PathPattern:路径解析的模式。路径模式匹配器的最核心API
  • PathPatternParser:将一个String类型的模式解析为PathPattern实例,这是创建PathPattern实例的唯一方式


PathElement:路径元素


顾名思义,它表示路径节点。一个path会被解析成N多个PathElement节点。


核心属性:


// Since: 5.0
abstract class PathElement {
  protected final int pos;
  protected final char separator;
  @Nullable
  protected PathElement next;
  @Nullable
  protected PathElement prev;
}


  • pos:该节点在path里的起点位置
  • separator:该path使用的分隔符
  • next:后节点,可以为null(如最后一个节点)
  • prev:前节点,可以为null(如第一个节点)


所有的PathElement之间形成链状结构,构成一个完整的URL模板。


Tips:我个人意见,并不需要太深入去了解PathElement内部的具体实现,在宏观角度了解它的定义,然后认识下它的子类实现不同的节点类型即可


它有如下子类实现:


image.png


SeparatorPathElement


分离器元素。代表用于分离的元素(默认是/,也可以是.


@Test
public void test1() {
    PathPatternParser parser = new PathPatternParser();
    PathPattern pathPattern = parser.parse("/api/v1");
    System.out.println(pathPattern);
}


断点调试查看解析后的pathPattern变量拥有的元素情况:

image.png


可以看到这是标准的链式结构嘛,这种关系用图画出来就是这样子:


image.png


其中绿色的/都是SeparatorPathElement类型,蓝色都是LiteralPathElement字面量类型。将一个Pattern拆解成为了一个个的Element对象,后面就可以方便的面向对象编程,大大增加了可读性、降低出错的概率。


说明:由于这是第一个元素,所以才举了个实际的代码示例辅助理解。下面的就只需描述概念啦,举一反三即可~


WildcardPathElement

通配符元素。如:/api/*/yourbatman

说明:在路径中间它至少匹配1个字符(//不行,/ /可行),但在路径末尾可以匹配0个字符


SingleCharWildcardedPathElement

单字符通配符元素。如:/api/your??tman

说明:一个?代表一个单字通配符,若需要适配多个用多个?即可


WildcardTheRestPathElement

通配剩余路径元素。如:/api/yourbatman/**

说明:**只能放在path的末尾,这才是rest剩余的含义嘛


CaptureVariablePathElement

将一段路径作为变量捕获的路径元素。如:/api/yourbatman/{age}

说明:{age}就代表此元素类型被封装进来


CaptureTheRestPathElement

捕获路径其余部分的路径元素。如:/api/yourbatman/{*restPath}

说明:若待匹配的路径是/api/yourbatman/a/b/c,那么restPath=a/b/c


LiteralPathElement

字面量元素。不解释~


RegexPathElement

正则表达式元素。如:api/*_*/*_{age}

说明:*_*和*_{age}都会被解析为该元素类型,这种写法是从AntPathMatcher里派生来过的(但不会依赖于AntPathMatcher)


总之:任何一个字符串的pattern最终都会被解析为若干段的PathElement,这些PathElement以链式结构连接起来用以表示该pattern,形成一个对象数据。不同于AntPathMatcher的纯字符串操作,这里把每一段都使用对象来描述,结构化的表示使得可读性更强、更具灵活性,甚至可以获得更好的性能表现。


PathContainer:URL的结构化表示


和PathPattern类似,待匹配的path的每一段都会表示为一个元素并保存其元数据信息。也就是说:每一个待匹配的URL路径都会被解析为一个PathContainer实例。


PathContainer虽然是个接口,但我们无需关心其实现,类同于Java 8的java.util.stream.Collector接口使用者无需关心其实现一样。因为提供了静态工具方法用于直接生成对应实例。体验一把:


@Test
public void test2() {
    PathContainer pathContainer = PathContainer.parsePath("/api/v1/address", PathContainer.Options.HTTP_PATH);
    System.out.println(pathContainer);
}


debug模式运行,查看pathContainer对象详情:


image.png


这和解析为PathPattern的结构何其相似(不过这里元素们是通过有序的集合组织起来的)。对比看来,拍脑袋应该能够猜到何新版的匹配效率会更高了吧。


补充说明:


  • value和valueToMatch的区别:value是原值,valueToMatch是(处理过的,比如已解码的)最终参与匹配的值
  • parameters代表路径参数。若希望它有值只需使用;号分隔填值即可。如:/api;abc/v1,此参数一般都用不着
  • 因为Http中是允许这样携带参数的,但是目录(.形式)就不能这么写啦


PathPattern:路径解析的模式


表示解析路径的模式。包括用于快速匹配的路径元素链,并累积用于快速比较模式的计算状态。它是直接面向使用者进行匹配逻辑的最重要API,完成match操作。


PathPattern所在包是org.springframework.web.util.pattern.PathPattern,位于spring-web模块,专为web(含webmvc和webflux)设计的全新一套路径匹配API,具有更高的匹配效率。


认识下它的成员属性:


// Since: 5.0
public class PathPattern implements Comparable<PathPattern> {
  // pattern的字符串形式
  private final String patternString;
  // 用于构建本实例的解析器
  private final PathPatternParser parser;
  // 分隔符使用/还是.,默认是/
  private final PathContainer.Options pathOptions;
  // 如果pattern里结尾没/而待匹配的有,仍然让其匹配成功(true),默认是true
  private final boolean matchOptionalTrailingSeparator;
  // 是否对大小写敏感,默认是true
  private final boolean caseSensitive;
  // 链式结构:表示URL的每一部分元素
  @Nullable
  private final PathElement head;
  private int capturedVariableCount;
  private int normalizedLength;
  private boolean endsWithSeparatorWildcard = false;
  private int score;
  private boolean catchAll = false;
}


以上属性是直接读取,下面这些个是计算出来的,比较特殊就特别照顾下:


  • capturedVariableCount:在这个模式中捕获的变量总数。也就是{xxx}或者正则捕获的总数喽
  • normalizedLength:通配符批到的变量长度的总和(关于长度的计算有个约定:如?是1,字面量就是字符串长度),这个变量对提升匹配速度有帮助
  • endsWithSeparatorWildcard:标记该模式是否以隔离符或者通配符*结尾
  • score:分数用于快速比较该模式。不同的模式组件被赋予不同的权重。分数越低越具体,如:捕获到的变量分数值为1,通配符值是100

catchAll:该pattern是否以**或者{*xxx}结尾


score、catchAll等标记用于加速匹配的速度,具体体现PathPattern.SPECIFICITY_COMPARATOR这个比较器上,这是PathPattern速度比AntPathMatcher快的根因之一


值得注意的是:所有属性均不提供public的set方法,也就是说PathPattern实例一旦创建就是只读(不可变)实例了。



相关文章
|
前端开发 Java 编译器
Spring5新宠:PathPattern,AntPathMatcher:那我走?(下)
Spring5新宠:PathPattern,AntPathMatcher:那我走?(下)
Spring5新宠:PathPattern,AntPathMatcher:那我走?(下)
|
2月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
1月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
163 2
|
3月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
9天前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
20 2
 SpringBoot入门(7)- 配置热部署devtools工具
|
6天前
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
17 2
|
4月前
|
Java 测试技术 数据库
Spring Boot中的项目属性配置
本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。
|
1月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
54 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
1月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
168 2
|
1月前
|
数据采集 监控 Java
SpringBoot日志全方位超详细手把手教程,零基础可学习 日志如何配置及SLF4J的使用......
本文是关于SpringBoot日志的详细教程,涵盖日志的定义、用途、SLF4J框架的使用、日志级别、持久化、文件分割及格式配置等内容。
131 0
SpringBoot日志全方位超详细手把手教程,零基础可学习 日志如何配置及SLF4J的使用......