本文内容基于Tomcat8
先看一下tomcat是怎么保存servlet的路径的。理解了这个再理解匹配顺序就很简单了。
每个servlet在tomcat中是由一个wrapper表示的,由这个wrapper去进行servlet的加载,执行等操作。简单理解的话把wrapper当成servlet也行。所以添加一个servlet到context中就是添加一个wrapper。tomcat根据servlet配置的映射路径把wrapper分成四类。
MapperWrapper包含一个name和一个wrapper,就像一个k v键值对,用来保存它们之间的映射关系。
首先看看解析web.xml文件,把servlet添加到context的过程。
添加servlet
解析web.xml,添加wrapper到context中的过程大致如下。根据servlet的映射路径得到一个name,把servlet包装成wrapper,把name和wrapper存到一个新的MapperWrapper里面。根据映射路径不同这个MapperWrapper会被存到四个地方:
- 映射路径以/*结束:把MapperWrapper存到wildcardWrappers数组中。 name是 /*之前的部分映射路径以*.开始:把MapperWrapper存到extensionWrappers数组中 。 name是 *.之后的部分映射路径是 /:把MapperWrapper赋值给defaultWrapper。 name是空字符串其余的:把MapperWrapper存到exactWrappers数组中。 如果映射路径为空,把name设为/,其它就是映射路径本身
匹配顺序
从请求url找servlet的过程就是依次遍历图中的这三种MapperWrapper数组和defaultWrapper,根据url匹配name,匹配成功了就返回它对应的wrapper。
- 遍历exactWrappers数组。完全匹配。遍历wildcardWrappers数组。前缀匹配。遍历extensionWrappers数组。后缀名匹配。匹配defaultWrapper。默认匹配。
这样区分/ 和 /* 就很简单了。 / 会把DispatcherServlet保存成Default wrapper,对应默认匹配,/ * 会把DispatcherServlet保存成Wildcard wrapper,对应前缀匹配。所以如果配成/*,那就是会把所有的请求都匹配到DispatcherServlet中。如果配成/,那就会先匹配tomcat自带的一些servlet,比如一些后缀匹配servlet(.jsp之类的)。
源码部分添加servlet部分
// org/apache/catalina/mapper/Mapper.java
protected void addWrapper(ContextVersion context, String path,
Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) {
synchronized (context) {
if (path.endsWith("/*")) {
// Wildcard wrapper
String name = path.substring(0, path.length() - 2);
MappedWrapper newWrapper = new MappedWrapper(name, wrapper,
jspWildCard, resourceOnly);
MappedWrapper[] oldWrappers = context.wildcardWrappers;
MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1];
if (insertMap(oldWrappers, newWrappers, newWrapper)) {
context.wildcardWrappers = newWrappers;
int slashCount = slashCount(newWrapper.name);
if (slashCount > context.nesting) {
context.nesting = slashCount;
}
}
} else if (path.startsWith("*.")) {
// Extension wrapper
String name = path.substring(2);
MappedWrapper newWrapper = new MappedWrapper(name, wrapper,
jspWildCard, resourceOnly);
MappedWrapper[] oldWrappers = context.extensionWrappers;
MappedWrapper[] newWrappers =
new MappedWrapper[oldWrappers.length + 1];
if (insertMap(oldWrappers, newWrappers, newWrapper)) {
context.extensionWrappers = newWrappers;
}
} else if (path.equals("/")) {
// Default wrapper
MappedWrapper newWrapper = new MappedWrapper("", wrapper,
jspWildCard, resourceOnly);
context.defaultWrapper = newWrapper;
} else {
// Exact wrapper
final String name;
if (path.length() == 0) {
// Special case for the Context Root mapping which is
// treated as an exact match
name = "/";
} else {
name = path;
}
MappedWrapper newWrapper = new MappedWrapper(name, wrapper,
jspWildCard, resourceOnly);
MappedWrapper[] oldWrappers = context.exactWrappers;
MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1];
if (insertMap(oldWrappers, newWrappers, newWrapper)) {
context.exactWrappers = newWrappers;
}
}
}
}
匹配部分 (省略了一些代码和Welcome resources的匹配过程)
// org/apache/catalina/mapper/Mapper.java
private final void internalMapWrapper(ContextVersion contextVersion,
CharChunk path,
MappingData mappingData) {
// Rule 1 -- Exact Match
MappedWrapper[] exactWrappers = contextVersion.exactWrappers;
internalMapExactWrapper(exactWrappers, path, mappingData);
// Rule 2 -- Prefix Match
boolean checkJspWelcomeFiles = false;
MappedWrapper[] wildcardWrappers = contextVersion.wildcardWrappers;
internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,
path, mappingData);
// Rule 3 -- Extension Match
MappedWrapper[] extensionWrappers = contextVersion.extensionWrappers;
internalMapExtensionWrapper(extensionWrappers, path, mappingData,
true);
// Rule 4 -- Default servlet
mappingData.wrapper = contextVersion.defaultWrapper.object;
}



