spring mvc静态资源文件匹配规则解读

spring mvc 内部是使用java 的Pattern来匹配,"/"这个分割符是特殊的字符,它将正则表达式和请求的url分割。

比如路径localhost:8080/static/index2.html   

中的url资源将被分割成为static,index.html,对应的表达式比如是/static/*会分割成为static,*两个。

相关的源码在org.springframework.util.AntPathMatcher中。

protected boolean doMatch(String pattern, String path, boolean fullMatch,
			@Nullable Map<String, String> uriTemplateVariables) {

		if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
			return false;
		}

		String[] pattDirs = tokenizePattern(pattern); //分割正则
		if (fullMatch && this.caseSensitive && !isPotentialMatch(path, pattDirs)) {
			return false;
		}

		String[] pathDirs = tokenizePath(path); //分割url

		int pattIdxStart = 0;
		int pattIdxEnd = pattDirs.length - 1;
		int pathIdxStart = 0;
		int pathIdxEnd = pathDirs.length - 1;
.....

正则和path的数组是同时增加的,他们两个根据分割符分割的块是一个个必须与之对应。


		// Match all elements up to the first **
		while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
			String pattDir = pattDirs[pattIdxStart];
			if ("**".equals(pattDir)) {
				break;
			}
			if (!matchStrings(pattDir, pathDirs[pathIdxStart], uriTemplateVariables)) {
				return false;
			}
			pattIdxStart++;
			pathIdxStart++;
		}

当然上面的代码除了一个个必须与之 对应外,唯一的跨"/"的符号是"**"。

spring mvc中正则使用是使用了java的pattern,但是不全是。因为中间使用了替换。相关的代码在org.springframework.util.AntPathMatcher.AntPathStringMatcher中。

是上面AntPathMatcher的内部类。

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));
		}

所以spring mvc中

spring.mvc.static-path-pattern参数的值看着那么简单。并且,spring mvc中通配符只能是*,?,{这三个,相关代码如下:
String[] pattDirs = tokenizePattern(pattern);
		if (fullMatch && this.caseSensitive && !isPotentialMatch(path, pattDirs)) {
			return false;
		}

// 这儿调用isPotentialMatch对正则进行检测,其中有没有可能满足
private boolean isPotentialMatch(String path, String[] pattDirs) {
		if (!this.trimTokens) {
			int pos = 0;
			for (String pattDir : pattDirs) {
				int skipped = skipSeparator(path, pos, this.pathSeparator);  // 跳过"/"字符
				pos += skipped;
				skipped = skipSegment(path, pos, pattDir);
				if (skipped < pattDir.length()) {
					return (skipped > 0 || (pattDir.length() > 0 && isWildcardChar(pattDir.charAt(0)))); // 判断第一个字符是否是系统存在的通配符
				}
				pos += skipped;
			}
		}
		return true;
	}

private static final char[] WILDCARD_CHARS = { '*', '?', '{' };
private boolean isWildcardChar(char c) {
		for (char candidate : WILDCARD_CHARS) {
			if (c == candidate) {
				return true;
			}
		}
		return false;
	}

最后:

   我最初是想对静态资源设置的匹配规则是如果有后缀的是静态资源,没有后缀的就是servlet请求。这样我觉得比较美观(不需要在前面加上一个static)如果上面的localhost:8080/static/index2.html ,并且效率也很高,因为如果是servlet请求在正则匹配的时候就过滤了。看了源码后发现不能实现,因为其中跨"/"的逻辑是

if ("**".equals(pattDir)) {
				break;
			}

只要用了**后面的就无效了,正则表达是又不能实现跨"/"匹配。

附spring boot的spring mvc的我的静态资源配置:

spring.mvc.static-path-pattern=/static/**

spring.resources.static-locations=classpath:/public/,classpath:/page/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值