编辑
2023-11-03
Tomcat源码学习
00
请注意,本文编写于 554 天前,最后修改于 431 天前,其中某些信息可能已经过时。

目录

MapElement
ContextVersion
MappedHost
MappedContext
MappedWarpper
注册容器方法

image.png

MapElement

java
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; } } // ------------

ContextVersion

java
protected static final class ContextVersion extends MapElement<Context> { public final String path; public final int slashCount; public final WebResourceRoot resources; public String[] welcomeResources; public MappedWrapper defaultWrapper = null; public MappedWrapper[] exactWrappers = new MappedWrapper[0]; public MappedWrapper[] wildcardWrappers = new MappedWrapper[0]; public MappedWrapper[] extensionWrappers = new MappedWrapper[0]; public int nesting = 0; private volatile boolean paused; public ContextVersion(String version, String path, int slashCount, Context context, WebResourceRoot resources, String[] welcomeResources) { super(version, context); this.path = path; this.slashCount = slashCount; this.resources = resources; this.welcomeResources = welcomeResources; } public boolean isPaused() { return paused; } public void markPaused() { paused = true; } }

MappedHost

java
protected static final class MappedHost extends MapElement<Host> { public volatile ContextList contextList; /** * Link to the "real" MappedHost, shared by all aliases. */ private final MappedHost realHost; /** * Links to all registered aliases, for easy enumeration. This field is available only in the "real" MappedHost. * In an alias this field is <code>null</code>. */ private final List<MappedHost> aliases; /** * Constructor used for the primary Host * * @param name The name of the virtual host * @param host The host */ public MappedHost(String name, Host host) { super(name, host); realHost = this; contextList = new ContextList(); aliases = new CopyOnWriteArrayList<>(); } /** * Constructor used for an Alias * * @param alias The alias of the virtual host * @param realHost The host the alias points to */ public MappedHost(String alias, MappedHost realHost) { super(alias, realHost.object); this.realHost = realHost; this.contextList = realHost.contextList; this.aliases = null; } public boolean isAlias() { return realHost != this; } public MappedHost getRealHost() { return realHost; } public String getRealHostName() { return realHost.name; } public Collection<MappedHost> getAliases() { return aliases; } public void addAlias(MappedHost alias) { aliases.add(alias); } public void addAliases(Collection<? extends MappedHost> c) { aliases.addAll(c); } public void removeAlias(MappedHost alias) { aliases.remove(alias); } }

MappedContext

java
protected static final class MappedContext extends MapElement<Void> { public volatile ContextVersion[] versions; public MappedContext(String name, ContextVersion firstVersion) { super(name, null); this.versions = new ContextVersion[] { firstVersion }; } }

MappedWarpper

java
protected static class MappedWrapper extends MapElement<Wrapper> { public final boolean jspWildCard; public final boolean resourceOnly; public MappedWrapper(String name, Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) { super(name, wrapper); this.jspWildCard = jspWildCard; this.resourceOnly = resourceOnly; } }

这四个类是包含关系,一条链。

Mapper 的构建过程是在 MapperListener 中完成的, MapperListener 实现了 Tomcat 的生命周期接口,在 StandardService 启动的时候会调用 MapperListener 的 start 方法,这里会去构建 Mapper. MapperListener 也实现了 ContainerListener 和 LifecycleListener 接口,在发生容器事件和生命周期事件的时候,会往 Mapper 中添加或删除一些映射。

java
@Override public void containerEvent(ContainerEvent event) { // 处理容器事件的方法 if (Container.ADD_CHILD_EVENT.equals(event.getType())) { // 如果是添加子容器事件 Container child = (Container) event.getData(); addListeners(child); // 如果子容器已经启动,则在这里注册子容器(根据子容器类型执行不同的注册操作) if (child.getState().isAvailable()) { if (child instanceof Host) { // 如果子容器是 Host 类型 registerHost((Host) child); } else if (child instanceof Context) { // 如果子容器是 Context 类型 registerContext((Context) child); } else if (child instanceof Wrapper) { // 如果子容器是 Wrapper 类型 // 只有当 Context 已经启动时才注册 Wrapper if (child.getParent().getState().isAvailable()) { registerWrapper((Wrapper) child); } } } } else if (Container.REMOVE_CHILD_EVENT.equals(event.getType())) { // 如果是移除子容器事件 Container child = (Container) event.getData(); removeListeners(child); // 不需要取消注册,生命周期监听器会在子容器停止时处理此操作 } else if (Host.ADD_ALIAS_EVENT.equals(event.getType())) { // 如果是添加主机别名事件 // 处理动态添加主机别名 mapper.addHostAlias(((Host) event.getSource()).getName(), event.getData().toString()); } else if (Host.REMOVE_ALIAS_EVENT.equals(event.getType())) { // 如果是移除主机别名事件 // 处理动态移除主机别名 mapper.removeHostAlias(event.getData().toString()); } else if (Wrapper.ADD_MAPPING_EVENT.equals(event.getType())) { // 如果是添加映射事件 // 处理动态添加映射器 Wrapper wrapper = (Wrapper) event.getSource(); Context context = (Context) wrapper.getParent(); String contextPath = context.getPath(); if ("/".equals(contextPath)) { contextPath = ""; } String version = context.getWebappVersion(); String hostName = context.getParent().getName(); String wrapperName = wrapper.getName(); String mapping = (String) event.getData(); boolean jspWildCard = ("jsp".equals(wrapperName) && mapping.endsWith("/*")); mapper.addWrapper(hostName, contextPath, version, mapping, wrapper, jspWildCard, context.isResourceOnlyServlet(wrapperName)); } else if (Wrapper.REMOVE_MAPPING_EVENT.equals(event.getType())) { // 如果是移除映射事件 // 处理动态移除映射器 Wrapper wrapper = (Wrapper) event.getSource(); Context context = (Context) wrapper.getParent(); String contextPath = context.getPath(); if ("/".equals(contextPath)) { contextPath = ""; } String version = context.getWebappVersion(); String hostName = context.getParent().getName(); String mapping = (String) event.getData(); mapper.removeWrapper(hostName, contextPath, version, mapping); } else if (Context.ADD_WELCOME_FILE_EVENT.equals(event.getType())) { // 如果是添加欢迎文件事件 // 处理动态添加欢迎文件 Context context = (Context) event.getSource(); String hostName = context.getParent().getName(); String contextPath = context.getPath(); if ("/".equals(contextPath)) { contextPath = ""; } String welcomeFile = (String) event.getData(); mapper.addWelcomeFile(hostName, contextPath, context.getWebappVersion(), welcomeFile); } else if (Context.REMOVE_WELCOME_FILE_EVENT.equals(event.getType())) { // 如果是移除欢迎文件事件 // 处理动态移除欢迎文件 Context context = (Context) event.getSource(); String hostName = context.getParent().getName(); String contextPath = context.getPath(); if ("/".equals(contextPath)) { contextPath = ""; } String welcomeFile = (String) event.getData(); mapper.removeWelcomeFile(hostName, contextPath, context.getWebappVersion(), welcomeFile); } else if (Context.CLEAR_WELCOME_FILES_EVENT.equals(event.getType())) { // 如果是清除欢迎文件事件 // 处理动态清除欢迎文件 Context context = (Context) event.getSource(); String hostName = context.getParent().getName(); String contextPath = context.getPath(); if ("/".equals(contextPath)) { contextPath = ""; } String version = context.getWebappVersion(); mapper.clearWelcomeFiles(hostName, contextPath, version); } }

注册容器方法

java
private void registerHost(Host host) { // 获取主机的别名数组 String[] aliases = host.findAliases(); // 将主机和其别名添加到映射器中 mapper.addHost(host.getName(), aliases, host); // 遍历主机的子容器 for (Container container : host.findChildren()) { // 如果容器的状态可用,则注册上下文 if (container.getState().isAvailable()) { registerContext((Context) container); } } // 默认主机可能已更改 findDefaultHost(); if (log.isDebugEnabled()) { log.debug(sm.getString("mapperListener.registerHost", host.getName(), domain, service)); } } /** * 注销主机。 */ private void unregisterHost(Host host) { // 获取主机名称 String hostname = host.getName(); // 从映射器中移除主机 mapper.removeHost(hostname); // 默认主机可能已更改 findDefaultHost(); if (log.isDebugEnabled()) { log.debug(sm.getString("mapperListener.unregisterHost", hostname, domain, service)); } } /** * 注销包装器。 */ private void unregisterWrapper(Wrapper wrapper) { // 获取包装器所在的上下文和相关信息 Context context = ((Context) wrapper.getParent()); String contextPath = context.getPath(); String wrapperName = wrapper.getName(); // 如果上下文路径是根路径,将其设置为空字符串 if ("/".equals(contextPath)) { contextPath = ""; } String version = context.getWebappVersion(); String hostName = context.getParent().getName(); // 获取包装器的映射路径数组 String[] mappings = wrapper.findMappings(); // 遍历映射路径数组,从映射器中移除对应的映射 for (String mapping : mappings) { mapper.removeWrapper(hostName, contextPath, version, mapping); } if (log.isDebugEnabled()) { log.debug(sm.getString("mapperListener.unregisterWrapper", wrapperName, contextPath, service)); } } /** * 注册上下文。 */ private void registerContext(Context context) { // 获取上下文路径 String contextPath = context.getPath(); // 如果上下文路径是根路径,将其设置为空字符串 if ("/".equals(contextPath)) { contextPath = ""; } Host host = (Host) context.getParent(); // 获取上下文的资源、欢迎文件和包装器列表 WebResourceRoot resources = context.getResources(); String[] welcomeFiles = context.findWelcomeFiles(); List<WrapperMappingInfo> wrappers = new ArrayList<>(); // 遍历上下文的子容器 for (Container container : context.findChildren()) { // 准备包装器的映射信息,并添加到包装器列表中 prepareWrapperMappingInfo(context, (Wrapper) container, wrappers); if (log.isDebugEnabled()) { log.debug(sm.getString("mapperListener.registerWrapper", container.getName(), contextPath, service)); } } // 将上下文和相关信息添加到映射器中 mapper.addContextVersion(host.getName(), host, contextPath, context.getWebappVersion(), context, welcomeFiles, resources, wrappers); if (log.isDebugEnabled()) { log.debug(sm.getString("mapperListener.registerContext", contextPath, service)); } } /** * 注销上下文。 */ private void unregisterContext(Context context) { // 获取上下文路径 String contextPath = context.getPath(); // 如果上下文路径是根路径,将其设置为空字符串 if ("/".equals(contextPath)) { contextPath = ""; } String hostName = context.getParent().getName(); // 如果上下文已暂停,暂停上下文的版本;否则从映射器中移除上下文的版本 if (context.getPaused()) { if (log.isDebugEnabled()) { log.debug(sm.getString("mapperListener.pauseContext", contextPath, service)); } mapper.pauseContextVersion(context, hostName, contextPath, context.getWebappVersion()); } else { if (log.isDebugEnabled()) { log.debug(sm.getString("mapperListener.unregisterContext", contextPath, service)); } mapper.removeContextVersion(context, hostName, contextPath, context.getWebappVersion()); } } /** * 注册包装器。 */ private void registerWrapper(Wrapper wrapper) { // 获取包装器所在的上下文、上下文路径、版本和主机名称 Context context = ((Context) wrapper.getParent()); String contextPath = context.getPath(); String version = context.getWebappVersion(); String hostName = context.getParent().getName(); // 准备包装器的映射信息,并添加到映射器中 List<WrapperMappingInfo> mappingInfoList = new ArrayList<>(); prepareWrapperMappingInfo(context, wrapper, mappingInfoList); mapper.addWrappers(hostName, contextPath, version, mappingInfoList); if (log.isDebugEnabled()) { log.debug(sm.getString("mapperListener.registerWrapper", wrapper.getName(), contextPath, service)); } }

registerHost(Host host) 方法:

  • 获取主机的别名(aliases)数组。
  • 调用mapper.addHost()方法,将主机和其别名添加到映射器(mapper)中。
  • 遍历主机的子容器,如果容器的状态是可用的,则调用registerContext()方法,注册上下文(Context)。

unregisterHost(Host host) 方法:

  • 获取主机的名称。
  • 调用mapper.removeHost()方法,从映射器中移除主机。
  • 调用findDefaultHost()方法,找到默认的主机。

unregisterWrapper(Wrapper wrapper) 方法:

  • 获取包装器所在的上下文(Context)和相关信息。
  • 遍历包装器的映射路径(mappings),调用mapper.removeWrapper()方法,从映射器中移除相应的映射。

registerContext(Context context) 方法:

  • 获取上下文的路径(contextPath)。
  • 如果上下文路径是根路径"/",则将其设置为空字符串。
  • 获取上下文的父级主机(Host)。
  • 获取上下文的资源(WebResourceRoot)、欢迎文件(welcomeFiles)和包装器(Wrapper)列表。
  • 遍历上下文的子容器,调用prepareWrapperMappingInfo()方法,准备包装器的映射信息,并将它们添加到包装器列表中。
  • 调用mapper.addContextVersion()方法,将上下文和相关信息添加到映射器中。

unregisterContext(Context context) 方法:

  • 获取上下文的路径(contextPath)。
  • 如果上下文路径是根路径"/",则将其设置为空字符串。
  • 获取上下文的父级主机的名称。
  • 如果上下文已暂停,调用mapper.pauseContextVersion()方法,暂停上下文的版本。
  • 否则,调用mapper.removeContextVersion()方法,从映射器中移除上下文的版本。

registerWrapper(Wrapper wrapper) 方法:

  • 获取包装器所在的上下文、上下文路径、版本和主机名称。
  • 创建一个包装器映射信息的列表。
  • 调用prepareWrapperMappingInfo()方法,准备包装器的映射信息,并将其添加到列表中。
  • 调用mapper.addWrappers()方法,将包装器的映射信息添加到映射器中。

本文作者:yowayimono

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!