一、重要组件介绍
- Mapper: 映射关系最核心的、最重要的类。完成url与Host,Context,Wrapper映射关系的初始化、变更、存储及映射
- MapperListener: 实现了ContainerListener与 LifecycleListener接口,监听tomcat组件的变化,当有Host,Context及Wrapper变更时,调用Mapper相关方法,增加或者删除Host,Context,Wrapper等
- MappingData: url映射后的数据,表示一个url具体映射到哪个host,哪个context,哪个wrapper上
二、初始化Mapper
1、初始化时机
- StandardService初始化的时候会调用MapperListener#startInternal
- MapperListener在初始化的时候startInternal会遍历并注册所有的host,将hosts,contexts注册到Mapper中
2、初始化
Mapper#addHost
public synchronized void addHost(String name, String[] aliases,
Host host) {
// 如果name 以 *. 打头,则去掉 *. ,不具体上代码了
// 因此,当用户请求的Host为 abc.def.ge.com时,可能匹配abc.def.ge.com,也可能会匹配def.ge.com
name = renameWildcardHost(name);
MappedHost[] newHosts = new MappedHost[hosts.length + 1];
MappedHost newHost = new MappedHost(name, host);
// 将新的MapperHost插入MapperHost数组中,
// 如果没有以name命名的MapperHost,则插入,返回true,如果有相同的名字,则返回失败。
if (insertMap(hosts, newHosts, newHost)) {
// 插入成功,用新的的数组替换原来的MapperHost数组
hosts = newHosts;
// 如果是默认的host,则设为defaultHost
if (newHost.name.equals(defaultHostName)) {
defaultHost = newHost;
}
if (log.isDebugEnabled()) {
log.debug(sm.getString("mapper.addHost.success", name));
}
} else {
// 说明有相同名字的MapperHost,找到已经存在的Host的对象,根据相同或者不同,打不同的日志
MappedHost duplicate = hosts[find(hosts, name)];
if (duplicate.object == host) {
// The host is already registered in the mapper.
// E.g. it might have been added by addContextVersion()
if (log.isDebugEnabled()) {
log.debug(sm.getString("mapper.addHost.sameHost", name));
}
newHost = duplicate;
} else {
log.error(sm.getString("mapper.duplicateHost", name,
duplicate.getRealHostName()));
// Do not add aliases, as removeHost(hostName) won't be able to
// remove them
return;
}
}
// 如果有别名,处理别名,根据这个Host对应的别名,根据新的MapperHost(readHost相同),插入MapperHost数组中。
// 并且对当前的Mapperhost对象设置aliasHost属性。
List<MappedHost> newAliases = new ArrayList<>(aliases.length);
for (String alias : aliases) {
alias = renameWildcardHost(alias);
MappedHost newAlias = new MappedHost(alias, newHost);
if (addHostAliasImpl(newAlias)) {
newAliases.add(newAlias);
}
}
// 将aliasHost增加到Mapper的 hosts数组中
newHost.addAliases(newAliases);
}
Mapper#addContextVersion
public void addContextVersion(String hostName, Host host, String path,
String version, Context context, String[] welcomeResources,
WebResourceRoot resources, Collection<WrapperMappingInfo> wrappers) {
// 将 *. 开头的去掉 *
hostName = renameWildcardHost(hostName);
// 根据hostName查找相应的host
MappedHost mappedHost = exactFind(hosts, hostName);
if (mappedHost == null) {
// 如果没有找到,就增加一个
addHost(hostName, new String[0], host);
// 再次查找一次
mappedHost = exactFind(hosts, hostName);
// 这个应该不会发生,因为当不存在时,根据上面的addHost的代码,发现肯定会增加一个以hostName命名的MappedHost
if (mappedHost == null) {
log.error("No host found: " + hostName);
return;
}
}
// 如果是一个aliasHost,不处理
if (mappedHost.isAlias()) {
log.error("No host found: " + hostName);
return;
}
// 获取 path,即url中 “/”的长度
int slashCount = slashCount(path);
synchronized (mappedHost) {
// 新建一个ContextVersion,并且如果有wrappers,也增加到相应的ContextVersion中
ContextVersion newContextVersion = new ContextVersion(version,
path, slashCount, context, resources, welcomeResources);
if (wrappers != null) {
// 增加wrapper到ContextVersion中
addWrappers(newContextVersion, wrappers);
}
ContextList contextList = mappedHost.contextList;
// 从这个可以看出,contextList中不同的MapperContext是通过path区分的,而这个path就是context的path
MappedContext mappedContext = exactFind(contextList.contexts, path);
// 如是没有这个path的mapperContext
if (mappedContext == null) {
// 新建一个mapperContext,并获取已有的mappedContext与这个mappedContext的新的组合
mappedContext = new MappedContext(path, newContextVersion);
ContextList newContextList = contextList.addContext(
mappedContext, slashCount);
// 如果不为空时,更新mapperHost。
// 当mappedContext为空时,会返回null,而看上面,是查找不到的时候才会填加,因此,这里正常情况下不为null
if (newContextList != null) {
updateContextList(mappedHost, newContextList);
// 并将这个context 与 ContextVersion建立关联
contextObjectToContextVersionMap.put(context, newContextVersion);
}
} else {
// mappedContext 不为空,说明不是第一次添加,则直接将这个version的context增加到所有具有相同path的context中,
// 形成多version的MappedContext
ContextVersion[] contextVersions = mappedContext.versions;
ContextVersion[] newContextVersions = new ContextVersion[contextVersions.length + 1];
if (insertMap(contextVersions, newContextVersions,
newContextVersion)) {
mappedContext.versions = newContextVersions;
contextObjectToContextVersionMap.put(context, newContextVersion);
} else {
// Re-registration after Context.reload()
// Replace ContextVersion with the new one
int pos = find(contextVersions, version);
if (pos >= 0 && contextVersions[pos].name.equals(version)) {
contextVersions[pos] = newContextVersion;
contextObjectToContextVersionMap.put(context, newContextVersion);
}
}
}
}
}
Mapper#addWrapper
protected void addWrapper(ContextVersion context, String path,
Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) {
synchronized (context) {
if (path.endsWith("/*")) {
// Wildcard wrapper
// 通配符匹配的 wrapper,放到通配符匹配的 mappedWrapper中
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);
// 将ContextVersion的nesting值设置为path的 “/”的最大数量
if (slashCount > context.nesting) {
context.nesting = slashCount;
}
}
// 扩展名匹配,将结果放到扩展名匹配的 mappedwrapper中
} 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;
}
// 默认Wrapper
} else if (path.equals("/")) {
// Default wrapper
MappedWrapper newWrapper = new MappedWrapper("", wrapper,
jspWildCard, resourceOnly);
context.defaultWrapper = newWrapper;
// 精确匹配的wrapper
} 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;
}
}
}
}
- addHost
- 对host的名字做处理,如果以 *. 打头,去掉 *.
- 将host插入已存在的MappedHost数组中
- 插入成功:当hostName不存在时,会插入成功
- 插入失改:打日志,并退出
- 处理别名
- 根据别名,新建MappedHost,增加到原来的MappedHost数组中
- 将对应的aliasHost与原来的realHost建立关联
- addContextVersion
- 查找MappedHost,如果没有找到,就新建一个
- 如果找到了,但是是aliasHost,则不进行处理,退出
- 新建ContextVersion
- 根据path,查找MappedContext
- 找到,将ContextVersion 加入到MappedContext中
- 没有找到,新建一个,将Context加入到MappedContext中
- 查找MappedHost,如果没有找到,就新建一个
- addWrapper
- 如果是前缀匹配:即path 以 /* 结尾,则将之放到前缀匹配的MappedWrapper数组中(当数组中已经存在该匹配字符串时,不能再进行插入,先插入的先生效)
- 扩展名匹配:即path以 *. 打头,将之放到扩展名匹配的MappedWrapper数组中(先插入的生效,同path不能再次插入)
- 默认Wrapper:path 等于”/”,表示默认的匹配,后配置的生效
- 精确匹配:除上述以前的,放到精确匹配MappedWrapper数组中(先插入的生效,同path的不能重复插入)
三、调用Mapper
CoyoteAdapter#postParseRequest
connector.getService().getMapper().map(serverName, decodedURI,
version, request.getMappingData());
- request.getMappingData()是将mappingData作为参数
- 解析后的映射关系都会存入mappingData
- 通过调用request.getContext,request.getHost获取请求的host和context
- 调用request.getContext,request.getHost其实是在调用mappingData.host,mappingData.context
Mapper#map
public void map(MessageBytes host, MessageBytes uri, String version,
MappingData mappingData) throws IOException {
if (host.isNull()) {
String defaultHostName = this.defaultHostName;
if (defaultHostName == null) {
return;
}
host.getCharChunk().append(defaultHostName);
}
host.toChars();
uri.toChars();
internalMap(host.getCharChunk(), uri.getCharChunk(), version, mappingData);
}
Mapper#internalMap
private final void internalMap(CharChunk host, CharChunk uri,
String version, MappingData mappingData) throws IOException {
// 如果mappingData.host不为空,说明mappingData是有问题的。抛出异常
if (mappingData.host != null) {
// The legacy code (dating down at least to Tomcat 4.1) just
// skipped all mapping work in this case. That behaviour has a risk
// of returning an inconsistent result.
// I do not see a valid use case for it.
throw new AssertionError();
}
// Virtual host mapping
MappedHost[] hosts = this.hosts;
// 查找的MappedHost,可以分为三步
// 1. 根据当前的hostName查找具体的host
// 2. 如果没有找到的话,就去掉第一个 "."之前的部分,再次查找。
// 例如,当输入时 abc.bde.ef.com,时,如果没有找到相应匹配的host时,则查找bde.ef.com。
// 因为看到在构建mapper,增加host上,当有会把host name的前导* 去掉的。
// 3.如果还没有找到,则设置为 默认的host
MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
if (mappedHost == null) {
// Note: Internally, the Mapper does not use the leading * on a
// wildcard host. This is to allow this shortcut.
int firstDot = host.indexOf('.');
if (firstDot > -1) {
int offset = host.getOffset();
try {
host.setOffset(firstDot + offset);
mappedHost = exactFindIgnoreCase(hosts, host);
} finally {
// Make absolutely sure this gets reset
host.setOffset(offset);
}
}
if (mappedHost == null) {
mappedHost = defaultHost;
if (mappedHost == null) {
return;
}
}
}
mappingData.host = mappedHost.object;
if (uri.isNull()) {
// Can't map context or wrapper without a uri
return;
}
uri.setLimit(-1);
// Context mapping
// 根据url选择相应的Context,选择的依据是 以path作为url的前缀,并且contextPath 一定是url中 “/”分割的。
// 比如,url为 /test/abc/def ,contextpath 分别为 /test/abc 与/test/abcd,则会选择前者
// 当没有匹配时,如果有默认的context,则选择默认的context,否则直接返回
// Context mapping
ContextList contextList = mappedHost.contextList;
MappedContext[] contexts = contextList.contexts;
int pos = find(contexts, uri);
if (pos == -1) {
return;
}
int lastSlash = -1;
int uriEnd = uri.getEnd();
int length = -1;
boolean found = false;
MappedContext context = null;
while (pos >= 0) {
context = contexts[pos];
if (uri.startsWith(context.name)) {
length = context.name.length();
if (uri.getLength() == length) {
found = true;
break;
} else if (uri.startsWithIgnoreCase("/", length)) {
found = true;
break;
}
}
if (lastSlash == -1) {
lastSlash = nthSlash(uri, contextList.nesting + 1);
} else {
lastSlash = lastSlash(uri);
}
uri.setEnd(lastSlash);
pos = find(contexts, uri);
}
uri.setEnd(uriEnd);
// 如果没有找到,如果第一个context的name为空,即全匹配时,则设置为默认的context
if (!found) {
if (contexts[0].name.equals("")) {
context = contexts[0];
} else {
context = null;
}
}
if (context == null) {
return;
}
//设置 context的路径
mappingData.contextPath.setString(context.name);
// 匹配version, 如果有传version,则匹配相应的version,如果没有传或者没有匹配上,则匹配版本最大的version
ContextVersion contextVersion = null;
ContextVersion[] contextVersions = context.versions;
final int versionCount = contextVersions.length;
if (versionCount > 1) {
Context[] contextObjects = new Context[contextVersions.length];
for (int i = 0; i < contextObjects.length; i++) {
contextObjects[i] = contextVersions[i].object;
}
mappingData.contexts = contextObjects;
if (version != null) {
contextVersion = exactFind(contextVersions, version);
}
}
if (contextVersion == null) {
// Return the latest version
// The versions array is known to contain at least one element
contextVersion = contextVersions[versionCount - 1];
}
mappingData.context = contextVersion.object;
mappingData.contextSlashCount = contextVersion.slashCount;
// 最后匹配 Wrapper
// Wrapper mapping
if (!contextVersion.isPaused()) {
internalMapWrapper(contextVersion, uri, mappingData);
}
}
- 匹配Host(不区分大小写)
- 匹配 输入的hostName
- 没有匹配上 ,将hostName第一个“.”及其之前的内容去掉,再次进行匹配
- 没有匹配上,采用默认的host
- 匹配Context
- 根据url与context的path进行匹配(前缀匹配)。
- 没有匹配上,如果第一个context的path 为空,则采用第一个
- 没有匹配上,则返回
- 匹配ContextVersion
- 根据传入的version进行精确匹配
- 当没有找到时,就采用最大的version对应的ContextVersion
- 匹配Wrapper
- 先找精确匹配Wrapper
- 当没有找到时,再找扩展名匹配
- 当没有找到时,再找前缀匹配
- 当没有找到时,根据欢迎文件查找
- 当没有找到时,采用默认的Wrapper