转载请注明出处:http://blog.csdn.net/linxdcn/article/details/73863126
1 概述
如果你对Tomcat的整个框架、组件、请求流程不熟悉,建议你先阅读以下3篇Tomcat概述性的文章,再来看本篇文章:
【Tomcat9源码分析】组件与框架概述
【Tomcat9源码分析】生命周期、启动、停止概述
【Tomcat9源码分析】请求过程概述
在Tomcat中,当一个请求到达时,该请求最终由哪个Servlet来处理呢?这个任务是由Mapper路由映射器完成的。Mapper是由Service管理。
2 存储结构
Mapper所用到内部类可用下面这张图概括:
2.1 MapElement
protected abstract static class MapElement<T> {
public final String name; // 名字
public final T object; // 对应的对象,如host, context, wrapper
}
MapElement 是下面所有类的基类。
2.2 MappedHost
protected static final class MappedHost extends MapElement<Host> {
// host包含的context列表,即MappedContext数组的包装
public volatile ContextList contextList;
}
2.3 MappedContext
protected static final class MappedContext extends MapElement<Void> {
// 一个Context可能会对应许多个不同的版本的context,一般情况下是1个
public volatile ContextVersion[] versions;
}
其中ContextVersion包含了Context下的所有Servlet,有多种映射方式,如精确的map,通配符的map,扩展名的map,如下:
protected static final class ContextVersion extends MapElement<Context> {
// 对wrapper的精确的map
public MappedWrapper[] exactWrappers = new MappedWrapper[0];
// 基于通配符的map
public MappedWrapper[] wildcardWrappers = new MappedWrapper[0];
// 基于扩展名的map
public MappedWrapper[] extensionWrappers = new MappedWrapper[0];
}
2.4 MappedWrapper
protected static class MappedWrapper extends MapElement<Wrapper> {
public final boolean jspWildCard;
public final boolean resourceOnly;
}
3 Mapper类设计
简单地说,Mapper中以数组的形式保存了host, context, wrapper, 且他们在数组中有序的,Mapper可以通过请求的url,通过二分法查找定位到wrapper。
public final class Mapper {
// host数组,host里面又包括了context和wrapper数组
volatile MappedHost[] hosts = new MappedHost[0];
// 下面三个是添加host, context, wrapper的函数,都是同步的
// 而且保证添加后是有序的
public synchronized void addHost(String name, String[] aliases, Host host) { }
public void addContextVersion(String hostName, Host host, String path,
String version, Context context, String[] welcomeResources,
WebResourceRoot resources, Collection<WrapperMappingInfo> wrappers) {
}
protected void addWrapper(ContextVersion context, String path,
Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) {
}
// 根据name,查找一个MapElement(host, context, 或者wrapper)
private static final <T> int find(MapElement<T>[] map, CharChunk name,
int start, int end) {
// ...省略
// 核心是二分法
while (true) {
i = (b + a) / 2;
int result = compare(name, start, end, map[i].name);
if (result == 1) {
a = i;
} else if (result == 0) {
return i;
} else {
b = i;
}
if ((b - a) == 1) {
int result2 = compare(name, start, end, map[b].name);
if (result2 < 0) {
return a;
} else {
return b;
}
}
}
}
}
4 实现过程
下面介绍在一次http请求中,哪个环节调用了Mapper作路由映射,已经路由映射的过程。
【Tomcat9源码分析】请求过程概述已经介绍了http请求经过http11Processor
解析之后,会调用CoyoteAdapter
的service()
函数转发给Container
,我们来详细看一下这一步。
public class CoyoteAdapter implements Adapter {
@Override
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
// ...省略
connector.getService().getMapper().map(serverName, decodedURI,
version, request.getMappingData());
}
}
CoyoteAdapter
会获取Connector
中的Service
中的Mapper
,然后调用map()
方法。
public final class Mapper {
public void map(MessageBytes host, MessageBytes uri, String version,
MappingData mappingData) throws IOException {
// ...省略
// 1 调用私有方法internalMap,传入host, uri, version, 结果将会保存在mappingData中
internalMap(host.getCharChunk(), uri.getCharChunk(), version,
mappingData);
}
private final void internalMap(CharChunk host, CharChunk uri,
String version, MappingData mappingData) throws IOException {
// ...省略
// 2 查找映射的host
MappedHost[] hosts = this.hosts;
// 跟mapper的find方法一样,采用二分法查找
MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
mappingData.host = mappedHost.object;
// 3 查找映射的context
// ...省略
contextVersion = exactFind(contextVersions, version);
// 4 查找映射的wrapper
if (!contextVersion.isPaused()) {
internalMapWrapper(contextVersion, uri, mappingData);
}
}
}
- 调用私有方法internalMap,传入host, uri, version, 结果将会保存在request的mappingData中
- 查找映射的host
- 查找映射的context
- 查找映射的wrapper
转载请注明出处:http://blog.csdn.net/linxdcn/article/details/73863126