Spring中有一个doMatch的工具方法,在代码中很多地方都用到过,比如资源加载,Spring MVC中url匹配,那我们写一个main方法来测试一下这个方法。

public class SpringTest1_1 {//默认路径为/public static final String DEFAULT_PATH_SEPARATOR = "/";private static final int CACHE_TURNOFF_THRESHOLD = 65536;private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{[^/]+?\\}");private static volatile Boolean cachePatterns;public static boolean caseSensitive = true;private static boolean trimTokens = true;private static String pathSeparator = "/";private static final Map<String, String[]> tokenizedPatternCache = new ConcurrentHashMap<String, String[]>(256);public static final Map<String, AntPathStringMatcher> stringMatcherCache = new ConcurrentHashMap<String, AntPathStringMatcher>(256);public static void main(String[] args) {String pattern = "/**/a/**/b";String path = "/a/a/b";boolean a = doMatch(pattern, path, true, null);System.out.println(a);}protected static boolean doMatch(String pattern, String path, boolean fullMatch, Map<String, String> uriTemplateVariables) {if (path.startsWith(pathSeparator) != pattern.startsWith(pathSeparator)) {return false;}// 1.1. 分解模式字符串String[] pattDirs = tokenizePattern(pattern);// 1.2 分解路径字符串String[] pathDirs = tokenizePath(path);// pattern的可分配下标 pattIdxStart ~ pattIdxEnd// path的可分配下标    pathIdxStart ~ pathIdxEndint 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) {// 数组中第一个值是pattern,第二个值是path ,后面所有注释都是如此 [/xxx/abc,/xxx/abc]  , [ /xxx/*,/xxx/abc]if (pattIdxStart > pattIdxEnd) {return (pattern.endsWith(pathSeparator) ? path.endsWith(pathSeparator) :!path.endsWith(pathSeparator));}// [/xxx/abc/*,/xxx/abc] , [/xxx/abc/ddd,/xxx/abc] && fullMatch = falseif (!fullMatch) {return true;}// [/*/*/,/abc/] , [/abc/*,/abc/]    &&   fullMatch == trueif (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(pathSeparator)) {return true;}for (int i = pattIdxStart; i <= pattIdxEnd; i++) {// [/*/*/,/abc/] , [/abc/*,/abc ]    &&   fullMatch == true 注意 第二个数组中abc后面没有【/】if (!pattDirs[i].equals("**")) {return false;}}// [/abc/**,/abc]  && fullMatch == truereturn true;}// [/abc/def , /abc/def/ccc ] , [/abc/* , /abc/def/ccc ]else if (pattIdxStart > pattIdxEnd) {return false;}// [/abc/** , /abc/def/ccc ] && fullMatch == falseelse if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {return true;}// 3. 两个字符串数组都从最后的下标开始匹配, 直到遇到pattDir为'**'时结束while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {String pattDir = pattDirs[pattIdxEnd];if (pattDir.equals("**")) {break;}if (!matchStrings(pattDir, pathDirs[pathIdxEnd], uriTemplateVariables)) {// [/xxxx/abcd/**/ddd.class,/xxxx/abcd/xxx.class] && fullMatch == truereturn false;}pattIdxEnd--;pathIdxEnd--;}if (pathIdxStart > pathIdxEnd) {for (int i = pattIdxStart; i <= pattIdxEnd; i++) {if (!pattDirs[i].equals("**")) {// [/**/xxx/bb, /bb]  && fullMatch == truereturn false;}}// 这里返回true 一般字符串为// [/xxxx/abcd/**/*.class, /xxxx/abcd/xxx.class]  && fullMatch == true// 即只有一个**, 而且**没发挥到什么作用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) {// '**/**' situation, so skip one// '**/**' 遇到连续的/**/**就跳过, 因为这没有意义, 一个/**也可以表达多条路径pattIdxStart++;continue;}// Find the pattern between padIdxStart & padIdxTmp in str between// strIdxStart & strIdxEnd// patLength: 两个'**'之间的字符串的长度  /**/a/b/** = 2// strLength: 路径还剩多少个没匹配   /a/b/c/d    如果/a/b都匹配了, 就只剩下/b/c = 2int 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次判断,// [/**/a/b/**/a/b/**/a/b/**,/q/q/q/a/b/q/q/q/a/b/q/q/q/a/b/q/q/q/a/b] &&  fullMatch == truefor (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) {// [/**/a/b/c/**/c,/q/a/b/c ] &&  fullMatch == truereturn false;}pattIdxStart = patIdxTmp;pathIdxStart = foundIdx + patLength;}for (int i = pattIdxStart; i <= pattIdxEnd; i++) {if (!pattDirs[i].equals("**")) {// 实在想不出哪种情况了。以后遇到再来补吧return false;}}// 如果上面的都没有返回值  ....就会在此处返回 [/**, /sdjdd/djkd/] &&  fullMatch == truereturn true;}

上面匹配有4个步骤

  1. 分解模式字符串, 分解路径字符串
  2. 第一个while 循环, 用来判断绝对匹配 /xxx/abc ==> /xxx/abc
  3. 第二个while循环两个字符串数组都从最后的下标开始匹配, 直到遇到pattDir为’**'时结束
  4. 第三个while循环, 主要解决有多个’**'字符串. /**/djdjdjd/**,/a/**/**/b/**/c/**/*.class等
    public static String[] tokenizePattern(String pattern) {String[] tokenized = null;Boolean cachePatterns = SpringTest1_1.cachePatterns;if (cachePatterns == null || cachePatterns.booleanValue()) {tokenized = tokenizedPatternCache.get(pattern);}if (tokenized == null) {tokenized = tokenizePath(pattern);if (cachePatterns == null && tokenizedPatternCache.size() >= 65536) {deactivatePatternCache();return tokenized;}if (cachePatterns == null || cachePatterns.booleanValue()) {tokenizedPatternCache.put(pattern, tokenized);}}return tokenized;}private static void deactivatePatternCache() {cachePatterns = false;tokenizedPatternCache.clear();stringMatcherCache.clear();}protected static String[] tokenizePath(String path) {return tokenizeToStringArray(path, "/", trimTokens, true);}public static String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {if (str == null) {return null;}StringTokenizer st = new StringTokenizer(str, delimiters);List tokens = new ArrayList();while (st.hasMoreTokens()) {String token = st.nextToken();if (trimTokens) {token = token.trim();}if (!ignoreEmptyTokens || token.length() > 0) {tokens.add(token);}}return toStringArray(tokens);}private static boolean matchStrings(String pattern, String str, Map<String, String> uriTemplateVariables) {return getStringMatcher(pattern).matchStrings(str, uriTemplateVariables);}protected static AntPathStringMatcher getStringMatcher(String pattern) {AntPathStringMatcher matcher = null;Boolean cachePatterns = SpringTest1_1.cachePatterns;if (cachePatterns == null || cachePatterns.booleanValue()) {matcher = stringMatcherCache.get(pattern);}if (matcher == null) {matcher = new AntPathStringMatcher(pattern, caseSensitive);if (cachePatterns == null && stringMatcherCache.size() >= CACHE_TURNOFF_THRESHOLD) {// Try to adapt to the runtime situation that we're encountering:// There are obviously too many different patterns coming in here...// So let's turn off the cache since the patterns are unlikely to be reoccurring.deactivatePatternCache();return matcher;}if (cachePatterns == null || cachePatterns.booleanValue()) {stringMatcherCache.put(pattern, matcher);}}return matcher;}protected static class AntPathStringMatcher {private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}");private static final String DEFAULT_VARIABLE_PATTERN = "(.*)";private final Pattern pattern;private final List variableNames = new LinkedList();public AntPathStringMatcher(String pattern) {this(pattern, true);}public AntPathStringMatcher(String pattern, boolean caseSensitive) {StringBuilder patternBuilder = new StringBuilder();Matcher matcher = GLOB_PATTERN.matcher(pattern);int end = 0;while (matcher.find()) {patternBuilder.append(quote(pattern, end, matcher.start()));String match = matcher.group();if ("?".equals(match)) {patternBuilder.append('.');}else if ("*".equals(match)) {patternBuilder.append(".*");}else if (match.startsWith("{") && match.endsWith("}")) {int colonIdx = match.indexOf(':');if (colonIdx == -1) {patternBuilder.append(DEFAULT_VARIABLE_PATTERN);this.variableNames.add(matcher.group(1));}else {String variablePattern = match.substring(colonIdx + 1, match.length() - 1);patternBuilder.append('(');patternBuilder.append(variablePattern);patternBuilder.append(')');String variableName = match.substring(1, colonIdx);this.variableNames.add(variableName);}}end = matcher.end();}patternBuilder.append(quote(pattern, end, pattern.length()));this.pattern = (caseSensitive ? Pattern.compile(patternBuilder.toString()) :Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE));}private String quote(String s, int start, int end) {if (start == end) {return "";}return Pattern.quote(s.substring(start, end));}public boolean matchStrings(String str, Map<String, String> uriTemplateVariables) {Matcher matcher = this.pattern.matcher(str);if (matcher.matches()) {if (uriTemplateVariables != null) {if (this.variableNames.size() != matcher.groupCount()) {throw new IllegalArgumentException("The number of capturing groups in the pattern segment " +this.pattern + " does not match the number of URI template variables it defines, " +"which can occur if capturing groups are used in a URI template regex. " +"Use non-capturing groups instead.");}for (int i = 1; i <= matcher.groupCount(); i++) {String name = this.variableNames.get(i - 1);//使用正则进行匹配String value = matcher.group(i);uriTemplateVariables.put(name, value);}}return true;}else {return false;}}}
}

Spring对不同种类的字符串都做了适配,因此,从一个简单的工具类方法就可以看出Spring的博大精深。

本文的github地址是
[https://github.com/quyixiao/spring_tiny/blob/master/src/main/java/com/spring_1_100/test_1_10/test/SpringTest1_1.java]

Spring源码深度解析(郝佳)-学习-资源匹配-doMatch相关推荐

  1. Spring源码深度解析(郝佳)-学习-源码解析-基于注解bean定义(一)

    我们在之前的博客 Spring源码深度解析(郝佳)-学习-ASM 类字节码解析 简单的对字节码结构进行了分析,今天我们站在前面的基础上对Spring中类注解的读取,并创建BeanDefinition做 ...

  2. Spring源码深度解析(郝佳)-学习-源码解析-创建AOP静态代理实现(八)

    继上一篇博客,我们继续来分析下面示例的 Spring 静态代理源码实现. 静态 AOP使用示例 加载时织入(Load -Time WEaving,LTW) 指的是在虚拟机载入字节码时动态织入 Aspe ...

  3. Spring源码深度解析(郝佳)-学习-源码解析-基于注解切面解析(一)

    我们知道,使用面积对象编程(OOP) 有一些弊端,当需要为多个不具有继承关系的对象引入同一个公共的行为时,例如日志,安全检测等,我们只有在每个对象引用公共的行为,这样程序中能产生大量的重复代码,程序就 ...

  4. Spring源码深度解析(郝佳)-学习-源码解析-Spring MVC(三)-Controller 解析

    在之前的博客中Spring源码深度解析(郝佳)-学习-源码解析-Spring MVC(一),己经对 Spring MVC 的框架做了详细的分析,但是有一个问题,发现举的例子不常用,因为我们在实际开发项 ...

  5. Spring源码深度解析(郝佳)-学习-源码解析-基于注解注入(二)

    在Spring源码深度解析(郝佳)-学习-源码解析-基于注解bean解析(一)博客中,己经对有注解的类进行了解析,得到了BeanDefinition,但是我们看到属性并没有封装到BeanDefinit ...

  6. Spring源码深度解析(郝佳)-学习-源码解析-Spring整合MyBatis

    了解了MyBatis的单独使用过程之后,我们再来看看它也Spring整合的使用方式,比对之前的示例来找出Spring究竟为我们做了什么操作,哪些操作简化了程序开发. 准备spring71.xml &l ...

  7. Spring源码深度解析(郝佳)-学习-源码解析-创建AOP静态代理(七)

    加载时织入(Load-Time Weaving ,LTW) 指的是在虚拟机加载入字节码文件时动态织入Aspect切面,Spring框架的值添加为 AspectJ LTW在动态织入过程中提供了更细粒度的 ...

  8. Spring源码深度解析(郝佳)-学习-构造器注入

    本文主要是Spring源码有一定基础的小伙伴而言的,因为这里我只想讲一下,Spring对于构造器的注入参数是如何解析,不同参数个数构造器. 相同参数个数,不同参数类型. Spring是如何选择的. 1 ...

  9. Spring源码深度解析(郝佳)-学习-Spring消息-整合RabbitMQ及源码解析

      我们经常在Spring项目中或者Spring Boot项目中使用RabbitMQ,一般使用的时候,己经由前人将配置配置好了,我们只需要写一个注解或者调用一个消息发送或者接收消息的监听器即可,但是底 ...

最新文章

  1. SSM框架整合(Spring+SpringMVC+MyBatis)
  2. C++,那些可爱的小陷阱(二)
  3. boost::mpl模块实现deque相关的测试程序
  4. C# 页面调用控制台应用程序
  5. linux下基于jrtplib库的实时传送实现
  6. spring AOP解说
  7. 深圳美景品牌策划机构:美景推动国际品牌MASHIMARO闪电招商,3天签约211家
  8. Ubuntu设置局域网Windows共享文件Samba
  9. CMOS、BIOS介绍
  10. oracle怎么查找数据泵,ORACLE数据泵使用详解
  11. 芯片国产化进程提速 赶超洋品牌核心技术尚欠火候
  12. 超详细的ENSP安装教程附下载地址
  13. 网络设备设置/取消console口登陆单独密码
  14. 科沃斯机器人招股_603486_科沃斯招股说明书.pdf
  15. prism在java_Prism 框架应用-基础知识篇
  16. pyplot显示和保存没有边框的图片
  17. 田园仿古砖特点大汇总,田园风格你选对了吗?
  18. 线性代数(15)——矩阵的QR分解
  19. 60分钟快速掌握RabbitMQ,java面试技巧和注意事项
  20. 清华张钹院士:走向真正的人工智能

热门文章

  1. python二维向量运算模拟_【转载 Python】Numpy基础:数组和矢量运算
  2. tele 安卓Android版,TeleOffice
  3. web大学生个人网站作业模板 HTML期末大作业
  4. DeDeCMS-仿站
  5. 新闻稿撰写要点有哪些?记住这几点
  6. su root 与 su - root的区别
  7. 计算机文件清理教程,史上最详细的C盘清理方法
  8. RBW VBW-解析带宽与视频带宽(转)
  9. 风力发电功率预测数据(两个风力发电场25台风力发电机2年发电数据,时间间隔30min,含风速、风向、外界温度等天气特征)
  10. 开始慢慢长征 红军不怕远征难