使用Spring AntPathMatcher的doMatch方法

 更新时间:2021年09月08日 11:07:10   作者:王其狗勾儿痒  
这篇文章主要介绍了使用Spring AntPathMatcher的doMatch方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

AntPathMatcher的doMatch方法

AntPathMatcher.doMatch(...), 是解决模式匹配的源码

有4个步骤

1. 分解模式字符串, 分解路径字符串

2. 第一个while 循环, 用来判断绝对匹配 /xxx/abc ==> /xxx/abc

3. 第二个while循环两个字符串数组都从最后的下标开始匹配, 直到遇到pattDir为'**'时结束

4. 第三个while循环, 主要解决有多个'**'字符串./**/djdjdjd/**, /a/**/**/b/**/c/**/*.class等

// 解决模式匹配的函数, 返回true or false 表示是否匹配
// 参数 pattern: 表示模式字符串
	    path: 文件的路径
	protected boolean doMatch(String pattern, String path, boolean fullMatch, Map<String, String> uriTemplateVariables) {		
		if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
			return false;
		}
 
		1.1. 分解模式字符串
		String[] pattDirs = tokenizePattern(pattern);
		if (fullMatch && this.caseSensitive && !isPotentialMatch(path, pattDirs)) {
			return false;
		}
		
		1.2 分解路径字符串
		String[] pathDirs = tokenizePath(path);		
		// pattern的可分配下标 pattIdxStart ~ pattIdxEnd
		// path的可分配下标	pathIdxStart ~ pathIdxEnd
		int pattIdxStart = 0;
		int pattIdxEnd = pattDirs.length - 1;
		int pathIdxStart = 0;
		int pathIdxEnd = pathDirs.length - 1;
 
		// Match all elements up to the first **
		// 2. 第一个while 循环, 用来判断绝对匹配的   /xxx/abc ==> /xxx/abc
		// 两个字符串都从下标0开始, 直到模式字符串遇到**结束
		while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
			String pattDir = pattDirs[pattIdxStart];
			if ("**".equals(pattDir)) {
				break;
			}
			if (!matchStrings(pattDir, pathDirs[pathIdxStart], uriTemplateVariables)) {
				return false;
			}
			pattIdxStart++;
			pathIdxStart++;
		}
 
		// pathIdxStart > pathIdEnd, 表示文件路径(path), 已经逐一的匹配到了
		if (pathIdxStart > pathIdxEnd) {
			
			/*
			// Path is exhausted, only match if rest of pattern is * or **'s
			if (pattIdxStart > pattIdxEnd) {
				// 判断最后一个字符是否为'/'
				return (pattern.endsWith(this.pathSeparator) == path.endsWith(this.pathSeparator));
			}
			if (!fullMatch) {
				return true;
			}
			if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) {
				return true;
			}
			// 不会执行到这里
			for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
				if (!pattDirs[i].equals("**")) {
					return false;
				}
			}
			*/
			// 这里返回true 一般是相等的字符串匹配(长度相同)
			// /abc/zzzz ==> /abc/zzzz
			return true;
		}
		
		/*
		else if (pattIdxStart > pattIdxEnd) {
			// String not exhausted, but pattern is. Failure.
			return false;
		}
		else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {
			// Path start definitely matches due to "**" part in pattern.
			return true;
		}*/
		
		// 3. 两个字符串数组都从最后的下标开始匹配, 直到遇到pattDir为'**'时结束
		while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
			String pattDir = pattDirs[pattIdxEnd];
			if (pattDir.equals("**")) {
				break;
			}
			if (!matchStrings(pattDir, pathDirs[pathIdxEnd], uriTemplateVariables)) {
				return false;
			}
			pattIdxEnd--;
			pathIdxEnd--;
		}
		if (pathIdxStart > pathIdxEnd) {
			for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
				if (!pattDirs[i].equals("**")) {
					return false;
				}
			}
			// 这里返回true 一般字符串为 
			// /xxxx/abcd/**/*.class => /xxxx/abcd /xxx.class 
			// 即只有一个**, 而且**没发挥到什么作用 
			// 测试
			// AntPathMatcher ant = new AntPathMatcher("/");
			//String pattern = "/abc/**/*.class";
			//String path = "/abc/ddd.class";
			//System.out.println(ant.match(pattern, path));
			return true;
		}
 
		// 4. 第3个while循环, 主要解决有多个'**'字符串.	/**/djdjdjd/**, /a/**/**/b/**/c/**/*.class等
		// 每次下标又从pattIdxStart+1开始
		while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
			int patIdxTmp = -1;	// 这个用来指向**的位置
			for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
				if (pattDirs[i].equals("**")) {
					patIdxTmp = i;
					break;
				}
			}
			if (patIdxTmp == pattIdxStart + 1) {
				// '**/**' 遇到连续的/**/**就跳过, 因为这没有意义, 一个/**也可以表达多条路径
				pattIdxStart++;
				continue;
			}
			// patLength: 两个'**'之间的字符串的长度  /**/a/b/** = 2
			// strLength: 路径还剩多少个没匹配 	/a/b/c/d	如果/a/b都匹配了, 就只剩下/b/c = 2
			int patLength = (patIdxTmp - pattIdxStart - 1);
			int strLength = (pathIdxEnd - pathIdxStart + 1);
			int foundIdx = -1;
 
			strLoop:
			// 因为已经确定了有 /**/a/b/**这样的模式字符串存在, 中间2长度
			// 如果存在/q/a/b/c/d 有5个长度, 那么就要循环3次
			// 第一次匹配 /a/b => /q/a
			// 第二次	 /a/b => /a/b	=> 这里已经匹配到了, 所以就break了
			// 			 /a/b => /b/c
			// 			 /a/b => /c/d
			// 当然, 如果存在更复杂的如: /**/a/b/**/a/b/**/a/b/**, 外层的while循环就会做3次判断, 
			//String pattern = "/**/a/b/**/a/b/**/a/b/**";
			//String path = "/q/q/q/a/b/q/q/q/a/b/q/q/q/a/b/q/q/q/a/b";
			for (int i = 0; i <= strLength - patLength; i++) {
				for (int j = 0; j < patLength; j++) {
					String subPat = pattDirs[pattIdxStart + j + 1];
					String subStr = pathDirs[pathIdxStart + i + j];
					if (!matchStrings(subPat, subStr, uriTemplateVariables)) {
						continue strLoop;
					}
				}
				foundIdx = pathIdxStart + i;
				break;
			}
 
			if (foundIdx == -1) {
				return false;
			}
 
			pattIdxStart = patIdxTmp;
			pathIdxStart = foundIdx + patLength;
		}
 
		for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
			if (!pattDirs[i].equals("**")) {
				return false;
			}
		}
		//如果上面的都没有返回值 /** => /sdjdd/djkd/.....就会在此处返回
        // 当然还有更多的
		return true;
	}

Spring的AntPathMatcher工具类用法

AntPathMatcher

是org.springframework.util工具包下的方法。

 /**
  * A convenient, alternative constructor to use with a custom path separator.
  * @param pathSeparator the path separator to use, must not be {@code null}.
  * @since 4.1
  */
 public AntPathMatcher(String pathSeparator) {
  Assert.notNull(pathSeparator, "'pathSeparator' is required");
  this.pathSeparator = pathSeparator;
  this.pathSeparatorPatternCache = new PathSeparatorPatternCache(pathSeparator);
 }
public boolean hasUrl(String url) {
    if (url == null || "".equals(url)) {
    return false;
}
 
AntPathMatcher antPathMatcher = new AntPathMatcher();
// 可根据需求做动态匹配
String pattern = "/app/*.html"
if (antPathMatcher.match(pattern, url)) {
   // 根据入参url和pattern匹配上返回ture,否则false.
     return true;
 }
   return false;
}

下面是模糊匹配规则

也就是在响应的路径上添加* 或则 ** 对路径进行替代即可。

URL路径 说明
/app/*.x 匹配(Matches)所有在app路径下的.x文件
/app/p?ttern 匹配(Matches) /app/pattern 和 /app/pXttern,但是不包括/app/pttern
/**/example 匹配(Matches) /app/example, /app/foo/example, 和 /example
/app/**/dir/file. 匹配(Matches) /app/dir/file.jsp, /app/foo/dir/file.html,/app/foo/bar/dir/file.pdf, 和 /app/dir/file.java
/**/*.jsp 匹配(Matches)任何的.jsp 文件

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • java的Console类的使用方法及实例

    java的Console类的使用方法及实例

    这篇文章主要介绍了java的Console类的使用方法及实例的相关资料,需要的朋友可以参考下
    2017-07-07
  • 基于java中两个对象属性的比较

    基于java中两个对象属性的比较

    下面小编就为大家带来一篇基于java中两个对象属性的比较。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • 浅谈maven单元测试设置代理

    浅谈maven单元测试设置代理

    下面小编就为大家带来一篇浅谈maven单元测试设置代理。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • Java8新特性Optional类及新时间日期API示例详解

    Java8新特性Optional类及新时间日期API示例详解

    这篇文章主要为大家介绍了Java8新特性Optional类及新时间日期API示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • SpringBoot应用部署于外置Tomcat容器的方法

    SpringBoot应用部署于外置Tomcat容器的方法

    这篇文章主要介绍了SpringBoot应用部署于外置Tomcat容器的方法,本文分步骤给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-06-06
  • 基于java socket实现 聊天小程序

    基于java socket实现 聊天小程序

    这篇文章主要介绍了基于java socket实现 聊天小程序,代码分为服务器和客户端,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-12-12
  • Java的设计模式编程中迪米特法则的应用示例

    Java的设计模式编程中迪米特法则的应用示例

    这篇文章主要介绍了Java的设计模式编程中迪米特法则的应用示例,迪米特法则中主张创建和使用弱耦合的类,需要的朋友可以参考下
    2016-02-02
  • springboot druid数据库连接池连接失败后一直重连的解决方法

    springboot druid数据库连接池连接失败后一直重连的解决方法

    本文主要介绍了springboot druid数据库连接池连接失败后一直重连的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • Java Collection集合的三种遍历方式详解

    Java Collection集合的三种遍历方式详解

    Collection集合遍历的方式有三种,迭代器foreach/增强for循环lambda表达式,这篇文章主要介绍了Java Collection集合的三种遍历方式,需要的朋友可以参考下
    2022-11-11
  • Java实现lucene搜索功能的方法(推荐)

    Java实现lucene搜索功能的方法(推荐)

    下面小编就为大家带来一篇Java实现lucene搜索功能的方法(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12

最新评论