根据上一章所知,tomcat解析server.xml的规则是在org.apache.catalina.startup.Catalina类的createStartDigester方法里面,部分代码如下:
protected Digester createStartDigester() {
......
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/"));
......
}
看到HostRuleSet类的addRuleInstances方法,部分代码如下:
@Override
public void addRuleInstances(Digester digester) {
digester.addObjectCreate(prefix + "Host",
"org.apache.catalina.core.StandardHost",
"className");
digester.addSetProperties(prefix + "Host");
......
digester.addRule(prefix + "Host",
new LifecycleListenerRule
("org.apache.catalina.startup.HostConfig",
"hostConfigClass"));
......
}
从中可以知道这个的host标签创建的是org.apache.catalina.core.StandardHost类的实例,并且由LifecycleListenerRule类可以知道,org.apache.catalina.startup.HostConfig作为StandardHost的一个监听类.
LifecycleListenerRule类的begin方法部分代码如下:
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
Container c = (Container) digester.peek(); // 这个就是StandardHost的实例
......
Class<?> clazz = Class.forName(className);
LifecycleListener listener =
(LifecycleListener) clazz.newInstance(); // 实例化HostConfig类
c.addLifecycleListener(listener); // 添加到standardHost里面去
}
从server.xml文件可以知道host的父容器是engin,而从org.apache.catalina.startup.Catalina类的createStartDigester可以知道server.xml的engin标签创建的是org.apache.catalina.core.StandardEngine类的实例.从上一章分析可以知道StandardEngine的启动是在StandardService类的startInternal方法里面启动的,部分代码如下:
@Override
protected void startInternal() throws LifecycleException {
......
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
......
mapperListener.start();
......
}
这时候看到StandardEngine的start方法,而start方法又调用了startInternal方法,部分代码如下:
@Override
protected synchronized void startInternal() throws LifecycleException {
......
super.startInternal();
}
这时候调用的是父类ContainerBase的startInternal方法,部分代码如下:
@Override
protected synchronized void startInternal() throws LifecycleException {
......
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
......
threadStart();
}
看到代码Container children[] = findChildren();因为之前StandardHost实例化的时候作为StandardEngine的子容器添加到StandardEngine里面的,因此findChildren就可以获取到StandardHost的实例.从StartChild类知道调用了StandardHost的start方法,而StandardHost的start的方法有调用了ContainerBase的startInternal方法,找到该方法里面的threadStart方法,部分代码如下:
protected void threadStart() {
......
thread = new Thread(new ContainerBackgroundProcessor(), threadName);
thread.setDaemon(true);
thread.start();
......
}
该方法新启动了线程执行的是ContainerBackgroundProcessor里面的run方法,部分代码如下:
@Override
public void run() {
......
processChildren(ContainerBase.this);
......
}
看到processChildren方法,部分代码如下:
protected void processChildren(Container container) {
......
container.backgroundProcess();
......
}
因为这个时候Container 是StandardHost,而该类没有覆盖此方法因此调用的还是ContainerBase的,该方法的部分代码如下:
@Override
public void backgroundProcess() {
......
fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
......
}
在这里看到fireLifecycleEvent,在分析LifecycleListenerRule类的begin方法的方法知道HostConfig监听StandardHost的事件,因此这时候看到HostConfig类的
@Override
public void lifecycleEvent(LifecycleEvent event) {
......
// Process the event that has occurred
if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
check();
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
beforeStart();
} else if (event.getType().equals(Lifecycle.START_EVENT)) {
start();
} else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
stop();
}
}
backgroundProcess方法里面出发的事件类型为Lifecycle.PERIODIC_EVENT,因此看到该类的check方法,部分代码如下:
protected void check() {
if (host.getAutoDeploy()) {
......
deployApps(); // 检测
}
}
再看到deployApps方法,代码如下:
protected void deployApps() {
File appBase = host.getAppBaseFile();
File configBase = host.getConfigBaseFile();
String[] filteredAppPaths = filterAppPaths(appBase.list());
// Deploy XML descriptors from configBase
deployDescriptors(configBase, configBase.list()); //
// Deploy WARs
deployWARs(appBase, filteredAppPaths);
// Deploy expanded folders
deployDirectories(appBase, filteredAppPaths);
}
看到该方法是不是有点熟悉呢,平时我们发布web的时候可以直接拷贝web的目录或者war压缩包到webapps目录下的.因此这里都一一对应了,deployDirectories该方法对应的就是web目录的发布,deployWARs对应的就是war包的发布方式.deployDescriptors的发布方式没有去细看.而在这里我们主要看deployDirectories就好,部分代码如下:
protected void deployDirectory(ContextName cn, File dir) {
......
try {
if (deployXML && xml.exists()) {
synchronized (digesterLock) {
......
context = (Context) digester.parse(xml);// 创建StandardContext对象
......
}
} else if (!deployXML && xml.exists()) {
......
} else {
context = (Context) Class.forName(contextClass).newInstance(); // 创建StandardContext对象
}
Class<?> clazz = Class.forName(host.getConfigClass()); // 创建ContextConfig对象
LifecycleListener listener =
(LifecycleListener) clazz.newInstance();
context.addLifecycleListener(listener);
context.setName(cn.getName());
context.setPath(cn.getPath());
context.setWebappVersion(cn.getVersion());
context.setDocBase(cn.getBaseName());
host.addChild(context);
......
deployed.put(cn.getName(), deployedApp);
}
看到digester的创建代码:
protected Digester digester = createDigester(contextClass);
而contextClass的值为:
protected String contextClass = "org.apache.catalina.core.StandardContext";
createDigester方法的代码如下:
protected static Digester createDigester(String contextClassName) {
Digester digester = new Digester();
digester.setValidating(false);
// Add object creation rule
digester.addObjectCreate("Context", contextClassName, "className");
// Set the properties on that object (it doesn't matter if extra
// properties are set)
digester.addSetProperties("Context");
return (digester);
}
由此可知,deployDirectory方法主要是创建StandardContext的实例,并且创建org.apache.catalina.startup.ContextConfig监听StandardContext的事件.又因为StandardContext继承了ContainerBase类,由此可知,肯定会的调用StandardContext的startInternal方法,部分代码如下:
@Override
protected synchronized void startInternal() throws LifecycleException { //----在这里启动了
......
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null); // 出发解析web.xml的
......
if (ok) {
if (!loadOnStartup(findChildren())){ // 启动servlet----------启动那些有启动要求的
......
}
}
......
}
fireLifecycleEvent在这里知道这里会触发ContextConfig的lifecycleEvent方法,部分代码如下:
@Override
public void lifecycleEvent(LifecycleEvent event) {
......
context = (Context) event.getLifecycle();
......
// Process the event that has occurred
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
configureStart(); // 开始解析配置了
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
beforeStart(); // 启动之前
} else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
// Restore docBase for management tools
if (originalDocBase != null) {
context.setDocBase(originalDocBase);
}
} else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
configureStop();// 配置结束开始添加规则
} else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
init();
} else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
destroy();
}
}
在这里知道event.getType()为Lifecycle.CONFIGURE_START_EVENT,configureStart的部分代码如下:
protected synchronized void configureStart() {
......
webConfig(); // 解析所有的配置
......
}
webConfig方法的部分代码如下:
protected void webConfig() {
......
WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
context.getXmlValidation(), context.getXmlBlockExternal());
Set<WebXml> defaults = new HashSet<>();
defaults.add(getDefaultWebXmlFragment(webXmlParser));
WebXml webXml = createWebXml();
// Parse context level web.xml
InputSource contextWebXml = getContextWebXmlSource(); // 读取/WEB-INF/web.xml
if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) { // /WEB-INF/web.xml文件的内容解析到webXml里面
ok = false;
}
ServletContext sContext = context.getServletContext();
Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser); // 解析的是webFragment
......
// Step 3. Look for ServletContainerInitializer implementations
if (ok) {
processServletContainerInitializers(); // 这个是解析继承ServletContainerInitializer类的配置,
}
......
}
从中可以看出:
parseWebXml 是解析web.xml文件的
processJarsForWebFragments 是解析webfragment的
processServletContainerInitializers 是解析继承了ServletContainerInitializer的配置类
下面先分析,parseWebXml方法的部分代码如下:
public boolean parseWebXml(InputSource source, WebXml dest,
boolean fragment) {
......
if (fragment) {
digester = webFragmentDigester;
ruleSet = webFragmentRuleSet;
} else {
digester = webDigester;
ruleSet = webRuleSet;
}
digester.push(dest);
digester.setErrorHandler(handler);
......
digester.parse(source);
......
}
因为传进来的fragment是false,那么digester = webDigester;看一下webDigester的实现
webDigester = DigesterFactory.newDigester(validation,
namespaceAware, webRuleSet, blockExternal);
看到webRuleSet的addRuleInstances的方法,部分代码如下:
@Override
public void addRuleInstances(Digester digester) {
......
digester.addCallMethod(fullPrefix + "/context-param",
"addContextParam", 2); //添加context-param参数两个参数的
digester.addCallParam(fullPrefix + "/context-param/param-name", 0);
digester.addCallParam(fullPrefix + "/context-param/param-value", 1);
digester.addCallMethod(fullPrefix + "/display-name",
"setDisplayName", 0);
digester.addRule(fullPrefix + "/distributable",
new SetDistributableRule());
......
digester.addObjectCreate(fullPrefix + "/error-page",
"org.apache.tomcat.util.descriptor.web.ErrorPage");
digester.addSetNext(fullPrefix + "/error-page", // 添加到父容器
"addErrorPage",
"org.apache.tomcat.util.descriptor.web.ErrorPage");
digester.addCallMethod(fullPrefix + "/error-page/error-code",
"setErrorCode", 0);
digester.addCallMethod(fullPrefix + "/error-page/exception-type",
"setExceptionType", 0);
digester.addCallMethod(fullPrefix + "/error-page/location",
"setLocation", 0);
digester.addObjectCreate(fullPrefix + "/filter",
"org.apache.tomcat.util.descriptor.web.FilterDef");
digester.addSetNext(fullPrefix + "/filter",
"addFilter",
"org.apache.tomcat.util.descriptor.web.FilterDef");
digester.addCallMethod(fullPrefix + "/filter/description",
"setDescription", 0);
digester.addCallMethod(fullPrefix + "/filter/display-name",
"setDisplayName", 0);
digester.addCallMethod(fullPrefix + "/filter/filter-class",
"setFilterClass", 0);
digester.addCallMethod(fullPrefix + "/filter/filter-name",
"setFilterName", 0);
digester.addCallMethod(fullPrefix + "/filter/icon/large-icon",
"setLargeIcon", 0);
digester.addCallMethod(fullPrefix + "/filter/icon/small-icon",
"setSmallIcon", 0);
digester.addCallMethod(fullPrefix + "/filter/async-supported",
"setAsyncSupported", 0);
digester.addCallMethod(fullPrefix + "/filter/init-param",
"addInitParameter", 2);
digester.addCallParam(fullPrefix + "/filter/init-param/param-name",
0);
digester.addCallParam(fullPrefix + "/filter/init-param/param-value",
1);
digester.addObjectCreate(fullPrefix + "/filter-mapping",
"org.apache.tomcat.util.descriptor.web.FilterMap");
digester.addSetNext(fullPrefix + "/filter-mapping",
"addFilterMapping",
"org.apache.tomcat.util.descriptor.web.FilterMap");
digester.addCallMethod(fullPrefix + "/filter-mapping/filter-name",
"setFilterName", 0);
digester.addCallMethod(fullPrefix + "/filter-mapping/servlet-name",
"addServletName", 0);
digester.addCallMethod(fullPrefix + "/filter-mapping/url-pattern",
"addURLPattern", 0);
digester.addCallMethod(fullPrefix + "/filter-mapping/dispatcher",
"setDispatcher", 0);
digester.addCallMethod(fullPrefix + "/listener/listener-class",
"addListener", 0);
digester.addRule(fullPrefix + "/jsp-config",
jspConfig);
digester.addObjectCreate(fullPrefix + "/jsp-config/jsp-property-group",
"org.apache.tomcat.util.descriptor.web.JspPropertyGroup");
digester.addSetNext(fullPrefix + "/jsp-config/jsp-property-group",
"addJspPropertyGroup",
"org.apache.tomcat.util.descriptor.web.JspPropertyGroup");
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/deferred-syntax-allowed-as-literal",
"setDeferredSyntax", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/el-ignored",
"setElIgnored", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/include-coda",
"addIncludeCoda", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/include-prelude",
"addIncludePrelude", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/is-xml",
"setIsXml", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/page-encoding",
"setPageEncoding", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/scripting-invalid",
"setScriptingInvalid", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/trim-directive-whitespaces",
"setTrimWhitespace", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/url-pattern",
"addUrlPattern", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/default-content-type",
"setDefaultContentType", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/buffer",
"setBuffer", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/error-on-undeclared-namespace",
"setErrorOnUndeclaredNamespace", 0);
digester.addRule(fullPrefix + "/login-config",
loginConfig);
......
digester.addRule(fullPrefix + "/servlet",
new ServletDefCreateRule());
digester.addSetNext(fullPrefix + "/servlet",
"addServlet",
"org.apache.tomcat.util.descriptor.web.ServletDef");
digester.addCallMethod(fullPrefix + "/servlet/init-param",
"addInitParameter", 2);
digester.addCallParam(fullPrefix + "/servlet/init-param/param-name",
0);
digester.addCallParam(fullPrefix + "/servlet/init-param/param-value",
1);
digester.addCallMethod(fullPrefix + "/servlet/jsp-file",
"setJspFile", 0);
digester.addCallMethod(fullPrefix + "/servlet/load-on-startup",
"setLoadOnStartup", 0);
digester.addCallMethod(fullPrefix + "/servlet/run-as/role-name",
"setRunAs", 0);
digester.addObjectCreate(fullPrefix + "/servlet/security-role-ref",
"org.apache.tomcat.util.descriptor.web.SecurityRoleRef");
digester.addSetNext(fullPrefix + "/servlet/security-role-ref",
"addSecurityRoleRef",
"org.apache.tomcat.util.descriptor.web.SecurityRoleRef");
digester.addCallMethod(fullPrefix + "/servlet/security-role-ref/role-link",
"setLink", 0);
digester.addCallMethod(fullPrefix + "/servlet/security-role-ref/role-name",
"setName", 0);
digester.addCallMethod(fullPrefix + "/servlet/servlet-class",
"setServletClass", 0);
digester.addCallMethod(fullPrefix + "/servlet/servlet-name",
"setServletName", 0);
digester.addObjectCreate(fullPrefix + "/servlet/multipart-config",
"org.apache.tomcat.util.descriptor.web.MultipartDef");
digester.addSetNext(fullPrefix + "/servlet/multipart-config",
"setMultipartDef",
"org.apache.tomcat.util.descriptor.web.MultipartDef");
digester.addCallMethod(fullPrefix + "/servlet/multipart-config/location",
"setLocation", 0);
digester.addCallMethod(fullPrefix + "/servlet/multipart-config/max-file-size",
"setMaxFileSize", 0);
digester.addCallMethod(fullPrefix + "/servlet/multipart-config/max-request-size",
"setMaxRequestSize", 0);
digester.addCallMethod(fullPrefix + "/servlet/multipart-config/file-size-threshold",
"setFileSizeThreshold", 0);
digester.addCallMethod(fullPrefix + "/servlet/async-supported",
"setAsyncSupported", 0);
digester.addCallMethod(fullPrefix + "/servlet/enabled",
"setEnabled", 0);
digester.addRule(fullPrefix + "/servlet-mapping",
new CallMethodMultiRule("addServletMapping", 2, 0));
digester.addCallParam(fullPrefix + "/servlet-mapping/servlet-name", 1);
digester.addRule(fullPrefix + "/servlet-mapping/url-pattern", new CallParamMultiRule(0));
digester.addRule(fullPrefix + "/session-config", sessionConfig);
digester.addObjectCreate(fullPrefix + "/session-config",
"org.apache.tomcat.util.descriptor.web.SessionConfig");
digester.addSetNext(fullPrefix + "/session-config", "setSessionConfig",
"org.apache.tomcat.util.descriptor.web.SessionConfig");
digester.addCallMethod(fullPrefix + "/session-config/session-timeout",
"setSessionTimeout", 0);
digester.addCallMethod(fullPrefix + "/session-config/cookie-config/name",
"setCookieName", 0);
digester.addCallMethod(fullPrefix + "/session-config/cookie-config/domain",
"setCookieDomain", 0);
digester.addCallMethod(fullPrefix + "/session-config/cookie-config/path",
"setCookiePath", 0);
digester.addCallMethod(fullPrefix + "/session-config/cookie-config/comment",
"setCookieComment", 0);
digester.addCallMethod(fullPrefix + "/session-config/cookie-config/http-only",
"setCookieHttpOnly", 0);
digester.addCallMethod(fullPrefix + "/session-config/cookie-config/secure",
"setCookieSecure", 0);
digester.addCallMethod(fullPrefix + "/session-config/cookie-config/max-age",
"setCookieMaxAge", 0);
digester.addCallMethod(fullPrefix + "/session-config/tracking-mode",
"addSessionTrackingMode", 0);
// Taglibs pre Servlet 2.4
digester.addRule(fullPrefix + "/taglib", new TaglibLocationRule(false));
digester.addCallMethod(fullPrefix + "/taglib",
"addTaglib", 2);
digester.addCallParam(fullPrefix + "/taglib/taglib-location", 1);
digester.addCallParam(fullPrefix + "/taglib/taglib-uri", 0);
// Taglibs Servlet 2.4 onwards
digester.addRule(fullPrefix + "/jsp-config/taglib", new TaglibLocationRule(true));
digester.addCallMethod(fullPrefix + "/jsp-config/taglib",
"addTaglib", 2);
digester.addCallParam(fullPrefix + "/jsp-config/taglib/taglib-location", 1);
digester.addCallParam(fullPrefix + "/jsp-config/taglib/taglib-uri", 0);
digester.addCallMethod(fullPrefix + "/welcome-file-list/welcome-file",
"addWelcomeFile", 0);
digester.addCallMethod(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping",
"addLocaleEncodingMapping", 2);
digester.addCallParam(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping/locale", 0);
digester.addCallParam(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping/encoding", 1);
digester.addRule(fullPrefix + "/post-construct",
new LifecycleCallbackRule("addPostConstructMethods", 2, true));
digester.addCallParam(fullPrefix + "/post-construct/lifecycle-callback-class", 0);
digester.addCallParam(fullPrefix + "/post-construct/lifecycle-callback-method", 1);
digester.addRule(fullPrefix + "/pre-destroy",
new LifecycleCallbackRule("addPreDestroyMethods", 2, false));
digester.addCallParam(fullPrefix + "/pre-destroy/lifecycle-callback-class", 0);
digester.addCallParam(fullPrefix + "/pre-destroy/lifecycle-callback-method", 1);
}
这边有web.xml的解析信息储存在WebXml类中,接着看到processServletContainerInitializers方法,代码如下:
protected void processServletContainerInitializers() {
List<ServletContainerInitializer> detectedScis;
try {
WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context);
detectedScis = loader.load(ServletContainerInitializer.class);
} catch (IOException e) {
log.error(sm.getString(
"contextConfig.servletContainerInitializerFail",
context.getName()),
e);
ok = false;
return;
}
for (ServletContainerInitializer sci : detectedScis) {
initializerClassMap.put(sci, new HashSet<Class<?>>()); // 为每一个类创建一个容器
HandlesTypes ht;
try {
ht = sci.getClass().getAnnotation(HandlesTypes.class); // 判断有没有HandlesTypes注解
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.info(sm.getString("contextConfig.sci.debug",
sci.getClass().getName()),
e);
} else {
log.info(sm.getString("contextConfig.sci.info",
sci.getClass().getName()));
}
continue;
}
if (ht == null) {
continue;
}
Class<?>[] types = ht.value(); // 要启动的类有哪些
if (types == null) {
continue;
}
for (Class<?> type : types) {
if (type.isAnnotation()) { // 判断有没有注解
handlesTypesAnnotations = true;
} else {
handlesTypesNonAnnotations = true;
}
Set<ServletContainerInitializer> scis =
typeInitializerMap.get(type);
if (scis == null) {
scis = new HashSet<>();
typeInitializerMap.put(type, scis);
}
scis.add(sci); // 添加进去并把注解弄好的都KO了
}
}
}
用过spring的0配置的话都知道该类的作用,在这里就先不分析该方法的代码了,等后面讲解到javaEE的配置方式再分析.好了到这里web.xml的配置信息基本全部加载完了,加载完之后怎么跟请求联系在一起呢?
在这里我们再看回到StandardService的startInternal方法里面有这么一句:
mapperListener.start();
在这里我们看一下它做了什么看到MapperListener的startInternal方法,部分代码如下:
@Override
public void startInternal() throws LifecycleException {
......
Engine engine = service.getContainer();
......
Container[] conHosts = engine.findChildren();
for (Container conHost : conHosts) {
Host host = (Host) conHost;
......
registerHost(host);
......
}
}
从下面的分析知道engine的子容器是standardHost,再看到registerHost方法,部分代码如下:
private void registerHost(Host host) {
String[] aliases = host.findAliases();
mapper.addHost(host.getName(), aliases, host); // 把host添加到mapper里面去了
for (Container container : host.findChildren()) {
if (container.getState().isAvailable()) {
registerContext((Context) container);
}
}
......
}
该方法把standardHost添加到mapper里面去了,由上分析可知standardHost的子容器为standardContext.
registerContext方法的部分代码如下:
private void registerContext(Context context) { // 注册webapp进来,standardContext
......
Host host = (Host)context.getParent();
WebResourceRoot resources = context.getResources();
String[] welcomeFiles = context.findWelcomeFiles(); // 知道欢迎页面
List<WrapperMappingInfo> wrappers = new ArrayList<>();
for (Container container : context.findChildren()) { // 找到servlet等
prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);
......
}
mapper.addContextVersion(host.getName(), host, contextPath,
context.getWebappVersion(), context, welcomeFiles, resources,
wrappers);
......
}
根据分析知道standardContext的子容器为standardWraper并且每一个standardWraper对应一个servlet,跟踪代码可知最后standardWraper都储存在了context.wildcardWrappers里面.
好了分析到这里,基本分析完结了.虽然本文很长,代码最后还是要总结一下的.
standardHost代表的是一个虚拟主机
standardContext代表的是webapps目录下的每一个路径
standardWrapper代表的是servlet容器.
好了下一篇接着分析,请求过程了.