第一节 参考
一.参考资料
<深入分析Tomcat>
如果碰到check-sum校验不过的,直接xxx.checksum.enabled=false.
apache.org的地址改为http
https://blog.csdn.net/google2606/article/details/52529829
二.看源码前疑问
1.静态资源是在servlet容器中处理,还是具体的servlet框架中处理?
2.从tomcat接受socket请求,到servlet框架中,比如springmvc,struts,webx等的核心处理类中,
具体经过了哪些流程链处理?怎么区分不同的网站,url,servlet?
3.tomcat的常见报错,比如broken pipeline经历了什么样的错误或者异常处理过程?
4.不同的网站怎么做类加载,区分包路径相同得类?
第二节 架构
一.概述
tomcat核心由两大部分组成,connector和container.connector处理与客户端的socket通信,主要是多线程监听,建立,接受,处理socket连接。支持http1.1(bio和nio),apr,memory,ajp协议.container解析处理具体的http请求报文,由外到内主要包括Server服务器,Service,Engine引擎(Connector和Engine并列统计别,不同的协议对应不同的Connector),Host主机,Context上下文,Wrapper四部分.Host主要用来区分同一个server上的多个虚拟主机。Context用来区分同一主机上的多个网站。wrapper用来区分同一网站的不同的Servlet请求.这几个域模型在tomcat源码中都有对应的interface,和StandardXXX的默认实现.域模型的生命周期通过集成LifecycleBase,或者实现Lefecycle接口实现.
第三节 源码细节
一.tomcat启动
入口地址Bootstrap.main().
(一).load()加载
1.调用init().先调用Bootstrap.initClassLoaders()加载classLoader.classLoader加载三种,commonLoader,catalinaLoader,sharedloader.三种classLoader加载的类路径在static代码块内,通过CatalinaProperties.loadProperties(),读取catalina.properties文件得到.common loader主要读取catalina home下lib文件夹的jar包.server和shared默认为空.设置当前主线程的cloass loader为catalinaLoader.加载床架org.apache.catalina.startup.Catalina类的对象,调用它的setParentClassLoader()方法设置Catalina的cloassLoader为java.lang.ClassLoader.
2.解析命令行命令,如果start,调用load().进入org.apache.catalina.startup.Catalina.load(),在这个方法里面完成tomcat初始化.代码如下:
public void load() {
if (loaded) {
return;
}
loaded = true;
long t1 = System.nanoTime();
//初始化java的tmp目录
initDirs();
// Before digester - it may be needed
initNaming();
// Set configuration source
//读取conf/server.xml文件路径到ConfigFileLoader中.
ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
//读取conf/server.xml到file中
File file = configFile();
// Create and execute our Digester
//创建Digester,用来解析xml文件的接口,用来解析server.xml文件.
//这里面定义了解析server.xml时,碰到server就创建
Digester digester = createStartDigester();
//通过Digester解析server.xml文件.解析完后所有的对象实例挂到StandardServer的对象中引用
try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
InputStream inputStream = resource.getInputStream();
InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
} catch (Exception e) {
log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
if (file.exists() && !file.canRead()) {
log.warn(sm.getString("catalina.incorrectPermissions"));
}
return;
}
//设置Server的Catalina属性,即tomcat目录
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// Stream
//重定向System.out到log文件
initStreams();
// Start the new server
try {
//生命周期的模板模式,进入StandardServer.initInternal()
getServer().init();
}
...
}
3.解析server.xml创建对象,类似spring的ioc,根据xml配置创建对象.
protected Digester createStartDigester() {
...
// Configure the actions we will be using
//碰到server标签时,创建StandardServer类的对象
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
//碰到server节点的子节点时,调用server的setter方法设置进去
digester.addSetProperties("Server");
//当再次碰到server节点时,调用父节点的setServer方法.
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
//创建NamingResourcesImpl子节点对象
digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
//创建Listener对象
digester.addRule("Server/Listener",
new ListenerCreateRule(null, "className"));
digester.addSetProperties("Server/Listener");
digester.addSetNext("Server/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
//对Server/Service节点创建StandardService对象
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
//再次碰到Service时,调用Server.addService方法
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");
digester.addObjectCreate("Server/Service/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Listener");
digester.addSetNext("Server/Service/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
...
// Add RuleSets for nested elements
digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
//对Service下的Engine子节点调用EngineRuleSet.addRuleInstances()方法处理.
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
// When the 'engine' is found, set the parentClassLoader.
digester.addRule("Server/Service/Engine",
new SetParentClassLoaderRule(parentClassLoader));
addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");
...
return digester;
}
初始化java的tmp目录,创建Digester,加载解析tomcat的核心配置文件conf/server.xml.比如解析到一个Server标签,进入org.apache.catalina.core.StandardServer.addService(Service)方法添加Server.tomcat启动时,每一级模型会调用下一级别的addxxx方法实现,比如Server.addService(Service)方法.但是Service.addConnector(Connector)特别,因为所有的Connector共享线程连接池,不同的协议对应不同的Connector,比如http和https.
4.在Catalina.load()方法中继续进入StandardServer.initInternal()方法注册MBeanFactory工厂。依次加载tomcat的lib目录下的lib文件.
在StandardServer.initInternal()方法的最后,遍历所有的Service子节点,进入StandardService.initInternal()方法.这个方法代码如下:
protected void initInternal() throws LifecycleException {
super.initInternal();
//对service下的engine子节点调用初始化方法.这里可以看出只能有一个engine子节点.
if (engine != null) {
engine.init();
}
// Initialize any Executors
//对service节点下的Executor线程池子节点调用初始化
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize mapper listener
//mapper存储了url和处理host的映射关系
mapperListener.init();
// Initialize our defined Connectors
//调用所有的connector子节点初始化,比如http,ajp两种协议的Connector
//不同的connector共享前面的线程池
synchronized (connectorsLock) {
for (Connector connector : connectors) {
connector.init();
}
}
}
进入StandardEngine.initInternal()方法.在StandardService.initInternal()方法中继续调用不同协议的Connector.init()方法.比如调用Http11NioProtocol.init()处理http协议.所有协议都继承自AbstractProtocol,实现ProtocolHandler接口.协议的源和目标地址继承自AbstractJsseEndpoint,通过该方法的bind()方法实现地址绑定。不同的IO模型,都实现bind()方法,比如NIO模型的org.apache.tomcat.util.net.NioEndpoint.bind()方法,实现NIO通信.在NioEndpoint.bind()方法中,会创建服务端的ServerSocketChannel套接字,绑定服务端口号.在StandService.initInternal()中,还会初始化AJP/1.3-8009连接器.
protected void initInternal() throws LifecycleException {
super.initInternal();
//connector对应不同的http,ajp协议.
if (protocolHandler == null) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));
}
// Initialize adapter
//CoyoteAdapter实现协议到容器Container之间的桥接,创建request,response请求
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
if (service != null) {
protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
}
// Make sure parseBodyMethodsSet has a default
//默认post方法解析body
if (null == parseBodyMethodsSet) {
setParseBodyMethods(getParseBodyMethods());
}
...
try {
//调用协议的初始化方法.进入AbstractProtocol.init()
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
}
AbstractProtocol.init()调用AbstractEndpoint#.init()->NioEndpoint.initServerSocket()调用bind()方法绑定默认的8080端口.默认用NIO非阻塞NioEndPoint.如下图:
5.在bind()方法里面调用NioSelectorPool.open().在这里面通过BlockPoller类启动poller机制.
(二).start()启动
1.进入Bootstrap.start()方法.进入StandardServer.startInternal().按照和init()类似的机制,进入子节点,所有service的start()方法.依次进入StandardService.startInternal()->StandardEngine.startInternal()->ContainerBase.startInternal()方法.
2.在ContainerBase.startInternal中,StandardHost也是继承自ContainerBase类.每个Container封装到一个StartChild,它继承Callable,实现start方法,通过新线程调用内部Container的start方法.新线程进入ContainerBase.StartChild.call().进入StandardHost.startInternal().在这个方法里面遍历Host标签内部所有的Valve阀门标签,使用分散式职责链模式,每个Valve保存下一个Valve的引用.Valves都存储在Pipeline管道内部。目前tomcat内部的valve有AccessLogValve,ErrorReportValve,JDBCAccessLogValve,RequestFilterValve,SSLValve等等.
3.在StandardHost.startInternal中继续调用StandardPipeline.start,进入StandardPipeline.startInternal().在这个方法内部,会依次遍历所有的Valve,调用Valve的start方法.依次调用了AccessLogValve,ErrorReportValve,StandardHostValve。先调用了AccessLogValve.startInternal()初始化日志格式.StandardHostValve继承自ValveBase.
(三).HostConfig监听start事件
HostConfig继承自LifecycleListener,在处理start事件时,部署各种app网站.代码如下:
protected void deployApps() {
//指向webapps目录
File appBase = host.getAppBaseFile();
//指向conf/Catalina/localhost目录
File configBase = host.getConfigBaseFile();
//指向webapps下的所有子目录
String[] filteredAppPaths = filterAppPaths(appBase.list());
// Deploy XML descriptors from
//部署xml文件描述的网站
deployDescriptors(configBase, configBase.list());
// Deploy WARs
//部署war包网站
deployWARs(appBase, filteredAppPaths);
// Deploy expanded folders
//部署webapps下的所有目录,对每个目录创建线程调用HostConfig.deployDirectory
deployDirectories(appBase, filteredAppPaths);
}
1.线程进入HostConfig.deployDirectory()中先查找,使用digester库解析网站下的context.xml文件.解析到StandardContext对象里面.一个Context对象代表一个网站.继续调用StandardHost.addChild(),把网站加入到StandardHost对象中,形成级联关系. 在addChildInternal()中.进入StandardContext.start().开始对每个网站的start启动过程.
2.StandardContext.startInternal()方法逻辑比较多,完成网站的启动操作.代码如下:
protected synchronized void startInternal() throws LifecycleException {
...
setConfigured(false);
boolean ok = true;
// Currently this is effectively a NO-OP but needs to be called to
// ensure the NamingResources follows the correct lifecycle
if (namingResources != null) {
namingResources.start();
}
// Post work directory
//设置catalinaHome,ServletContext等属性.
postWorkDirectory();
// Add missing components as necessary
//解析resources目录到StandardRoot对象里面.
if (getResources() == null) { // (1) Required by Loader
if (log.isDebugEnabled())
log.debug("Configuring default Resources");
try {
setResources(new StandardRoot(this));
} catch (IllegalArgumentException e) {
log.error(sm.getString("standardContext.resourcesInit"), e);
ok = false;
}
}
if (ok) {
//调用资源文件StandardRoot的init,start启动方法
//遍历/WEB-INF/lib目录,搜索class类文件
resourcesStart();
}
if (getLoader() == null) {
//加载搜索到的class类
WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}
// An explicit cookie processor hasn't been specified; use the default
if (cookieProcessor == null) {
cookieProcessor = new Rfc6265CookieProcessor();
}
// Initialize character set mapper
getCharsetMapper();
...
// Binding thread
ClassLoader oldCCL = bindThread();
try {
if (ok) {
// Start our subordinate components, if any
Loader loader = getLoader();
if (loader instanceof Lifecycle) {
//这里进入WebappLoader.startInternal()->WebappClassLoaderBase.start()
//类加载器加载/WEB-INF/classes目录,/WEB-INF/lib
((Lifecycle) loader).start();
}
...
//这里触发Context网站的配置,进入ContextConfig.configureContext()
//读取web.xml,创建网站的Servlet
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
// Start our child containers, if not already started
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
//进入StandardWrapper#startInternal,调用各种Servlet的start方法.
child.start();
}
}
// Start the Valves in our pipeline (including the basic),
// if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
...
(Lifecycle) manager).start();
}
...
//启动所有的servlet
if (ok) {
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
...
}
3.网站配置初始化ContextConfig.configureContext():
private void configureContext(WebXml webxml) {
...
for (Entry<String, String> entry :
webxml.getMimeMappings().entrySet()) {
//添加excel,xml等Mime类型的处理mapping
context.addMimeMapping(entry.getKey(), entry.getValue());
}
...
//添加web.xml中配置的各种servlet.比如StandardWrapper,JspServlet,
for (ServletDef servlet : webxml.getServlets().values()) {
Wrapper wrapper = context.createWrapper();
// Description is ignored
// Display name is ignored
// Icons are ignored
// jsp-file gets passed to the JSP Servlet as an init-param
//设置servlet的启动级别
if (servlet.getLoadOnStartup() != null) {
wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
}
if (servlet.getEnabled() != null) {
wrapper.setEnabled(servlet.getEnabled().booleanValue());
}
//设置servlet的name
wrapper.setName(servlet.getServletName());
Map<String,String> params = servlet.getParameterMap();
for (Entry<String, String> entry : params.entrySet()) {
wrapper.addInitParameter(entry.getKey(), entry.getValue());
}
wrapper.setRunAs(servlet.getRunAs());
Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
for (SecurityRoleRef roleRef : roleRefs) {
wrapper.addSecurityReference(
roleRef.getName(), roleRef.getLink());
}
//设置servlet对应的Class对象
wrapper.setServletClass(servlet.getServletClass());
MultipartDef multipartdef = servlet.getMultipartDef();
if (multipartdef != null) {
if (multipartdef.getMaxFileSize() != null &&
multipartdef.getMaxRequestSize()!= null &&
multipartdef.getFileSizeThreshold() != null) {
wrapper.setMultipartConfigElement(new MultipartConfigElement(
multipartdef.getLocation(),
Long.parseLong(multipartdef.getMaxFileSize()),
Long.parseLong(multipartdef.getMaxRequestSize()),
Integer.parseInt(
multipartdef.getFileSizeThreshold())));
} else {
wrapper.setMultipartConfigElement(new MultipartConfigElement(
multipartdef.getLocation()));
}
}
if (servlet.getAsyncSupported() != null) {
wrapper.setAsyncSupported(
servlet.getAsyncSupported().booleanValue());
}
wrapper.setOverridable(servlet.isOverridable());
context.addChild(wrapper);
}
//添加servlet对应的url路径,比如*.jsp
for (Entry<String, String> entry :
webxml.getServletMappings().entrySet()) {
context.addServletMappingDecoded(entry.getKey(), entry.getValue());
}
//设置session,cookie配置
SessionConfig sessionConfig = webxml.getSessionConfig();
if (sessionConfig != null) {
if (sessionConfig.getSessionTimeout() != null) {
context.setSessionTimeout(
sessionConfig.getSessionTimeout().intValue());
}
SessionCookieConfig scc =
context.getServletContext().getSessionCookieConfig();
scc.setName(sessionConfig.getCookieName());
scc.setDomain(sessionConfig.getCookieDomain());
scc.setPath(sessionConfig.getCookiePath());
scc.setComment(sessionConfig.getCookieComment());
if (sessionConfig.getCookieHttpOnly() != null) {
scc.setHttpOnly(sessionConfig.getCookieHttpOnly().booleanValue());
}
if (sessionConfig.getCookieSecure() != null) {
scc.setSecure(sessionConfig.getCookieSecure().booleanValue());
}
if (sessionConfig.getCookieMaxAge() != null) {
scc.setMaxAge(sessionConfig.getCookieMaxAge().intValue());
}
if (sessionConfig.getSessionTrackingModes().size() > 0) {
context.getServletContext().setSessionTrackingModes(
sessionConfig.getSessionTrackingModes());
}
}
// Context doesn't use version directly
//添加欢迎页.
for (String welcomeFile : webxml.getWelcomeFiles()) {
/*
* The following will result in a welcome file of "" so don't add
* that to the context
* <welcome-file-list>
* <welcome-file/>
* </welcome-file-list>
*/
if (welcomeFile != null && welcomeFile.length() > 0) {
context.addWelcomeFile(welcomeFile);
}
}
// Do this last as it depends on servlets
for (JspPropertyGroup jspPropertyGroup :
webxml.getJspPropertyGroups()) {
String jspServletName = context.findServletMapping("*.jsp");
if (jspServletName == null) {
jspServletName = "jsp";
}
if (context.findChild(jspServletName) != null) {
for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
context.addServletMappingDecoded(urlPattern, jspServletName, true);
}
} else {
if(log.isDebugEnabled()) {
for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
log.debug("Skipping " + urlPattern + " , no servlet " +
jspServletName);
}
}
}
}
...
}
先初始化网站路径,然后创建ApplicationContext,调用webresources.StandardRoot.initInternal()进行资源文件的初始化,比如通过DirResourceSet处理目录.在startInternal中继续创建Rfc6265CookieProcessor处理Cookie.调用WebappLoader.startInternal().进入WebappClassLoaderBase.start().进入ContextConfig.configureStart()扫描web.xml文件加载网站的配置.调用方法StandardContext.createWrapper()创建Warpper,每个Wrapper对应一个Servlet,开始创建的是DefaultServlet.DefaultServlet继承自HttpServlet,GenericServlet,实现了Servlet接口.当调用StandardContext.addChild(Container)时,进入StandardWrapper.startInternal().调用StandardWrapperValve.startInternal().在ContextConfig.configureStart中继续加载StandardWrapper对jsp的解析对象,调用StandardWrapperValve对jsp的配置.添加完Servlet后调用StandardContext.addServletMappingDecoded()添加Servlet的url的mapping.在StandardContxt中有个servletMappings属性保存了url和servlet之间的映射关系.如果是/,进入名字叫default的servlet.在在ContextConfig.configureStart中继续中继续配置SessionConfig,SessionCookieConfig.welcomeFile欢迎页面(欢迎页默认是index.html, index.htm, index.jsp).最后初始化jsp引擎JasperInitializer.
在StandardHost.addChild中继续调用JasperInitializer.onStartup().调用TldScanner.scan()扫描jsp标签.StandardHost.addChild中继续调用StandardManager.startInternal().进入SessionIdGeneratorBase.startInternal().调用StandardManager.doLoad().在StandardContext.startInternal中,调用tandardContext.loadOnStartup(Container[]).装载servlet.比如.DefaultServlet.进入DefaultServlet.init().
4.在StandardService.startInternal()中调用Connector.startInternal(),进入不同协议Connector的start方法,比如AjpNioProtocol.start(),Http11NioProtocol.start.
二 http请求处理流程
如下图:
1.过程为Socket处理线程进入SocketProcessorBase.run()->NioEndpoint.SocketProcessor.doRun().NioEndpoint有个Handler属性->ConnectionHandler,进入AbstractProtocol.ConnectionHandler#process().协议里面有Processor属性->AbstractProcessorLight#process()->进入Http协议的Processor,Http11Processor#service().这个http协议Processor里面,有request,response,Adapter属性,在这个方法里面初始化Request,Response对象.完成Socket到Request,Response的转换.->CoyoteAdapter#service().在service()方法里先调用CoyoteAdapter#postParseRequest()匹配Context网站,servlet容器.再调用connector.getService().getContainer().getPipeline().getFirst().invoke()容器的管道进入网站处理.Adapter指向CoyoteAdapter,Adapter是Socket和容器之间的桥接.里面有server.xml配置的connector字段.在CoyoteAdapter#postParseRequest()里面获取请求的url,判断是否是http支持的get,head,post,put等请求.然后依次调用->在mapper.Mapper#internalMap()中根据MappingData中存储的url路径中的的host(比如localhost),取出所有的context网站.然后根据url路径匹配对应的Context网站->匹配到网站后,进入Mapper.internalMapWrapper方法中匹配用来处理请求的Wrapper,即Servlet.代码如下:
2.匹配url的网站mapper.Mapper#internalMap()
private final void internalMap(CharChunk host, CharChunk uri,
String version, MappingData mappingData) throws IOException {
...
// Virtual host mapping
//取出所有的host主机名
MappedHost[] hosts = this.hosts;
//匹配主机名
MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
//如果url路径中的主机名为空,重新从url中获取.
if (mappedHost == null) {
...
}
mappingData.host = mappedHost.object;
if (uri.isNull()) {
// Can't map context or wrapper without a uri
return;
}
uri.setLimit(-1);
// Context mapping
//从匹配出的主机中,取出主机支持的contexts网站列表.
ContextList contextList = mappedHost.contextList;
MappedContext[] contexts = contextList.contexts;
//从url路径中匹配网站名.
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);
if (!found) {
if (contexts[0].name.equals("")) {
context = contexts[0];
} else {
context = null;
}
}
//如果没有匹配的网站,直接返回
if (context == null) {
return;
}
mappingData.contextPath.setString(context.name);
...
// Wrapper mapping
//匹配网站Context下的处理的Wrapper,即Servelet
if (!contextVersion.isPaused()) {
internalMapWrapper(contextVersion, uri, mappingData);
}
3.匹配Servlet
private final void internalMapWrapper(ContextVersion contextVersion,
CharChunk path,
MappingData mappingData) throws IOException {
...
//精确匹配
// Rule 1 -- Exact Match
MappedWrapper[] exactWrappers = contextVersion.exactWrappers;
internalMapExactWrapper(exactWrappers, path, mappingData);
//匹配前缀通配符
// Rule 2 -- Prefix Match
boolean checkJspWelcomeFiles = false;
MappedWrapper[] wildcardWrappers = contextVersion.wildcardWrappers;
if (mappingData.wrapper == null) {
internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,
path, mappingData);
if (mappingData.wrapper != null && mappingData.jspWildCard) {
char[] buf = path.getBuffer();
if (buf[pathEnd - 1] == '/') {
mappingData.wrapper = null;
checkJspWelcomeFiles = true;
} else {
// See Bugzilla 27704
mappingData.wrapperPath.setChars(buf, path.getStart(),
path.getLength());
mappingData.pathInfo.recycle();
}
}
}
if(mappingData.wrapper == null && noServletPath &&
...
}
//扩展匹配
// Rule 3 -- Extension Match
MappedWrapper[] extensionWrappers = contextVersion.extensionWrappers;
if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
internalMapExtensionWrapper(extensionWrappers, path, mappingData,
true);
}
//欢迎页匹配
// Rule 4 -- Welcome resources processing for servlets
if (mappingData.wrapper == null) {
boolean checkWelcomeFiles = checkJspWelcomeFiles;
if (!checkWelcomeFiles) {
char[] buf = path.getBuffer();
checkWelcomeFiles = (buf[pathEnd - 1] == '/');
}
if (checkWelcomeFiles) {
for (int i = 0; (i < contextVersion.welcomeResources.length)
&& (mappingData.wrapper == null); i++) {
path.setOffset(pathOffset);
path.setEnd(pathEnd);
path.append(contextVersion.welcomeResources[i], 0,
contextVersion.welcomeResources[i].length());
path.setOffset(servletPath);
// Rule 4a -- Welcome resources processing for exact macth
internalMapExactWrapper(exactWrappers, path, mappingData);
// Rule 4b -- Welcome resources processing for prefix match
if (mappingData.wrapper == null) {
internalMapWildcardWrapper
(wildcardWrappers, contextVersion.nesting,
path, mappingData);
}
// Rule 4c -- Welcome resources processing
// for physical folder
if (mappingData.wrapper == null
&& contextVersion.resources != null) {
String pathStr = path.toString();
WebResource file =
contextVersion.resources.getResource(pathStr);
if (file != null && file.isFile()) {
internalMapExtensionWrapper(extensionWrappers, path,
mappingData, true);
if (mappingData.wrapper == null
&& contextVersion.defaultWrapper != null) {
...
}
}
}
}
...
}
}
//默认的DefaultServlet来处理
// Rule 7 -- Default servlet
if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
if (contextVersion.defaultWrapper != null) {
mappingData.wrapper = contextVersion.defaultWrapper.object;
...
mappingData.matchType = MappingMatch.DEFAULT;
}
// Redirection to a folder
char[] buf = path.getBuffer();
if (contextVersion.resources != null && buf[pathEnd -1 ] != '/') {
...
}
}
}
4.在CoyoteAdapter#service()中通过
connector.getService().getContainer().getPipeline().getFirst().invoke();一行进入Service容器,进入server.xml中配置的Service下一级Engine处理.这里,开始Engine->Host->Context->Wrapper的各级管道.进入StandardEngineValve#invoke().
三.connector
poller机制
四.container和Service
五.host
六.context
七.wrapper
八.生命周期
九.类加载器
十.session管理器
十一.网络IO模型