深入剖析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两种容器。

相关推荐
xlsw_2 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹2 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭3 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫3 小时前
泛型(2)
java
超爱吃士力架3 小时前
邀请逻辑
java·linux·后端
南宫生3 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石3 小时前
12/21java基础
java
李小白664 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp4 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
装不满的克莱因瓶4 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb