前面提到,tomcat从架构上看,包含Service,Engine,Host,Context,Wrapper。那么,当用户发起一个请求时,tomcat是如何将url映射到具体的Wrapper上的呢,就这是本文要阐述的问题。
与url到Wrapper映射相关的类位于org.apache.catalina.mapper
包下,包含四个类:
Mapper
:映射关系最核心的、最重要的类。完成url与Host,Context,Wrapper映射关系的初始化、变更、存储及映射MapperListener
:实现了ContainerListener
与LifecycleListener
接口,监听tomcat组件的变化,当有Host,Context及Wrapper变更时,调用Mapper相关方法,增加或者删除Host,Context,Wrapper等。MappingData
:url映射后的数据,表示一个url具体映射到哪个host,哪个context,哪个wrapper上。WrapperMappingInfo
:表示一个Wrapper的信息,是一个普通的类,不太重要。
本文章主要介绍Mapper,对其中用到的MapperingData做简要介绍。
Mapper主要功能是完成url到Wrapper的映射,有三个主要的功能
1. 映射关系存储:存储所有的Host,context及Wrapper的对应关系;
2. 映射关系初始化及变更:当新增一个组件或者移除一个组件时,mapper如何维护url到Wrapper的映射关系;
3. 映射关系使用:根据url,映射到具体的host,context和wrapper。
映射关系存储
我们先上结论,再看详细的代码。
总体结论
我们知道,一个Service有一个Engine,而一个Engine中有一个Mapper。根据Engine,Host,Context及Wrapper的对应关系,易得到以下的结论。
- 一个Mapper中,应该保存有多个Host对象(的确是这样的,每个Host对象称之为MappedHost,多个MappedHost以数组形式组合,各元素通过其name进行排序)
- 一个Host对象包含多个context(Context在Mapper中定义为MappedContext,也是通过数组形式存在,并且元素根据MappedContext的命名排序。但是与组件不同的是,每一个Context可以有多个版本,因此每一个MappedContext 包含了多个ContextVersion,每一个MappedContext下的多个ContextVersion表示同一个Context的多个版本)
- 一个Context包含多个Wrapper(此处的Context在Mapper中为ContextVersion,包含多个Wrapper,这些Wrapper分成四类,精确匹配的Wrapper,前缀匹配的Wrapper,扩展名匹配的Wrapper,默认的Wrapper,在Mapper中,每一个Wrapper都通过一个MappedWrapper表示)
因此,Mapper的构成可以用下图表示
详细代码
辅助类MapElement
个人感觉这个名字起的不太好,让人以为是Map,其实就是一个含有名字的Object。
protected abstract static class MapElement<T> {
public final String name;
public final T object;
public MapElement(String name, T object) {
this.name = name;
this.object = object;
}
}
mapper 的属性
//定义所有的Host组合,表示一个Engine下所有Host
volatile MappedHost[] hosts = new MappedHost[0];
/**
* Default host name.
*/
private String defaultHostName = null;
private volatile MappedHost defaultHost = null;
/**
* Mapping from Context object to Context version to support
* RequestDispatcher mappings.
*/
// 没有明白到底干吗用的
private final Map<Context, ContextVersion> contextObjectToContextVersionMap =
new ConcurrentHashMap<>();
MappedHost定义
protected static final class MappedHost extends MapElement<Host> {
// 包含的COntext信息,定义的是一个Object,其实内部是一个数组,下面可以看到
public volatile ContextList contextList;
/**
* Link to the "real" MappedHost, shared by all aliases.
* 因为一个Host,可能是真正的一个Host,也可能只是起、一个别名,而在Mapper中,两者都是MapperHost,
* 只是真正的MapperHost的realHost是自身,而alias的MapperHost的realHost是其对应的真实的MapperHost
*/
private final MappedHost realHost;
/**
* 所有注册的别名的MapperHost
* 对于alias MapperHost,这个值为null。当一个 real MapperHost 没有alias的时候,这个值也为空
*/
private final List<MappedHost> aliases;
}
ContextList 定义
protected static final class ContextList {
// 如在MappedHost定义中提到的,这个里面存储的是一系列的MapperContext
public final MappedContext[] contexts;
// 这个值主要是为了 url 到wrapper的映射时更快的定位目标,在看代码时可以忽略
public final int nesting;
}
MappedContext定义
protected static final class MappedContext extends MapElement<Void> {
// 一个MappedContext 中又有多个ContextVersion,表示多个版本的context
public volatile ContextVersion[] versions;
}
ContextVersion的定义
protected static final class ContextVersion extends MapElement<Context> {
// context 的匹配路径
public final String path;
// 没有直接的用处,主要目的是为了计算Context中的nesting
public final int slashCount;
public final WebResourceRoot resources;
//context 的欢迎页面
public String[] welcomeResources;
// 默认的wrapper
public MappedWrapper defaultWrapper = null;
// 精确匹配路径的 wrapper
public MappedWrapper[] exactWrappers = new MappedWrapper[0];
// 通配符结束的wrapper
public MappedWrapper[] wildcardWrappers = new MappedWrapper[0];
// 扩展名匹配的wrapper
public MappedWrapper[] extensionWrappers = new MappedWrapper[0];
public int nesting = 0;
// 这个context是否还可用,是否被暂停
private volatile boolean paused;
}
MappedWrapper 定义
仅有两个属性
protected static class MappedWrapper extends MapElement<Wrapper> {
// true if the wrapper corresponds to the JspServlet and the mapping path contains a wildcard; false otherwise
public final boolean jspWildCard;
//true if this wrapper always expects a physical resource to be present (such as a JSP)
public final boolean resourceOnly;
}
小结:
从定义可以看出,Mapper中包含了这个Engine中所有的映射关系,首先包含了多个MapperHost,MapperHost分两类,一个是real MapperHost,另一类是 alias的MapperHost。每一个MapperHost都包含有多个MappedContext,代表不同的Context,每一个MappedContext又有多个ContextVersion,表示每一个Context的不同的版本号,这样在请求参数