Tomcat6.0源码学习-构建Eclipse源码工程

摘要

        对于学习j2ee,且想提高自己Java编程设计水平的每个人来说,研究Tomcat的源码是一件很向往的事,这其中的获益不仅仅是Java编程水平的提高,还有很多其他方面,如j2ee规范的了解,设计模式的运用,产品构建及版本控制等等,这些个方面无一不是我们学习和模拟的对象。但是,当我们开始这段旅程时,往往会迷失于浩瀚的代码海洋之中,这个时候,我们多么希望有这么一盏明灯,指引我们向前进。

在Eclipse中构建Tomcat6.0源码工程

        毫不夸张的说,Tomcat 6项目是最友好的项目,原因是它的源码工程构建起来很是方便,它没有使用很先进的maven工具来管理工程,也没有使用通用的IDE来构建工程,而它其实就是一个Eclipse的Java工程,构建时只使用ant。从这一点来看,好像特定了IDE,对贡献者的开发环境要求限制了,但是它却吸引了更多的贡献者。毕竟,像我这样的程序员还是很多,一没钱机子烂,通常只用Eclipse开发,看见Eclipse工程很亲切

      好了,如前文所说,Tomcat 6项目就是一个Eclipse的Java工程,那么我们直接用Eclipse中的SVN插件上

http://svn.apache.org/repos/asf/tomcat/tc6.0.x/tags/TOMCAT_6_0_20/导出工程到本地的workspace中。

如果有错,那就是因为classpath中找不到ANT_HOME变量和TOMCAT_LIBS_BASE,重新设置一下ANT_HOME和TOMCAT_LIBS_BASE,

如ANT_HOME=D:\JavaTeam\apache-ant-1.7.1,TOMCAT_LIBS_BASE=D:\JavaTeam\eclipse。

     如果你没有SVN插件,也没关系,在http://tomcat.apache.org/download-60.cgi下载Tomcat 6.0.20的源码包,然后解压到workspace。

     由于源码包没有.project文件和.classpath文件,所以我们要制作一个,什么?制作太麻烦,好吧,那下载一个,在http://svn.apache.org/repos/asf/tomcat/tc6.0.x/tags/TOMCAT_6_0_20/目录下,把.project文件和.classpath文件保存到解开的源码目录(apache-tomcat-6.0.20-src)下,然后,在Eclipse中导入该工程。ok,这下跟刚才的过程一样了。


Tomcat6.0源码学习--启动框架 

1.摘要

对于独立运行的应用程序来说,都有一个入口,以便启动应用程序。Java应用程序的入口是类的main方法,在这里你可以初始化应用的上下文环境,然后创建应用组件并提供服务。对于简单的应用程序,可以直接将启动代码放在main方法中,但是,对于复杂的,或者可扩展的应用来说,这样做是不负责任的,也是不优雅的。那么,怎么做才是负责任的,优雅的?下面我们看看Tomcat是如何启动的,分析一下它的启动框架。希望从中能找到答案。

2.Tomcat启动框架

默认的情况下,在命令行下启动Tomcat,程序的入口是org.apache.catalina.startup.Bootstrap类的main方法,Bootstrap是一个final类,不允许扩展。下面看看main方法,

publicstaticvoid main(String args[]) {

        if (daemon ==null) {

            daemon =new Bootstrap();

            try {

                daemon.init();

            } catch (Throwable t) {

                t.printStackTrace();

                return;

            }

        }

 

        try {

            String command = "start";

            if (args.length > 0) {

                command = args[args.length - 1];

            }

 

            if (command.equals("startd")) {

                args[0] = "start";

                daemon.load(args);

                daemon.start();

            } elseif (command.equals("stopd")) {

                args[0] = "stop";

                daemon.stop();

            } elseif (command.equals("start")) {

                daemon.setAwait(true);

                daemon.load(args);

                daemon.start();

            } elseif (command.equals("stop")) {

                daemon.stopServer(args);

            } else {

                log.warn("Bootstrap: command \"" + command +"\" does not exist.");

            }

        } catch (Throwable t) {

            t.printStackTrace();

        }

    }

由代码可知,main方法很简单,就做2件事,一.实例化和初始化Bootstrap并缓存起来,二.处理命令行参数,根据参数调用不同的操作,此之谓引导。看一下序列图更加清晰:


2.1引导过程

       一般来说,在使用一个对象之前,要先初始化好这个对象的状态(就是属性的值),而Bootstrap的初始化也无非干了这些事。

首先初始化属性commonLoader,catalinaLoader,sharedLoader的值,在初始化的过程中先设置了环境变量${catalina.home}和${catalina.base},然后读取配置文件,创建ClassLoader并赋值给属性字段。

接着使用catalinaLoader加载org.apache.catalina.startup.Catalina类并实例化一个对象,并将该对象设置到属性catalinaDaemon中。

当Bootstrap对象初始化完了,就可以接受命令并引导服务了。不同的命令对应不同的Bootstrap动作,而Bootstrap动作委托给Catalina对象来处理。

2.2Catalina

    我们知道,Tomcat是面向组件设计的典范,而且组件的装配是可配置的,那么在Tomcat实例启动之前,必须有一个地方来装配这些个组件。Catalina类正好担此重任。Catalina主要负责Tomcat的装配,并在Bootstrap的引导下操作Tomcat实例。

       当Bootstrap接到“start”命令后,会引导Catalina对象的load动作装配一个新的Server实例,接着Bootstrap引导Catalina对象启动Server实例。由此可以看出,Catalina是Tomcat实例的装配工厂和引线。

Tomcat6.0源码学习--架构概述 

Tomcat6是最新版本的web容器,其支持最新版本的servlet2.5jsp2.1。而且Tomcat6架构也是经过重新设计优化过的,所以我们有必要分析一下它的架构过程。显然,这是一个通过阅读Tomcat的源代码及相关文档,演绎架构的过程。或许有人会说,这不是放马后炮吗?!!但我觉得这是自我进步的一个必经步骤,先模仿之,然后才能超越之,毕竟我本凡人。(模仿并超越

Tomcat的架构总的来说是分层次的、可插拔的组件架构分层次是指构成Tomcat的组件不是同一级别的,上层组件可以包含子组件,各个组件有其功能范围,当一个组件停止服务时,不会影响上层组件的服务。可插拔是指对于组件的添加和删除并不影响服务器的运行。那么为了达到可插拔的组件架构,分层次的组件架构必成为基础。

对于任何服务器,即使最简单的实现,从面向对象设计(OOD)的角度来说,我们都有必要将“服务器”这个概念抽象出来,为什么呢?因为只有有了这个概念,才能谈服务器的实例,服务器的功能等等其它概念,此之谓“皮之不存,毛将焉附”。赶巧(其实是我的想法恰好撞上人家的想法),Tomcat也将“服务器”抽象为java接口org.apache.catalina.Server,显然Server应该就是最最顶层的组件了。

有了Server这个抽象,很自然的,我们希望它能够提供对servletjsp支持的功能。但是我们发现这个概念太大了,我们还需再细化。所以别急,我们还有一些事情要解决。服务器要提供服务就必须能够启动,当然也应该能够停止吧,也就是说服务器应该是有生命的,在启动时初始化必要的资源,而在停止时将其销毁掉。好吧,我们把这个也抽象出来,叫做生命周期接口,tomcat实现为org.apache.catalina.Lifecycle.如上所述我们知道Lifecycle需要完成的工作了。

publicvoidstart()throws LifecycleException;

publicvoid stop()throws LifecycleException;

接下来我们分析服务器如何来处理客户端的请求,一般的我们会在浏览器中输入如下格式的请求,http://192.168.8.221:8080/explorer/loginInit.do。对于服务器来说,要想处理这个请求,就必须监听指定的端口8080,当有TCP的请求包来时,建立Socket连接,分析并解析之,然后给客户端返回响应。在这个过程中,我们发现,其实包含了俩个功能点,即监听并接受请求处理请求。那么我们能否将这俩个功能给抽象出来呢?Tomcat告诉我们,可以。是的,Tomcat将“监听并接收请求”抽象为org.apache.catalina.connector.Connector类,负责接受请求;将“处理请求”抽象为“容器” org.apache.catalina.Container,负责处理Connector传递过来的请求。

Ok,到此,我们分析构建的简单服务器模型出来了,ServerConnector组件和Container组件结合提供web服务。

2

有了这个模型后,要实现一个简单的Server已经很简单了,但是在实现Container时,我们还是要做很多事情,如当来请求,我们怎么知道该请求对应得虚拟主机,以及请求的那个应用,应该交给那个servlet对象来处理?这样看来,Container还是太大了,需要细化。根据Servlet规范,我们知道,servlet属于某个应用,且有上下文环境,Container要根据应用上下文环境初始化servlet,然后根据servlet映射调用servletservice方法。在这里“应用上下文环境”的概念很重要,Tomcat将其抽象为org.apache.catalina.ContextContext继承了Container接口。对于虚拟主机,Tomcat将其抽象为org.apache.catalina.HostHost继承了Container接口。

好了,有了这些概念,我们再回顾一下请求的处理过程:浏览器发出请求,Connector接受请求,将请求交由Container处理,Container查找请求对应的Host并将请求传递给它,Host拿到请求后查找相应的应用上下文环境Context,准备servlet环境并调用service方法。

现在,我们的服务器模型变成了如图3所示了。

3

但是在Tomcat的实现体系中还有一个Engine的接口,Engine也继承了Container接口,那么这个接口什么用呢?设计Engine的目的有俩个,一,当希望使用拦截器查看(过滤预处理)每个请求时,Engine是个很好的拦截点。二,当希望多个虚拟Host共享一个HttpConnector时,Engine是个很好的门面。所以,Engine接口是作为顶级Container组件来设计的,其作用相当于一个Container的门面。有了Engine,请求的处理过程变为:浏览器发出请求,Connector接受请求,将请求交由Container(这里是Engine)处理,ContainerEngine来担当)查找请求对应的Host并将请求传递给它,Host拿到请求后查找相应的应用上下文环境Context,准备servlet环境并调用service方法。再看看服务器的模型,如图4.

4

到目前,我们碰到的组件类型有ConnectorContainer,其实,这也就是Tomcat的核心组件。如图4,一组Connector和一个Container有机的组合在一起构成Server,就可以提供服务了,对于Tomcat来说,主要是提供Servlet服务,那么也就是说Tomcat服务器也可以提供其它服务了?是的,Tomcat将“一组Connector和一个Container有机的组合”抽象为“服务”接口org.apache.catalina.Service,然而,这些服务实例彼此独立,仅仅共享JVM的基础设施,如系统类路径。

进一步的,我们得到了服务器的框架模型,如图5.

5

    由图5,我们知道,对于Tomcat服务器来说,除了Server代表它自己以外,其它组件都是功能组件,都有其职责范围。Service为最顶层的组件,可以添加ConnectorContainer组件。EngineContainer的最顶层组件,可以添加Host组件,但不能添加父组件。Host组件的父组件是EngineHost下面包含有Context组件。

    接下来看看标准的Tomcat体系结构,如图6.

6

比较图 5 和图 6. 我们发现,还有很多辅助组件没有抽象出来。当然,随着需求的一步步加深,我的分析也会一步步深入,这些个组件也会慢慢浮出水面。

Tomcat6.0源码学习--Connector架构 

概述

Tomcat虽然具有传统Web服务器(如Apache)的功能(处理Html页面,css文件等静态资源的能力),但更多的,TomcatServlet容器著称(处理JspServlet等动态资源的应用服务器)。由Tomcat的总体架构可知(参见:Tomcat6.x架构概述),Servlet容器由2个主要组件构成:Connector(连接器)和Container(容器)。Connector负责接收客户端的请求,而Container处理并响应该请求。

       JavaEE规范可知,Servlet容器内部只处理HTTP协议的请求,但是对于连接器的设计来说,它可以接收任何协议(如HTTPAJP等)的请求,因此,在连接器接收到客户端的请求(Socket)后,需要将该请求包装成容器可以处理的对象,然后再传递给容器处理,同时,连接器也要创建一个响应对象一并传给容器,好让容器响应请求。如此一来,容器就与具体传输协议解耦了,而这正是Connector架构所要达到的目的。

Connector架构

基于上面的分析,我们可以将连接器和容器分别抽象为ConnectorContainer接口,将请求和响应抽象为RequestResponse接口。

Connector接口中的重要方法有:

public Request createRequest() ;

public Response createResponse();

public Container getContainer();

 

Tomcat6.x的设计

下面我们看看Tomcat6.x是如何设计的。我们知道,TomcatServlet容器有个好听的名字叫Catalina,在其内部,Catalina容器抽象为org.apache.catalina.Container接口,将连接器抽象为org.apache.catalina.connector.Connector类,请求响应抽象为org.apache.catalina.connector.Requestorg.apache.catalina.connector.Response

org.apache.catalina.connector.Connector类也有以下方法:

public Request createRequest() ;

public Response createResponse();

public Container getContainer();

 

Connector接收到客户端的Socket请求,调用createRequest方法创建请求对象(org.apache.catalina.connector.Request),调用createResponse方法创建响应对象(org.apache.catalina.connector.Response),然后调用getContainer方法获得容器,最后传递Request对象和Response对象并调用Containerinvoke方法处理请求。

 

Connector支持的协议

对于Apache(或其它Web服务器)来说,它可以加载用C语言写的Apache模块(mod_jk , mod_jk2 ,mod_proxy)作为连接器(Connector),这些连接器(Connector)的工作原理一致,可以根据配置支持不同协议的连接器。对于Tomcat来说,其Servlet容器也有用java语言实现的连接器(Connector)模块,这些模块的工作原理也应该一致并且可以根据配置支持不同的协议。为了实现连接器处理工作的一致性和协议的可配置、可扩展性,在Tomcat6.x中,将一致性的工作抽象到org.apache.catalina.connector.Connector类,而将具体协议的差异抽象到org.apache.coyote.ProtocolHandler接口之中。Tomcat6.x通过在配置文件中设置不同的协议类型来初始化不同的ProtocolHandler实例,从而实现协议的可配置、可扩展性

Tomcat6.x中,默认支持2种协议类型的连接器:HTTP/HTTPS协议的HTTP/1.1AJP协议的AJP/1.3Tomcat6.x使用Coyote模块来实现Connector框架,因此这些协议处理器(ProtocolHandler)在包org.apache.coyote下可以找到。

 

CoyoteConnector框架实现的名字,在该模块下提供了2种协议类型的ProtocolHandler

 

如果想扩展连接器,可以在server.xml文件中指定协议和协议处理器的类名。如下:

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

 

然而,HTTP协议和AJP协议几乎符合所有的需求,因此,我们很少会扩展连接器。

在构造连接器时,连接器根据是否支持Apache Portable Runtime (APR),选择不同的类支持HTTP协议和AJP协议,其对应关系如下:

l        支持APR

HTTP/1.1协议对应org.apache.coyote.http11.Http11AprProtocol

AJP/1.3协议对应org.apache.coyote.ajp.AjpAprProtocol

l        不支持APR

HTTP/1.1协议对应org.apache.coyote.http11.Http11Protocol

AJP/1.3协议对应org.apache.jk.server.JkCoyoteHandler

 

注:APR(Apache portable Run-time librariesApache可移植运行库)的目的如其名称一样,主要为上层的应用程序提供一个可以跨越多操作系统平台使用的底层支持接口库。在早期Apache版本中,应用程序本身必须能够处理各种具体操作系统平台的细节,并针对不同的平台调用不同的处理函数。启用APR将大大提升Tomcat对静态文件的处理性能。

 

下面看看如何构建不同的连接器:

public Connector(String protocol)throws Exception {

             setProtocol(protocol);

             // Instantiate protocol handler

             try {

                    Class clazz = Class.forName(protocolHandlerClassName);

                    this.protocolHandler = (ProtocolHandler) clazz.newInstance();

             }catch (Exception e) {

                    log.error(sm.getString(

                                  "coyoteConnector.protocolHandlerInstantiationFailed", e));

             }

}

 

publicvoid setProtocol(String protocol) {

             // Test APR support

             initializeAPR();

 

             if (aprInitialized) {

                    if ("HTTP/1.1".equals(protocol)) {

                           setProtocolHandlerClassName("org.apache.coyote.http11.Http11AprProtocol");

                    }elseif ("AJP/1.3".equals(protocol)) {

                           setProtocolHandlerClassName("org.apache.coyote.ajp.AjpAprProtocol");

                    }elseif (protocol !=null) {

                           setProtocolHandlerClassName(protocol);

                    }else {

                           setProtocolHandlerClassName("org.apache.coyote.http11.Http11AprProtocol");

                    }

             }else {

                    if ("HTTP/1.1".equals(protocol)) {

                           setProtocolHandlerClassName("org.apache.coyote.http11.Http11Protocol");

                    }elseif ("AJP/1.3".equals(protocol)) {

                           setProtocolHandlerClassName("org.apache.jk.server.JkCoyoteHandler");

                    }elseif (protocol !=null) {

                           setProtocolHandlerClassName(protocol);

                    }

             }

 

}

 

Connector构造函数接收一字符串参数protocol,并根据该字符串加载不同的ProtocolHandler

 

ProtocolHandler

每个ProtocolHandler代表着一种协议的支持,如Tomcat6.x默认支持的协议http1.1ajp,相应的处理器为:Http11AprProtocolAjpAprProtocolHttp11ProtocolAjpProtocolProtocolHandler的职责是,接收客户端Socket请求,处理Socket请求,将请求包装之后传给容器来处理。

   Coyote架构下,每个ProtocolHandler使用相应的端点类XXXEndpoint(如org.apache.tomcat.util.net.AprEndpoint, org.apache.tomcat.util.net.NioEndpoint,org.apache.tomcat.util.net.JIoEndpoint)监听指定端口并接收Socket请求。在请求传给容器之前,我们需要对请求做一些处理(如是否超时,是否保持连接等),而这些工作都是交给具体协议处理器(ProtocolHandler)相关类来实现,最后由Adapter包装请求并调用容器的invoke方法。

ProtocolHandler相关类

   为了实现协议处理器的相关功能,Coyote按功能职责抽象了协议处理器的相关类:

l        XXXEndpoint,监听指定端口,接收Socket请求

l        Handler,管理具体协议的Socket请求连接

l        Processor,真正的具体协议处理器

l        Adapter,包装请求,并调用容器的invoke方法(准确的说是Pipeline的第一个Valve

 

ProtocolHandler相关类的配合下,其处理请求的过程如下:

根据支持的协议,通常ProtocolHandler包含一个实现相应协议的Handler实例和XXXEndpoint实例Handler实例接收XXXEndpoint获得的Socket对象,然后创建相应协议的Processor实例,并将Socket请求交给它来处理,最后Processor调用Adapterservice方法,该方法把请求包装后调用容器的invoke方法。

Adapterservice方法签名如下:

publicvoid service(org.apache.coyote.Request req,

                      org.apache.coyote.Response res)

 

通过以上的分析,我们发现,Adapter是所有请求进入容器的一个阀。在Tomcat的设计中,阀的使用无处不在。

 

示例分析

   下面具体分析一下Tomcat6.x是如何支持HTTP/1.1协议的连接器。创建测试代码如下。

代码片段1,创建支持HTTP/1.1的连接器并启动:

   publicstaticvoid main(String[] args) {

      try {

          Connector connector =new Connector("HTTP/1.1");

          connector.setPort(8080);

          connector.setContainer(new SimpleContainer());

 

          connector.start();

          Thread.sleep(3000000);

      }catch (Exception e) {

          e.printStackTrace();

      }

   }

代码片段2SimpleContainer的实现,省略了部分没有实现的代码:

publicclass SimpleContainerimplements Container {

 

   private Containercontainer;

 

   publicSimpleContainer() {

      this.container = this;

   }

 

   public SimpleContainer(Container container) {

      this.container = container;

   }

 

   @Override

   public Pipeline getPipeline() {

      //TODO Auto-generated method stub

      returnnew Pipeline() {

 

          @Override

          public Valve getFirst() {

             returnnew Valve() {

                 @Override

                 publicvoid invoke(Request request, Response response)

                        throws IOException, ServletException {

                    container.invoke(request, response);

                 }

             };

          }

 

 

   @Override

   publicvoid invoke(Request request, Response response)throws IOException, ServletException {

 

      response.setContentType("text/html;charset=gbk");

 

      PrintWriter w = response.getWriter();

      Class c =request.getConnector().

getProtocolHandler().getClass();

      w.print("当前连接器支持的协议为:" +

request.getConnector().getProtocol());

      w.print("<br>");

      w.print("当前连接器的类型为:" + c.getName());

      w.print("<br>");

      w.print("容器方法[invoke]被调用");

   }

}

 

执行该程序,在地址栏输入:http://localhost:8080/index.html,你将看到如下画面:

 

 

Connector接收请求的过程如下:


Accepter线程接到客户端的Socket请求后,调用JIoEndpointprocessSocket方法分发请求给worker线程来处理。

 

Connector处理请求的过程如下:


worker线程接收到Socket请求后,将请求委托给Http11ConnectionHandler处理。Http11ConnectionHandler创建Http11Processor并调用其process方法,该方法解析协议内容并包装请求信息为org.apache.coyote.Requestorg.apache.coyote.Response之后调用CoyoteAdapterservice方法。

 

Connector传递请求的过程如下:


CoyoteAdapter service方法内部,将org.apache.coyote.Requestorg.apache.coyote.Response 转换为 org.apache.catalina.connector.Request org.apache.catalina.connector.Response。然后传递请求给容器。


Tomcat6.0源码学习--接受并传递请求

1.接受请求

Tomcat启动时,首先启动Container,然后启动Connector。而在Connector启动时,会启动协议处理器和映射监听器。协议处理器用来处理具体的协议的,对于http/1.1,其处理器为Http11Protocol,而AJP则为AjpProtocol。当然在协议处理器启动时,会启动监听线程来监听指定端口,从而接受请求。

对于http/1.1协议的处理器Http11Protocol在启动时会创建一个线程对象Acceptor并启动它[http-8080-Acceptor-0],默认监听8080端口的Http请求。当接收到http请求,立即将Socket对象转交给处理线程Work对象[http-8080-1]并返回,准备接受下一个http请求。在如图:

 

2.解析请求

       在交由Container处理请求之前,需要先解析一下请求,然后将请求包装一下再往下传。如果不加处理的将Socket传给Container处理,估计下面的人会疯掉的。如图为解析过程


Work线程将接受到得Socket传给协议处理器的链接处理器来处理,链接处理器创建一个协议处理单元(Http11Processor)来处理请求,并把Socket解析包装成org.apache.coyote.Request,然后交由CoyoteAdapter来处理。CoyoteAdapter将org.apache.coyote.Request包装成org.apache.catalina.connector.Request之后,请求将org.apache.catalina.connector.Request交由Container来处理。

2.1传递请求

那么请求是如何被传递的呢?如下图


 

Tomcat类加载机制 

Tomcat中的类加载策略和JDK中的委托模型略有不同。Tomcat Server在启动的时候将构造一个ClassLoader树,以保证模块的类库是私有的,ClassLoader结构如下:
             Bootstrap 1)
                   |
              System 2)
                  |
            Common 3)
          /                \
 Catalina 4)        Shared 5)
                      /  ... ...     \
               webapp1         webappN  6)


各个类加载器的作用描述如下:
1)Bootstrap ClassLoader: 负责加载由虚拟机提供的基本运行时类和系统扩展目录($JAVA_HOME/jre/lib/ext)下的JAR包;


2)System ClassLoader: 通常这个加载器用来加载CLASSPATH环境变量中指定的类,但在Tomcat5的标准启动脚本(

$CATALINA_HOME/bin/catalina.sh或%CATALINA_HOME%/bin/catalina.bat)中改变了它的行为,它只加载下面的类:
        $CATALINA_HOME/bin/bootstrap.jar    // Contains the main() method that is used to initialize the Tomcat 5 server,

      and the class loader implementation classes it depends on.
      $JAVA_HOME/lib/tools.jar // Contains the "javac" compiler used to convert JSP pages into servlet classes.

      $CATALINA_HOME/bin/commons-logging-api.jar // Jakarta commons logging API.
      $CATALINA_HOME/bin/commons-daemon.jar // Jakarta commons daemon API.

       jmx.jar // The JMX 1.2 implementation.

3)Common ClassLoader:它负责加载Tomcat本身和所有的web应用都能看到的类。通常,应用的类不应该由他加载。

$CATALINA_HOME/common/classes,

$CATALINA_HOME/commons/endorsed和

$CATALINA_HOME/common/lib下的类都由这个加载器加载。缺省的,

包括: 
      ant.jar - Apache Ant.
      commons-collection.jar  // Jakarta commons collection.
     commons-dbcp.jar // Jakarta commons DBCP, providing a JDBC connection pool to web applications.
     commons-el.jar // Jakarta commons el, implementing the expression language used by Jasper.
      commons-pool.jar // Jakarta commons pool.

     jasper-compiler.jar // The JSP 2.0 compiler.
     jasper-runtime.jar // The JSP 2.0 runtime.
      jsp-api.jar // The JSP 2.0 API.
      naming-common.jar // The JNDI implementation used by Tomcat 5 to represent in-memory naming contexts.
      naming-factory.jar // The JNDI implementation used by Tomcat 5 to resolve references to enterprise resources (EJB, connection pools).
      naming-resources.jar // The specialized JNDI naming context implementation used to represent the static resources of a web application.
      servlet-api.jar // The Servlet and JSP API classes.
      xerces.jar // The XML parser that is visible by default to Tomcat internal classes and to web applications.
      xml-apis.jar

4)Catalina ClassLoader: 用来加载实现Tomcat自己需要的类。由它加载的类对web应用都是不可见的。

$CATALINA_HOME/server/classes,

$CATALINA_HOME/server/lib,都由这个加载器加载。缺省的,包括:

catalina.jar - Implementation of the Catalina servlet container portion of Tomcat 5.

jakarta-regexp-X.Y.jar - The binary distribution of the Jakarta Regexp regular expression processing library, used in the implementation of request filters.

servlets-xxxxx.jar - The classes associated with each internal servlet that provides part of Tomcat's functionality. These are separated so that

         they can be completely removed if the corresponding service is not required, or they can be subject to specialized security manager permissions.

tomcat-coyote.jar - Coyote connector for Tomcat 5.

tomcat-http11.jar - Standalone Java HTTP/1.1 connector.

tomcat-jk2.jar - Classes for the Java portion of the JK 2 web server connector, which allows Tomcat to run behind web servers

         such as Apache and iPlanet iAS and iWS.

tomcat-util.jar - Utility classes required by some Tomcat connectors.


5)Shared ClassLoader:被所有的web应用共享的类和资源由这个加载器加载。$CATALINA_BASE/shared/classed,$CATALINA_BASE/shared/lib,都由这个加载器加载。


6)WebappX ClassLoader:对每个Tomcat里的web应用都创建一个加载器,web应用下的WEB-INF/classes,WEB-INF/lib,都由这个加载器加载,由它所加载的类对其他的web应用是不可见的。
web应用的加载器(WebappX)和JDK的委托模型略有不同,这是根据Servlet2.3规范做出的。当WebappX被请求加载一个类时,它首先尝试自己加载,而不是先委托给它的父加载器加载,如果无法加载该类,则委托它的父加载器加载。但是,对于下面的类,仍然要先委托给父加载器:

Classes which are part of the JRE base classes cannot be overriden. For some classes (such as the XML parser components in JDK 1.4+), the JDK 1.4 endorsed feature can be used (see the common classloader definition above). In addition, for the following class patterns, the classloader will always delegate first (and load the class itself if no parent classloader loads it):
     javax.*
    org.xml.sax.*
    org.w3c.dom.*
    org.apache.xerces.*
    org.apache.xalan.*
    Last, any JAR containing servlet API classes will be ignored by the classloader.

Tomcat中其他的加载器都是遵循委托模型的。

最后,以web应用的角度,要加载类或者资源时,会以下面的顺序查找:
      Bootstrap classes of your JVM
      System class loader classses (described above)
          /WEB-INF/classes of your web application
          /WEB-INF/lib/*.jar of your web application
          $CATALINA_HOME/common/classes
          $CATALINA_HOME/common/endorsed/*.jar
          $CATALINA_HOME/common/lib/*.jar
          $CATALINA_BASE/shared/classes
          $CATALINA_BASE/shared/lib/*.jar

结论

对于只用于某一个web应用的类或资源,放在这个web应用下的/WEB-INF/classes目录下,如果是JAR,就放在这个web应用下的WEB-INF/lib目录下。
        对于让所有的web应用共享的类或资源,放在$CATALINA_BASE/shared/classes目录下,如果是JAR,就放在$CATALINA_BASE/shared/lib目录下。


http://www.cnblogs.com/ajian005/archive/2012/09/27/2753667.html


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值