深入剖析Tomcat(十二) 详解StandardContext

StandardContext是Context容器的一个标准实现,一般情况下,Tomcat都是使用StandardContext类来创建Context容器。前面讲过,Context容器代表了一个Web应用,Tomcat本身支持部署多个应用,相应的每个应用都会有一个StandardContext实例来对应。在使用SpringBoot的前提下,一个Tomcat也就仅创建一个StandardContext实例,来服务这个SpringBoot框架对应的应用。

StandardContext既然针对的是一整个Web应用,那这个Web应用的功能就需要靠它来支撑起来,StandardContext通过一系列组件来实现它的功能。

载入器

Tomcat规定了每个Context容器都只加载自己应用下的类,不能加载该Tomcat下其他应用的类,所以,StandardContext持有一个"载入器(Loader)"对象,用来加载该应用程序下的所有类(servlet及相关类)。Loader的标准实现为WebappLoader。

重载

StandardContext中有一个reloadable属性,通过 setReloadable 方法可以设置它的值,如果设为true的话,证明该Context容器支持重载,Tomcat会安排一个后台线程不断地检查该Context关联的class及jar包的最后修改日期,如果发现有变化,就会重新加载该Context容器下的所有类。如果reloadable设为false,那么该Context容器不支持重载。

会话(session)

会话也是维护在Context这一层的,所以StandardContext中持有"会话管理器(Manager)"对象,来管理针对该Web应用的会话。Manager的标准实现为StandardManager,单个session的标准实现为StandardSession(实现了HttpSession接口)。session只在需要时才会创建,Tomcat会安排一个后台线程不断检查session对象有没有过期,如果过期了就将其失效。

子容器与映射器

一个Web应用程序一般来说会有多个servlet实例,一个servlet就对应一个Wrapper容器实例,所以一个Context实例下就会管理多个Wrapper实例,这些Wrapper实例称为Context的"子容器"。当Context接收到一个servlet请求后,需要知道该把这个请求具体交给哪个Wrapper来处理,这其间就需要有个映射关系了,将servlet的请求uri映射到具体的Wrapper上,StandardContext就持有一个"映射器(Mapper)"对象来实现这个映射逻辑。

StandardContext中维护了 servletMappings 与 children 两个map集合,通过这两个map,很容易就可以根据请求uri找到对应的Wrapper实例。

来看一下"映射器"是如何实现此映射逻辑的,StandardContextMapper是映射器的标准实现,其中的map方法就是通过request找Wrapper的方法,下面是缩减版的代码

java 复制代码
public Container map(Request request, boolean update) {

    // 获取相对路径uri  (假设请求的资源路径为 /myapp/animal/tom, /myapp 代表部署在Tomcat中的应用名为myapp)
    String contextPath = ((HttpServletRequest) request.getRequest()).getContextPath();   //结果示例: "/myapp"
    String requestURI = ((HttpRequest) request).getDecodedRequestURI();                  //结果示例: "/myapp/animal/tom"
    String relativeURI = requestURI.substring(contextPath.length());                     //结果示例: "/animal/tom"

    Wrapper wrapper = null;

    if (!(relativeURI.equals("/")))
        name = context.findServletMapping(relativeURI);
    if (name != null)
        wrapper = (Wrapper) context.findChild(name);


    return wrapper;
}

上面映射器的逻辑是Tomcat4的逻辑,自Tomcat5之后,获取Wrapper的代码被放到了Request对象中,StandardContextMapper这个类也没有了,但是整体逻辑大致想通,明白思想即可。

start() 与 invoke()

上面列举了StandardContext中持有的比较重要的几个组件,各个组件也为StandardContext提供了相应的功能实现。而StandardContext具体应用这些组件的逻辑则主要集中在两个方法中:start()与invoke()。

**start()**方法是一个生命周期方法,Tomcat启动时会调用所有组件的start()方法,在StandardContext中,start()方法主要完成的功能是

  • 配置资源,识别到该Context容器对应的应用目录,将class及jar纳入自己的资源库中。
  • 创建各个组件实例,并调用各组件的start()方法完成组件的初始化。
  • 创建各个子容器(Wrapper)实例,并调用子容器的start()方法完成子容器的初始化。

start()方法主要是完成Context的一系列初始化操作,start()方法执行成功后,该StandardContext就可以对外工作了。

**invoke()**方法是StandardContext处理具体请求的方法,当一个servlet请求打进来后,就会进入到invoke()方法中,StandardContext#invoke()方法逻辑很简单,就是调用了一下它的Pipeline组件的invoke方法,其实真正对请求进行处理的对象是该Pipeline的基础阀:StandardContextValve。StandardContextValve#invoke() 方法会调用到映射器的map方法,根据请求uri找到指定Wrapper对象,然后调用Wrapper对象的invoke()方法,等Wrapper#invoke()方法执行结束后,StandardContext对本次servlet请求的处理也就基本完成了。(Wrapper#invoke()方法的逻辑请参考上一篇文章)。

在servlet的service方法中,如果有获取session的逻辑,那么StandardContext就会为其去寻找session,如果没有找到就会创建一个session给servlet用。具体逻辑可以参考第九篇文章。

StandardContext的基本逻辑就讲到这里,这篇文章串联了前些篇文章的知识,将StandardContext的完整脉络呈现了一下。下篇文章来研究下Host与Engine两种容器。

相关推荐
xyliiiiiL1 分钟前
一文总结常见项目排查
java·服务器·数据库
shaoing3 分钟前
MySQL 错误 报错:Table ‘performance_schema.session_variables’ Doesn’t Exist
java·开发语言·数据库
腥臭腐朽的日子熠熠生辉1 小时前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
ejinxian1 小时前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之1 小时前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
俏布斯2 小时前
算法日常记录
java·算法·leetcode
27669582922 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿
爱的叹息2 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring
程序猿chen2 小时前
《JVM考古现场(十五):熵火燎原——从量子递归到热寂晶壁的代码涅槃》
java·jvm·git·后端·java-ee·区块链·量子计算