Tomcat系统架构分析

    1. Host 容器

Host 是 Engine 的字容器,一个 Host 在 Engine 中代表一个虚拟主机,这个虚拟主机的作用就是运行多个应用,它负责安装和展开这些应用,并且标识这个应用以便能够区分它们。它的子容器通常是 Context,它除了关联子容器外,还有就是保存一个主机应该有的信息。

1). Host 相关的类图

从上图中可以看出除了所有容器都继承的 ContainerBase 外,StandardHost 还实现了 Deployer 接口,上图清楚的列出了这个接口的主要方法,这些方法都是安装、展开、启动和结束每个 web application。

Deployer 接口的实现是 StandardHostDeployer,这个类实现了的最要的几个方法,Host 可以调用这些方法完成应用的部署等。

    1. Context 容器

Context 代表 Servlet 的 Context,它具备了 Servlet 运行的基本环境,理论上只要有 Context 就能运行 Servlet 了。简单的 Tomcat 可以没有 Engine 和 Host。

Context 最重要的功能就是管理它里面的 Servlet 实例,Servlet 实例在 Context 中是以 Wrapper 出现的,还有一点就是 Context 如何才能找到正确的 Servlet 来执行它呢? Tomcat5 以前是通过一个 Mapper 类来管理的,Tomcat5 以后这个功能被移到了 request 中,在前面的时序图中就可以发现获取子容器都是通过 request 来分配的。

Context 准备 Servlet 的运行环境是在 Start 方法开始的,这个方法的代码片段如下:

1). StandardContext.start

public synchronized void start() throws LifecycleException {

    ………

    if( !initialized ) {

        try {

            init();

        } catch( Exception ex ) {

            throw new LifecycleException("Error initializaing ", ex);

        }

    }

    

………

    lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);

    setAvailable(false);

    setConfigured(false);

    boolean ok = true;

    File configBase = getConfigBase();

    if (configBase != null) {

        if (getConfigFile() == null) {

            File file = new File(configBase, getDefaultConfigFile());

            setConfigFile(file.getPath());

            try {

                File appBaseFile = new File(getAppBase());

                if (!appBaseFile.isAbsolute()) {

                    appBaseFile = new File(engineBase(), getAppBase());

                }

                String appBase = appBaseFile.getCanonicalPath();

                String basePath =

                    (new File(getBasePath())).getCanonicalPath();

                if (!basePath.startsWith(appBase)) {

                    Server server = ServerFactory.getServer();

                    ((StandardServer) server).storeContext(this);

                }

            } catch (Exception e) {

                log.warn("Error storing config file", e);

            }

        } else {

            try {

                String canConfigFile =  (new File(getConfigFile())).getCanonicalPath();

                if (!canConfigFile.startsWith (configBase.getCanonicalPath())) {

                    File file = new File(configBase, getDefaultConfigFile());

                    if (copy(new File(canConfigFile), file)) {

                        setConfigFile(file.getPath());

                    }

                }

            } catch (Exception e) {

                log.warn("Error setting config file", e);

            }

        }

    }

 

    ………

    Container children[] = findChildren();

    for (int i = 0; i < children.length; i++) {

        if (children[i] instanceof Lifecycle)

            ((Lifecycle) children[i]).start();

    }

    

if (pipeline instanceof Lifecycle)

        ((Lifecycle) pipeline).start();

    ………

 

}

它主要是设置各种资源属性和管理组件,还有非常重要的就是启动子容器和 Pipeline。

我们知道 Context 的配置文件中有个 reloadable 属性,如下面配置:

2).  Server.xml

<Context

    path="/library"

    docBase="D:\projects\library\deploy\target\library.war"

    reloadable="true"

/>

当这个 reloadable 设为 true 时,war 被修改后 Tomcat 会自动的重新加载这个应用。如何做到这点的呢 ? 这个功能是在 StandardContext 的 backgroundProcess 方法中实现的,这个方法的代码如下:

3).  StandardContext. backgroundProcess

public void backgroundProcess() {

    if (!started) return;

    count = (count + 1) % managerChecksFrequency;

    if ((getManager() != null) && (count == 0)) {

        try {

            getManager().backgroundProcess();

        } catch ( Exception x ) {

            log.warn("Unable to perform background process on manager",x);

        }

    }

    if (getLoader() != null) {

        if (reloadable && (getLoader().modified())) {

            try {

                Thread.currentThread().setContextClassLoader

                    (StandardContext.class.getClassLoader());

                reload();

            } finally {

                if (getLoader() != null) {

                    Thread.currentThread().setContextClassLoader

                        (getLoader().getClassLoader());

                }

            }

        }

        if (getLoader() instanceof WebappLoader) {

            ((WebappLoader) getLoader()).closeJARs(false);

        }

    }

}

它会调用 reload 方法,而 reload 方法会先调用 stop 方法然后再调用 Start 方法,完成 Context 的一次重新加载。可以看出执行 reload 方法的条件是 reloadable 为 true 和应用被修改,那么这个 backgroundProcess 方法是怎么被调用的呢?

这个方法是在 ContainerBase 类中定义的内部类 ContainerBackgroundProcessor 被周期调用的,这个类是运行在一个后台线程中,它会周期的执行 run 方法,它的 run 方法会周期调用所有容器的 backgroundProcess 方法,因为所有容器都会继承 ContainerBase 类,所以所有容器都能够在 backgroundProcess 方法中定义周期执行的事件。

    1. Wrapper 容器

Wrapper 代表一个 Servlet,它负责管理一个 Servlet,包括的 Servlet 的装载、初始化、执行以及资源回收。Wrapper 是最底层的容器,它没有子容器了,所以调用它的 addChild 将会报错。

Wrapper 的实现类是 StandardWrapper,StandardWrapper 还实现了拥有一个 Servlet 初始化信息的 ServletConfig,由此看出 StandardWrapper 将直接和 Servlet 的各种信息打交道。

下面看一下非常重要的一个方法 loadServlet,代码片段如下:

1). StandardWrapper.loadServlet

public synchronized Servlet loadServlet() throws ServletException {

    ………

    Servlet servlet;

    try {

        ………

        ClassLoader classLoader = loader.getClassLoader();

        ………

        Class classClass = null;

        ………

        servlet = (Servlet) classClass.newInstance();

        if ((servlet instanceof ContainerServlet) &&

            (isContainerProvidedServlet(actualClass) ||

            ((Context)getParent()).getPrivileged() )) {

                ((ContainerServlet) servlet).setWrapper(this);

        }

        classLoadTime=(int) (System.currentTimeMillis() -t1);

        try {

            instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,servlet);

            if( System.getSecurityManager() != null) {

                Class[] classType = new Class[]{ServletConfig.class};

                Object[] args = new Object[]{((ServletConfig)facade)};

                SecurityUtil.doAsPrivilege("init",servlet,classType,args);

            } else {

                servlet.init(facade);

            }

            if ((loadOnStartup >= 0) && (jspFile != null)) {

                ………

                if( System.getSecurityManager() != null) {

                    Class[] classType = new Class[]{ServletRequest.class,

                        ServletResponse.class};

                    Object[] args = new Object[]{req, res};

                    SecurityUtil.doAsPrivilege("service",servlet,classType,args);

                } else {

                    servlet.service(req, res);

                }

            }

            instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,servlet);

            ………

        

return servlet;

}

它基本上描述了对 Servlet 的操作,当装载了 Servlet 后就会调用 Servlet 的 init 方法,同时会传一个 StandardWrapperFacade 对象给 Servlet,这个对象包装了 StandardWrapper,ServletConfig 与它们的关系图如下:

2). ServletConf与 StandardWrapperFacade、StandardWrapper 的关系

Servlet 可以获得的信息都在 StandardWrapperFacade 封装,这些信息又是在 StandardWrapper 对象中拿到的。所以 Servlet 可以通过 ServletConfig 拿到有限的容器的信息。

当 Servlet 被初始化完成后,就等着 StandardWrapperValve 去调用它的 service 方法了,调用 service 方法之前要调用 Servlet 所有的 filter。

  1. Tomcat 中其它组件

Tomcat 还有其它重要的组件,如安全组件 security、logger 日志组件、session、mbeans、naming 等其它组件。这些组件共同为 Connector 和 Container 提供必要的服务。

 

 

本教程由尚硅谷教育大数据研究院出品,如需转载请注明来源。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值