tomcat-请求url初始化

tomcat启动时会调用Lifecycle的start()方法,MapperListener.java中的方法startInternal()会被调用,此时开始了请求url映射到容器的初始化之路。首先看下private final Mapper mapper;属性,这个属性包含了请求url到容器的所有映射关系,其中hosts是Host容器的数组。
Maper类图

public final class Mapper {
    volatile MappedHost[] hosts = new MappedHost[0];

    private volatile String defaultHostName = null;
    private volatile MappedHost defaultHost = null;
    private final Map<Context,ContextVersion> contextObjectToContextVersionMap = new ConcurrentHashMap<>();
}

tomcat中请求url与各容器的映射关系保存在Mapper中的,关系图如下:
请求url与各容器映射关系

在tomcat启动阶段会调用MapperListener.java中的模板方法startInternal()

    public void startInternal() throws LifecycleException {
......
        Container[] conHosts = engine.findChildren();
        for (Container conHost : conHosts) {
            Host host = (Host) conHost;
            if (!LifecycleState.NEW.equals(host.getState())) {
                // Registering the host will register the context and wrappers
                registerHost(host);
            }
        }
    }

    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);
            }
        }

        // Default host may have changed
        findDefaultHost();

        if (log.isDebugEnabled()) {
            log.debug(sm.getString("mapperListener.registerHost", host.getName(), domain, service));
        }
    }

首先添加host到mapper中,看下这个过程:

    public synchronized void addHost(String name, String[] aliases, Host host) {
        name = renameWildcardHost(name);
        MappedHost[] newHosts = new MappedHost[hosts.length + 1];
        MappedHost newHost = new MappedHost(name, host);
        if (insertMap(hosts, newHosts, newHost)) {
            hosts = newHosts;
            if (newHost.name.equals(defaultHostName)) {
                defaultHost = newHost;
            }
            if (log.isDebugEnabled()) {
                log.debug(sm.getString("mapper.addHost.success", name));
            }
        } else {
            MappedHost duplicate = hosts[find(hosts, name)];
            if (duplicate.object == host) {
                // The host is already registered in the mapper.
                // E.g. it might have been added by addContextVersion()
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("mapper.addHost.sameHost", name));
                }
                newHost = duplicate;
            } else {
                log.error(sm.getString("mapper.duplicateHost", name, duplicate.getRealHostName()));
                // Do not add aliases, as removeHost(hostName) won't be able to
                // remove them
                return;
            }
        }
        ......
    }

使用MappedHost去封装Host,生成的MappedHost会保存在属性hostsvolatile MappedHost[] hosts = new MappedHost[0];中,现在在Mapper中有Host了。Host就是主机嘛,映射域名的。Host是由tomcat解析server.xml文件生成,因此我们可以在server.xml文件中配置Host。接下来是注册Context到Host中registerContext。Context就是加载servlet的文件夹路径,通过这个路径去匹配Context。

    private void registerContext(Context context) {
......
        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));
        }
    }

  • 使用WrapperMappingInfo封装Wrapper
    使用WrapperMappingInfo去封装mapping映射的请求和Wrapper(StandardWrapper),然后添加到public MappedWrapper[] exactWrappers = new MappedWrapper[0];
    private void prepareWrapperMappingInfo(Context context, Wrapper wrapper, List<WrapperMappingInfo> wrappers) {
        String wrapperName = wrapper.getName();
        boolean resourceOnly = context.isResourceOnlyServlet(wrapperName);
        String[] mappings = wrapper.findMappings();
        for (String mapping : mappings) {
            boolean jspWildCard = (wrapperName.equals("jsp") && mapping.endsWith("/*"));
            wrappers.add(new WrapperMappingInfo(mapping, wrapper, jspWildCard, resourceOnly));
        }
    }
  • 添加MappedContext
    这里有点绕,首先在MappedHost中有个public volatile ContextList contextList;,contextListcontextList中有个contextspublic final MappedContext[] contexts;用来存储MappedContext,这里先用ContextVersion封装Context,再用MappedContext封装ContextVersion,然后保存到MappedHost中。可以看到ContextVersion是包含请求路径的,因此tomcat收到请求时可以根据url定位到对应的ContextVersion。
    public void addContextVersion(String hostName, Host host, String path, String version, Context context,
            String[] welcomeResources, WebResourceRoot resources, Collection<WrapperMappingInfo> wrappers) {
......
        int slashCount = slashCount(path);
        synchronized (mappedHost) {
            ContextVersion newContextVersion =
                    new ContextVersion(version, path, slashCount, context, resources, welcomeResources);
            if (wrappers != null) {
                addWrappers(newContextVersion, wrappers);
            }

            ContextList contextList = mappedHost.contextList;
            MappedContext mappedContext = exactFind(contextList.contexts, path);
            if (mappedContext == null) {
                mappedContext = new MappedContext(path, newContextVersion);
                ContextList newContextList = contextList.addContext(mappedContext, slashCount);
                if (newContextList != null) {
                    updateContextList(mappedHost, newContextList);
                    contextObjectToContextVersionMap.put(context, newContextVersion);
                }
            } else {
                ContextVersion[] contextVersions = mappedContext.versions;
                ContextVersion[] newContextVersions = new ContextVersion[contextVersions.length + 1];
                if (insertMap(contextVersions, newContextVersions, newContextVersion)) {
                    mappedContext.versions = newContextVersions;
                    contextObjectToContextVersionMap.put(context, newContextVersion);
                } else {
                    // Re-registration after Context.reload()
                    // Replace ContextVersion with the new one
                    int pos = find(contextVersions, version);
                    if (pos >= 0 && contextVersions[pos].name.equals(version)) {
                        contextVersions[pos] = newContextVersion;
                        contextObjectToContextVersionMap.put(context, newContextVersion);
                    }
                }
            }
        }

    }
  • 添加Wrapper
    这里逻辑也不复杂,还是层层封装。使用MappedWrapper封装WrapperMappingInfo包含的Wrapper和对应的请求路径,再添加到ContextVersion的public MappedWrapper[] exactWrappers = new MappedWrapper[0];即可,注意Wrapper中的请求路径就是servlet的url路径。
    protected void addWrapper(ContextVersion context, String path, Wrapper wrapper, boolean jspWildCard,
            boolean resourceOnly) {

        synchronized (context) {
            if (path.endsWith("/*")) {
                // Wildcard wrapper
                String name = path.substring(0, path.length() - 2);
                MappedWrapper newWrapper = new MappedWrapper(name, wrapper, jspWildCard, resourceOnly);
                MappedWrapper[] oldWrappers = context.wildcardWrappers;
                MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1];
                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
                    context.wildcardWrappers = newWrappers;
                    int slashCount = slashCount(newWrapper.name);
                    if (slashCount > context.nesting) {
                        context.nesting = slashCount;
                    }
                }
            } else if (path.startsWith("*.")) {
                // Extension wrapper
                String name = path.substring(2);
                MappedWrapper newWrapper = new MappedWrapper(name, wrapper, jspWildCard, resourceOnly);
                MappedWrapper[] oldWrappers = context.extensionWrappers;
                MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1];
                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
                    context.extensionWrappers = newWrappers;
                }
            } else if (path.equals("/")) {
                // Default wrapper
                MappedWrapper newWrapper = new MappedWrapper("", wrapper, jspWildCard, resourceOnly);
                context.defaultWrapper = newWrapper;
            } else {
                // Exact wrapper
                final String name;
                if (path.length() == 0) {
                    // Special case for the Context Root mapping which is
                    // treated as an exact match
                    name = "/";
                } else {
                    name = path;
                }
                MappedWrapper newWrapper = new MappedWrapper(name, wrapper, jspWildCard, resourceOnly);
                MappedWrapper[] oldWrappers = context.exactWrappers;
                MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1];
                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
                    context.exactWrappers = newWrappers;
                }
            }
        }
    }

至此完成了请求url到servlet相关容器的初始化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/663571.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

珠宝首饰AR虚拟3D试戴增强企业商品营销效果

在西安这座古老与现代交织的城市中&#xff0c;VRAR软件开发公司相比其他城市也略多一些&#xff0c;作为专业的西安AR软件开发公司&#xff0c;我们正凭借着前沿的AR增强现实/VR虚拟现实技术&#xff0c;为客户打造独一无二的互动体验。 专业团队&#xff0c;定制开发 我们拥有…

轻松入门:HTML网页制作指南 进阶篇

一.表格标签 1.1表格的主要作用 表格不是用来布局页面的,而是用来展示数据的。 1.2基本语法 <table><tr><td>单元格内的文字</td>...</tr>...</table>说明&#xff1a; 1.<table> </table> 是用于定义表格的标签。 2.<t…

服务器数据恢复—异常断电导致ESXi虚拟机无法启动的数据恢复案例

服务器数据恢复环境&#xff1a; 某大厂PS4000服务器&#xff0c;服务器上部署VMware ESXi虚拟化平台。 服务器故障&#xff1a; 机房断电&#xff0c;重启后服务器中的某台虚拟机不能正常启动。管理员查看虚拟机配置文件&#xff0c;发现无法启动的虚拟机的配置文件除了磁盘文…

vivado原语使用

首先介绍一下原语&#xff1a;其英文名字为Primitive。原语是Xilinx针对其器件特征开发的一系列常用模块的名字&#xff0c;用户可以将其看成Xilinx公司为用户提供的ip&#xff0c;是芯片中的基本元件&#xff0c;代表FPGA中实际拥有的硬件逻辑单元&#xff0c;如LUT&#xff0…

freertos初体验 - 在stm32上移植

1. 说明 freertos内核 非常精简&#xff0c;代码量也很少&#xff0c;官方也针对主流的编译器和内核准备好了移植文件&#xff0c;所以 freertos 的移植是非常简单的&#xff0c;很多工具&#xff08;例如CubeMX&#xff09;点点鼠标就可以生成一个 freertos 的工程&#xff0…

【Qt知识】Qt中的对象树是什么?

在深入Qt编程的世界时&#xff0c;你会频繁遇到一个核心概念——对象树&#xff08;Object Tree&#xff09;。这个概念是Qt框架管理内存、处理事件和组织用户界面元素的基础。 什么是Qt对象树&#xff1f; 如果你的Qt应用程序就像一片茂盛的森林&#xff0c;而这片森林中的每…

【linux深入剖析】进程间通信

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1.进程间通信目的2. 什么…

大语言模型应用与传统程序的不同

大语言模型&#xff08;LLM&#xff09; 被描述的神乎其神&#xff0c;无所不能&#xff0c;其实&#xff0c;大语言模型只是一个模型&#xff0c;它能够理解和生成自然语言&#xff0c;唯有依靠应用程序才能够发挥作用。例如&#xff0c;基于大模型可以构建一个最简单的会话机…

搜狐视频专访神工坊创始人任虎:以先进计算技术为引擎,引领新一代CAE革新之路

搜狐视频采访 神工坊&#xff08;无锡&#xff09;数字技术有限公司&#xff0c;源自国家超级计算无锡中心&#xff08;始于2016年&#xff09;&#xff0c;于2022年8月在无锡成立&#xff0c;是一家以高性能计算等先进计算技术推动新一代CAE技术革新的公司。 在全国科技工作者…

[Algorithm][动态规划][子序列问题][最长递增子序列][摆动序列]详细讲解

目录 0.子序列 vs 子数组1.最长递增子序列1.题目链接2.算法原理详解3.代码实现 2.摆动序列1.题目链接2.题目链接3.代码实现 0.子序列 vs 子数组 子序列&#xff1a; 相对顺序是跟源字符串/数组是一致的但是元素和元素之间&#xff0c;在源字符串/数组中可以是不连续的一般时间…

python绘制piper三线图

piper三线图 Piper三线图是一种常用于水化学分析的图表&#xff0c;它能够帮助我们理解和比较水样的化学成分。该图表由三个部分组成&#xff1a;两个三角形和一个菱形。两个三角形分别用于显示阳离子和阴离子的相对比例&#xff0c;而菱形部分则综合显示了这些离子比例在水样…

蓝屏,联想持续很久停留在正在准备windows请勿关闭计算机怎么办?解决方案如下。

联想小新&#xff0c;连续出现两次间隔一年半遇上上述问题&#xff0c;按电源键后也会出现下述情况&#xff0c;遇到问题不要着急&#xff0c;偶尔电脑也会生个小病什么的。 附上最后的解决办法&#xff1a;两次都成功┗|&#xff40;O′|┛ 嗷~~ 按上述步骤执行后重启计算机等…

Buffer Pool运行机制理解

Buffer Pool机制理解 一、为什么使用Buffer Pool&#xff1f; 众所周知&#xff0c;磁盘数据是以数据页的形式来去读取的&#xff0c;一个数据页默认大小 16K&#xff0c;也就是说你本意只想读取一行数据&#xff0c;但是它会给你加载一页的数据到buffer pool里面。这样的话就…

高等教育的AI革新:OpenAI面向大学推出ChatGPT Edu

OpenAI推出了ChatGPT Edu&#xff0c;这是一个为大学设计的专用版本&#xff0c;旨在让学生、教职员工、研究人员和校园运营能够负责任地使用AI。 ChatGPT Edu 将AI技术引入了教育领域&#xff0c;其建立在GPT-4o的基础上&#xff0c;它不仅能够处理文本和图像&#xff0c;还…

xss漏洞学习(题解)

1.简单知识点回顾 XSS允许恶意web用户将代码植入到提供给其它用户使用的页面中。 特点&#xff1a;能注入恶意的HTML/JavaScript代码到用户浏览器网址上&#xff0c;从而劫持会话 类型&#xff1a; DOM型&#xff1a;属于反射型的一种&#xff0c;利用非法输入来闭合对应的h…

【源码+文档+调试讲解】火车票订票系统设计与实现

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装火车票订票系统软件来发挥其高效地信息处理的作用&#xff…

车灯罩外壳破裂破损掉角等修复,修复后合面合壳密封用泰达克车灯无痕修复UV胶车灯合面密封UV胶。

一&#xff1a;专用车灯无痕修复UV胶 车灯无痕修复专用胶主要成份是改性丙烯酸UV树脂&#xff0c;主要应用在车灯的专业无痕修复领域。 具有如下特点&#xff1a; 固化时间快&#xff0c;通过紫外光照射5-15秒固化。 高度透明&#xff0c;固化后透光率高&#xff0c;折射率接…

佳能R6M2断电覆盖的恢复方法

佳能R6是佳能R系列中的一款高端机&#xff0c;最近两年佳能和索尼不断斗法&#xff0c;都号称自己的新机型能达到影视级&#xff0c;不过目前看貌似索尼更胜一筹。下边这个案例是文件拍摄时断电&#xff0c;结果变成0字节&#xff0c;然后覆盖了部分数据。 故障存储:128G存储卡…

新加坡裸机云站群服务器稳定性怎么样

新加坡裸机云站群服务器的稳定性在云计算领域备受关注&#xff0c;这得益于其卓越的硬件配置、先进的数据中心设计、优质的网络连接以及严格的管理措施。以下是对新加坡裸机云站群服务器稳定性的详细科普&#xff1a; 一、硬件与配置 新加坡裸机云站群服务器通常采用高性能的物…

12V升20V3.5A升压恒压WT3207

12V升20V3.5A升压恒压WT3207 WT3207是一款高效PWM升压控制器&#xff0c;采用SO-8封装设计&#xff0c;专为低输入电压应用优化。该控制器支持5V至36V的宽输入电压范围&#xff0c;使其能够有效提升12V、15V和19V系统的电压水平&#xff0c;特别适合于两节或三节锂离子电池供电…