先看一下tomcat是怎么保存servlet的路径的。理解了这个再理解匹配顺序就很简单了。
每个servlet在tomcat中是由一个wrapper表示的,由这个wrapper去进行servlet的加载,执行等操作。所以添加一个servlet到context中就是添加一个wrapper。tomcat根据servlet配置的映射路径把wrapper分成四类。
name表示之后用于匹配的部分。
- 以/*结束:Wildcard wrapper。 name是 / *之前的部分
- 以*.开始:Extension wrapper 。 name是 *.之后的部分
- /:Default wrapper。 name是空字符串
- 其余的:Exact wrapper。 如果映射路径为空,把name设为/,其它就是path本身
匹配顺序
匹配servlet的过程就是根据url去匹配上面提到的name的过程。
- 匹配exact wrapper。完全匹配。
- 匹配Wildcard wrapper。前缀匹配。
- 匹配Extension wrapper。后缀名匹配。
- 匹配Default wrapper。默认匹配。
这样区分/ 和 /* 就很简单了。 / 会把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;
}
}
}
}
匹配部分
只展示完全匹配的代码
主要是对url为/的特殊处理。
// org/apache/catalina/mapper/Mapper.java
private final void internalMapExactWrapper
(MappedWrapper[] wrappers, CharChunk path, MappingData mappingData) {
MappedWrapper wrapper = exactFind(wrappers, path);
if (wrapper != null) {
mappingData.requestPath.setString(wrapper.name);
mappingData.wrapper = wrapper.object;
if (path.equals("/")) {
// Special handling for Context Root mapped servlet
mappingData.pathInfo.setString("/");
mappingData.wrapperPath.setString("");
// This seems wrong but it is what the spec says...
mappingData.contextPath.setString("");
} else {
mappingData.wrapperPath.setString(wrapper.name);
}
}
}