首页 > 代码库 > servlet工作原理

servlet工作原理

首先提出几个问题:

1.servlet容器是如何工作的;

2.一个Web工程在servlet容器中是如何启动的;

3.servlet容器如何解析你在web.xml中定义的servlet;

4.用户的请求是如何被分配给指定的servlet的;

5.servlet容器如何管理servlet生命周期;

servlet容器

servlet与servlet容器彼此依存,相互独立发展。这是为了适应工业化生产而解耦,通过标准化接口来相互协作。所以接口是连接servlet与servlet容器的关键。

servlet容器作为一个独立发展的标准化产品,种类很多,以Tomcat为例:

在Tomcat 的容器等级中,Context 容器直接管理Servlet在容器中的包装类 Wrapper,所以Context容器如何运行将直接影响Servlet 的工作方式。

Tomcat 的容器分为4个等级:

【Container【Engine【Host【Context【Wrapper1,Wrapper2...】,Context【Wrapper1,Wrapper2...】...】】】

真正管理Servlet的容器是Context容器,一个Context对应一个web工程,在Tomcat的配置文件中可以很容易地发现这一点

如下:<Context  path="/projectOne"  docBase="D:\projects\projectOne"  reloadable="true" />

Servlet容器的启动过程

Tomcat7开始支持嵌入式功能,增加了一个启动类org.apache.catalina.startup.Tomcat。创建一个实例对象并调用start方法就可以很容易地启动Tomcat。还可以通过这个对象来增加和修改Tomcat的配置参数,如动态增加Context,Servlet等。

利用Tomcat类来管理一个新增的Context容器,选择example中的一个工程,看看它是如何加到这个Context中的

package org.apache.catalina.webapp;

import static org.junit.Assert.*;

import java.io.File;

import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
import org.junit.Test;

public class LoadApp extends TomcatBaseTest {
    @Test
    public void loadAppTest() throws Exception{
        Tomcat tomcat = getTomcatInstance();
        File appDir = new File(System.getProperty("tomcat.test.tomcatbuild"), "webapps/examples");
        tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
        tomcat.start();
        ByteChunk res = getUrl("http://localhost:" + getPort()+ 
                "/examples/servlets/servlet/HelloWorldExample");
        assertTrue(res.toString().indexOf("<h1>Hello World!</h1>") > 0);
    }
}

一个Web应用对应一个Context容器,也就是Servlet运行时的Servlet容器。添加一个Web应用时将会创建一个StandardContext容器,并且给这个容器设置必要的参数,url和path分别代表这个应用在Tomcat中的访问路径和这个应用实际的物理路径,这两个参数与Tomcat配置中的两个参数是一致的。其中最重要的一个配置是ContextConfiig,这个类将会负责整个Web应用配置的解析工作,最后将这个Context容器加到父容器Host中。

Tomcat的addWebapp方法如下:

/**
     * @see #addWebapp(String, String)
     */
    public Context addWebapp(Host host, String url, String name, String path) {
        silence(host, url);

        Context ctx = new StandardContext();
        ctx.setName(name);
        ctx.setPath(url);
        ctx.setDocBase(path);
        ctx.addLifecycleListener(new DefaultWebXmlListener());
        ctx.setConfigFile(getWebappConfigFile(path, url));

        ContextConfig ctxCfg = new ContextConfig();
        ctx.addLifecycleListener(ctxCfg);

        // prevent it from looking ( if it finds one - it‘ll have dup error )
        ctxCfg.setDefaultWebXml(noDefaultWebXmlPath());

        if (host == null) {
            getHost().addChild(ctx);
        } else {
            host.addChild(ctx);
        }

        return ctx;
    }

最后调用Tomcat的start方法启动。Tomcat的启动逻辑是基于观察者模式设计的,所有的容器都会继承Lifecycle接口,它管理着容器的整个生命周期,所有容器的修改和状态的改变都会由它去通知已经注册的Listener。Tomcat启动的时序图如下:

技术分享

12.当Context的state改为init时,ContextConfig作为观察者将会被通知,ContextConfig.lifecycleEvent方法将会被触发。

13.完成对应的每个web应用的配置解析。

14.完成web应用的初始化工作

15.后台线程,用于处理一些定时操作或者监控配置的修改。

18.启动HTTP服务。

上图描述了Tomcat的启动过程中主要类之间的时序关系,下面重点关注下StandardContext容器的启动过程:

当Context容器初始化状态设为init时,添加到Context容器的Listener将会被调用。ContextConfig继承了LifecycleListener接口,它在调用Tomcat.addWebapp时被加入到StandardContext容器中。ContextConfig类会负责整个web应用的配置文件的解析工作。ContextConfig的init方法主要会完成以下工作:

1.创建用于解析xml配置文件的contextDigester对象

2.读取默认的context.xml配置文件,如果存在则解析它

3.读取默认的Host配置文件,如果存在则解析它

4.读取默认的Context自身的配置文件,如果存在则解析它

5.设置Context的DocBaseContextConfig的init方法完成后,Context容器就会执行startInternal方法,主要包括以下部分:

1.创建读取资源文件的对象

2.创建ClassLoader对象

3.设置应用的工作目录

4.启动相关的辅助类,如logger、realm、resources等

5.修改启动状态,通知感兴趣的观察者(web应用的配置)

6.子容器的初始化

7.获取ServletContext并设置必要的参数

8.初始化“load on startup”的servlet

 

 

 

 

 

 

#笔记内容来自 《深入分析 java web》

servlet工作原理