Mapper对于Tomcat或者jetty这种应用服务器来说应该算是非常重要的一个东西了。。
首先来说它是干嘛用的,听名字就基本上能猜出来,对于请求,对这个请求进行路由,交给应该负责处理这个请求的最终代码就是Mapp而应该干的或。。
对于servlet来说,对应的就是一次http请求该交给哪一个servlet来处理。
其实以前在看jetty的代码的时候就相当于看过了一种实现的方式,jetty采用的是一种类似于tries(字典树)的查询来进行请求的路由。。。感觉还算是蛮不错的吧。。毕竟字典树在做基于字符串的查询效率还是很高的。。
那么接下来来大体的来说一下tomcat是怎么实现的吧。。。嗯,最关键的就是:二分搜索,字符串也是可以排序的嘛,也就是字典序,那么也就可以做二分搜索咯。。。
在开始具体的代码之前,先来看一个简略的结构图吧:
这个是整个Mapper的比较简单的总体的结构层次图吧,通过以前的分析我们知道,在tomcat服务器中,可以有多个service,每个service只能有一个engine,而一个engine可以部署多个host,一个host又可以部署多个context,而mapper对象是属于service所拥有的。。。也符合上面的层次图。。。
先来看看上面提到一些对象的定义吧,他们都是定义在Mapper里面的嵌套类:
protected abstract static class MapElement<T> {
public String name = null; //名字
public T object = null; //对应的对象,例如是host对象,context对象或者wrapper对象啥的
}
// ------------------------------------------------------- Host Inner Class
protected static final class MappedHost //对host的map信息
extends MapElement<Host> {
public ContextList contextList = null; //有一个contextlist
}
// ------------------------------------------------ ContextList Inner Class
protected static final class ContextList { //在mappedhost里面将会用其来存拥有的context的信息
public MappedContext[] contexts = new MappedContext[0]; //mappedcontext对象的数组
public int nesting = 0; //所有的context的path中,最多的斜线数目
}
// ---------------------------------------------------- Context Inner Class
protected static final class MappedContext extends MapElement<Context> { //对context的map的信息
public ContextVersion[] versions = new ContextVersion[0]; //版本的数组
}
protected static final class ContextVersion extends MapElement<Context> { //某个context的某个版本的具体信息
public String path = null; //path
public String[] welcomeResources = new String[0]; //welcome的数据
public WebResourceRoot resources = null; //操作当前web应用程序的资源
public MappedWrapper defaultWrapper = null; //默认的wrapper
public MappedWrapper[] exactWrappers = new MappedWrapper[0]; //对wrapper的精确的map
public MappedWrapper[] wildcardWrappers = new MappedWrapper[0]; //基于通配符的map
public MappedWrapper[] extensionWrappers = new MappedWrapper[0]; //基于扩展名的map
public int nesting = 0; // 属于这个context的所有servlet的path里面最大斜线数目
}
// ---------------------------------------------------- Wrapper Inner Class
protected static class MappedWrapper //对wrapper对象的map信息
extends MapElement<Wrapper> {
public boolean jspWildCard = false;
public boolean resourceOnly = false;
}
这里最基本的类型是MapElement,它是一个泛型吧,属性首先是名字,对于host来说,那么就是host的名字了,对于context来说那就是context的path了。。。。以此类推。。
然后是MappedHost的定义,这里其实也就稍微扩展了一下MapElement类型吧,加入了一个ContextList,看名字就知道它用于保存当前host所拥有的所有的host对象。。。ContextList的定义我们可以看到其实也是用数组来保存MappedContext对象的。。。
然后是MappedContext的定义,这里扩展了 一个ContextVersion的数组,因对于context来说,可能在时间段上同一个context可能会重新部署啥的,就涉及到不同的版本了。。当然一般情况下这个数组的长度都是为1的,也就是只有一个版本。。。
接下来就是ContextVersion定义了,可以将它理解为具体的一个context的map的信息,有path,defaultWrapper,以及wrapper的匹配什么的。。分为精确匹配,通配符匹配,和扩展名匹配。。
最后就是MappedWrapper了,扩展的东西不多吧。。主要是一些标记。。。
好啦,接下来来具体的看看Mapper的定义,先来看看它的重要的属性吧:
protected MappedHost[] hosts = new MappedHost[0]; // 对host的map信息
protected String defaultHostName = null; // engine使用的默认的host名字
protected Map<Context, ContextVersion> contextObjectToContextVersionMap = //对context的map信息,key是context对象,value是contextVersion对象
new ConcurrentHashMap<>();
hosts数组,用于保存所有的host的map信息,然后又一个defaultHostName,在engine对象的定义中有一个defaultHostName属性,就是对应的这里。。。
然后就是一个map,用于将context对象与具体的contextVersion匹配起来。。。
好啦,接下来来看看如何在mapper中添加对一个host。。
//添加host的map信息,第一个参数是hsot的名字,第二个是别名,第三个是host对象
public synchronized void addHost(String name, String[] aliases,
Host host) {
MappedHost[] newHosts = new MappedHost[hosts.length + 1]; //创建MappedHost对象的数组,这里长度需要加1
MappedHost newHost = new MappedHost(); //创建一个MappedHost对象,用于保存host的map信息
ContextList contextList = new ContextList(); //ContextList对象,用于保存context
newHost.name = name; //设置名字
newHost.contextList = contextList; //设置
newHost.object = host; //设置对应的host对象
if (insertMap(hosts, newHosts, newHost)) { //这里需要复制以前的host的map信息,并维护好排序
hosts = newHosts; //指向新新的数组
}
for (int i = 0; i < aliases.length; i++) { //遍历这个host的所有的别名,为每一个别名都创建一次map信息,感觉这里做了挺多重复的事情
newHosts = new MappedHost[hosts.length + 1];
newHost = new MappedHost();
newHost.name = aliases[i];
newHost.conte