Tomcat7源码解析

来源:互联网 发布:javascript自学心得 编辑:程序博客网 时间:2024/05/20 05:56

Tomcat7 源码阅读学习
    背景
    Tomcat源码目录结构
    Tomcat体系结构
    Tomcat源码解析
          Tomcat的启动流程
          Tomcat一次完整请求的处理流程

          Tomcat的关闭流程

          Tomcat的Connector组件

          Tomcat的运行过程中的线程概况及线程模型

          Tomcat的类加载机制

          Tomcat所涉及的设计模式

   参考资源


一、背景

        Tomcat作为JavaWeb领域的Web容器,目前在我们淘宝也使用的也非常广泛,现在基本上所有线上业务系统都是部署在Tomcat上。为了对平时开发的Web系统有更深入的理解以及出于好奇心对我们写的Web系统是如何跑在Tomcat上的,于是仔细研究了下Tomcat的源码。大家都知道Servlet规范是Java领域中为服务端编程制定的规范,对于我们开发者只是关注了Servlet规范中提供的编程组件(ServletContextListener,Filer,Servlet) 等 ,但是规范中还有一些我们经常使用的接口(ServletContext,ServletRequest,ServletResponse,FilterChain)等都是由Tomcat去实现的,并且我们开发者实现的编程组件只是被Tomcat去回调而已。所以看Tomcat源码实现也有助于我们更好的理解Servlet规范及系统如何在容器中运行(一些开源的MVC框架如Struts2,Webx,SpringMVC本质无非就是这个),顺便整理了一下与大家分享(Tomcat版本7.0.23,JDK版本1.6)。  

 

二、Tomcat源码目录结构

Tomcat7源码解析0
Tomcat7源码解析1

三、Tomcat体系结构

        仔细查看下图(网络上描述Tomcat架构比较清晰的一张图),不难发现其中的Connecotr组件以及与Container组件是Tomcat的核心。一个Server可以有多个Service,而一个Service可以包含了多个Connector组件和一个Engine容器组件,一个Engine可以由多个虚拟主机Host组成,每一个Host下面又可以由多个Web应用Context构成,每一个的Context下面可以包含多个Wrapper(Servlet的包装器)组成。

        Tomcat将Engine,Host,Context,Wrapper统一抽象成Container。一个抽象的Container模块可以包含各种服务。例如,Manager管理器(Session管理),Pipeline管道( 维护管道阀门Value )等。Lifecycle接口统一定义了容器的生命周期,通过事件机制实现各个容器间的内部通讯。而容器的核心接口Container的抽象实现中定义了一个Pipeline,一个Manager,一个Realm以及ClassLoader统一了具体容器的实现规范。连接器(Connector)组件的主要任务是为其所接收到的每一个请求(可以是HTTP协议,也可以AJP协议),委托给具体相关协议的解析类ProtocolHandler,构造出Request 对象和Response 对象。然后将这两个对象传送给容器(Container)进行处理。容器(Container)组件收到来自连接器(Connector)的Request 和Response对象后,负责调用Filter,最后调用Servlet的service 方法(进入我们开发的Web系统中)。

Tomcat7源码解析2



四、Tomcat源码解析

  Servlet规范由一组用 Java编程语言编写的类和接口组成。Servlet规范为服务端开发人员提供了一个标准的 API以及为服务器厂商制定了相关实现规范,开发人员只需要关心Servlet规范中的编程组件(如Filter,Servlet等),其他规范接口由第三方服务器厂商(如Tomcat)去实现,三者的关系如下图,Servlet规范之于Tomcat的关系,也类似于JDBC规范与数据库驱动的关系,本质就是一套接口和一套实现的关系。对于一个Web服务器主要需要做的事情,个人认为基本由以下组件组成: [TCP连接管理] --> [请求处理线程池管理] --> [HTTP协议解析封装] --> [Servlet规范的实现,对编程组件的回调] --> [MVC框架,Web系统业务逻辑]

       Tomcat的源码及功能点因为实在过于庞大,下面的源码解析不可能全部功能模块都涉及到,接下来我们主要会根据Tomcat的启动、请求处理、关闭三个流程来梳理Tomcat的具体实现,尽可能把各个模块的核心组件都展示出来。 基本上每个流程的源码分析我都画了时序图,如果不想看文字的话,可以对着时序图看(如果想看完整的源代码分析也会在附件中上传),最后还会分析下Tomcat的Connector组件及Tomcat运行过程中的线程概况及线程模型及Tomcat的类加载机制及Tomcat所涉及的设计模式。

                      Servlet规范和Web应用及Web容器间的关系 

Tomcat7源码解析3

                                                          Tomcat对Servlet规范的实现

 Tomcat7源码解析4

1. Tomcat的启动流程         

Tomcat在启动时的重点功能如下:

·        初始化类加载器:主要初始化Tomcat加载自身类库的StandardClassLoader。

·        解析配置文件:使用Digester组件解析Tomcat的server.xml,初始化各个组件(包含各个web应用,解析对应的web.xml进行初始化)。

·        初始化Tomcat的各级容器Container,当然最后会初始我们Web应用(我们熟悉的Listener,Filter,Servlet等初始化等在这里完成)。

·        初始化连接器Connector:初始化配置的Connector,以指定的协议打开端口,等待请求。

不管是是通过脚本bootstrap.sh启动还是通过Eclipse中启动,Tomcat的启动流程是在org.apache.catalina.startup.Bootstrap类的main方法中开始的,启动时这个类的核心代码如下所示:

Java代码 

1.  public static void main(String args[]) {  

2.      if (daemon == null) {  

3.           daemon = new Bootstrap(); //实例化Bootstrap的一个实例  

4.           try {

5.               // 创建StandardClassLoader类加载器,并设置为main线程的线程上下文类加载器

6.               // 通过StandardClassLoader加载类Cataline并创建Catalina对象 ,并保存到Boosstrap对象中

7.               daemon.init();

8.           } catch (Throwable t) {  

9.          }  

10.     }  

11.     if (command.equals("start")) {  

12.          daemon.setAwait(true);  

13.           daemon.load(args); //执行load,加载资源,调用Catalina的load方法,利用Digester读取及解析server.xml配置文件并且创建StandardServer服务器对象 */

14.           daemon.start(); //启动各个组件,容器开始启动,调用Catalina的start方法

15.     }  

16. }  

从以上的代码中,可以看到在Tomcat启动的时候,执行了三个关键方法即init、load、和start。后面的两个方法都是通过反射调用org.apache.catalina.startup.Catalina的同名方法完成的,所以后面在介绍时将会直接转到Catalina的同名方法。首先分析一下Bootstrap的init方法,在该方法中将会初始化一些全局的系统属性、初始化类加载器、通过反射得到Catalina实例,在这里我们重点看一下初始化类加载器的initClassLoaders()方法

Java代码 

1.  private void initClassLoaders() {  

2.          try {

3.             //创建StandardClassLoader类加载器  

4.              commonLoader = createClassLoader("common", null);  

5.              if( commonLoader == null ) {  

6.                  commonLoader=this.getClass().getClassLoader();  

7.              }  

8.              catalinaLoader = createClassLoader("server", commonLoader);  

9.              sharedLoader = createClassLoader("shared", commonLoader);  

10.         } catch (Throwable t) {  

11.             System.exit(1);  

12.         }  

13. }  

在以上的代码总,我们可以看到初始化了StandardClassLoader类加载器,这个类加载器详细的会在后面关于Tomcat类加载器机制中介绍。

然后我们进入Catalina的load方法:

Java代码 

1.  public void load() {  

2.          //初始化Digester组件,定义了解析规则  

3.          Digester digester = createStartDigester();  

4.          try {  

5.              inputSource.setByteStream(inputStream);  

6.              digester.push(this);  

7.              //通过Digester解析这个文件,在此过程中会初始化各个组件实例及其依赖关系

8.             //最后会把server.xml文件中的内容解析到StandardServer中

9.              digester.parse(inputSource);  

10.             inputStream.close();  

11.         } catch (Exception e) {}  

12.         if (getServer() instanceof Lifecycle) {  

13.             try {

14.                // 调用Server的initialize方法,初始化各个组件  

15.                 getServer().init();  

16.             } catch (LifecycleException e) {  

17.             }  

18.         }  

19. }  

在以上的代码中,关键的任务有两项即使用Digester组件按照给定的规则解析server.xml、调用Server的init方法,而Server的init方法中,会发布事件并调用各个Service的init方法,从而级联完成各个组件的初始化。每个组件的初始化都是比较有意思的,但是我们限于篇幅先关注Tomcat各级容器的初始化及Connector的初始化,这可能是最值得关注的地方。

首先看下StandardService的start方法(), 核心代码如下:

1.  // StandardService的启动  

2.  protected void startInternal() throws LifecycleException {

4.      //启动容器StandardEngine 

5.      if (container != null) {  

6.          synchronized (container) {  

7.              // 启动StandardEngine容器  

8.              container.start();  

9.          }  

10.     }  

12.     //两个connector的启动,HTTP/1.18080和AJP/1.38009  

13.     synchronized (connectors) {  

14.         for (Connector connector: connectors) {  

15.             try {  

16.                 if (connector.getState() != LifecycleState.FAILED) {  

17.                     //依次启动Connector[HTTP/1.1-8080]或Connector[AJP/1.3-8009]

18.                     //这里会创建请求线程执行器并启动接收线程Acceptor

19.                     connector.start();  

20.                 }  

21.             } catch (Exception e) {  

22.             }  

23.         }  

24.     }  

25. }  

启动Tomcat各级容器的会依次先启动StandardEngine --> StandardHost --> StandardContext(代表一个WebApp应用), 因为我们比较关心我们的Web应用是哪里被初始化回调的,所以就重点看下StandardContext的start()方法,核心代码如下:

Java代码 

1.  //启动WebApp应用 

2.  @Override  

3.  protected synchronized void startInternal() throws LifecycleException {  

4.      boolean ok = true;  

6.      if (getLoader() == null) {  

7.          //创建此WebApp应用的WebApp载入器WebAppLoader,这个应用的WebAppClassLoader类加载器全部是由这个载入器创建

8.          WebappLoader webappLoader = new WebappLoader(getParentClassLoader());  

9.          webappLoader.setDelegate(getDelegate());  

10.         //在这里开始启动WebAppLoader加载器,并且创建此WebApp的WebAppClassLoader类加载器,保存到WebAppLoader中,最后会被设置到此Context的InstanceManager中

11.         setLoader(webappLoader);  

12.     }  

13.   

14.     ///线程上下文类加载器切换成当前WebApp的类加载器,从Context的loader中获取 

15.     ClassLoader oldCCL = bindThread();  

17.     try {  

18.         if (ok) {  

19.             //触发Context组件的configure_start事件,通知ContextConfig监听器

20.             //开始解析Web应用WEB-INF/web.xml文件配置到WebXml对象,最后把配置信息全部解析到StandardContext

21.             fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);  

23.             if (pipeline instanceof Lifecycle) {  

24.                 //启动StandardContext的管道Pipeline

25.                 ((Lifecycle) pipeline).start();  

26.             }  

27.         }  

28.     } finally {  

29.         //线程上下文类加载器切换成之前的StandardClassLoader

30.         unbindThread(oldCCL);  

31.     }  

32.   

33.     //线程上下文类加载器切换成当前WebApp的类加载器,从Context的loader中获取

34.     oldCCL = bindThread();  

35.   

36.     if (ok ) {  

37.         if (getInstanceManager() == null) {  

38.             Map<String, Map<String, String>> injectionMap = buildInjectionMap(  

39.                     getIgnoreAnnotations() ? new NamingResources(): getNamingResources());  

40.             //创建每个应用StandardContext自己的DefaultInstanceManager实例管理器对象

41.             //并且会从WebAppLoader拿到WebAppClassLoader类加载器,之后此WebApp的应用类都由此ClassLoader加载

42.             setInstanceManager(new DefaultInstanceManager(context,  

43.                     injectionMap, this, this.getClass().getClassLoader()));  

44.         }  

45.     }  

47.     try {  

48.         //把StandardContext的上下文参数设置到ServletContext中

49.         mergeParameters();  

51.         if (ok) {  

52.             //启动监听器Listener

53.             if (!listenerStart()) {  

54.                 log.error( "Error listenerStart");  

55.                 ok = false;  

56.             }  

57.         }  

59.         try {  

60.             if ((manager != null) && (manager instanceof Lifecycle)) {  

61.                 //启动Session管理器StandardManager

62.                 ((Lifecycle) getManager()).start();  

63.             }  

64.             super.threadStart();  

65.         } catch(Exception e) {  

66.             ok = false;  

67.         }  

69.         if (ok) {  

70.             //启动过滤器Filter

71.             if (!filterStart()) {  

72.                 log.error("Error filterStart");  

73.                 ok = false;  

74.             }  

75.         }  

77.         if (ok) {  

78.             //启动load-on-startup的Servlet

79.             loadOnStartup(findChildren());  

80.         }  

81.   

82.     } finally {  

83.         // WepApp应用启动完成,线程上下文类加载器切换成之前的StandardClassLoader

84.         unbindThread(oldCCL);  

85.     }  

86. }  

Tomcat的各级容器初始化完成后,就开始对Connector的初始化,接着看Connector的initInternal方法,核心代码如下:

Java代码 

1.  public void initInternal() throws LifecycleException{  

2.          //该协议适配器会完成请求的真正处理     

3.          adapter = new CoyoteAdapter(this);  

4.          //对于不同的实现,会有不同的ProtocolHandler实现类, Http11Protocol用来处理HTTP请求  

5.          protocolHandler.setAdapter(adapter);

6.          try {  

7.             // 初始化Http11Protocol协议

8.              protocolHandler.init();  

9.          } catch (Exception e) {  

10.         }  

11. }  

Http11Protocol的init方法中,核心代码如下:

Java代码 

1.  public void init() throws Exception {  

2.          endpoint.setName(getName());

3.          endpoint.setHandler(cHandler);  

4.          try {  

5.              endpoint.init(); //核心代码就是调用JIoEndpoint的初始化方法  

6.          } catch (Exception ex) {  

7.          }  

8.  }  

我们看到最终的初始化方法最终都会调到JIoEndpoint的bind方法,网络初始化和对请求的最初处理都是通过该类及其内部类完成的,后续的内容会详细阐述这个JioEndpoint:

Java代码 

1.  public void bind() throws Exception {

2.          //请求接收线程数,默认为1     

3.          if (acceptorThreadCount == 0) { 

4.              acceptorThreadCount = 1;  

5.          }  

6.          if (serverSocket == null) {  

7.              try {  

8.                  if (address == null) {  

9.                    //创建ServerSocket,绑定指定端口并打开该端口的服务,默认8080端口 

10.                   serverSocket = serverSocketFactory.createSocket(port, backlog);  

11.                 } else {  

12.                     serverSocket = serverSocketFactory.createSocket(port, backlog, address);  

13.                 }  

14.             } catch (BindException orig) {  

15.             }  

16.         }  

17.     }  

在上面的代码中,我们可以看到此时初始化了一个ServerSocket对象,用来监听绑定端口的请求。

紧接着我们看JioEndpoint的start()方法,核心代码如下:

Java代码 

1.  public void startInternal() throws Exception {  

2.      if (!running) {  

3.  

4.          if (getExecutor() == null) {  

5.              //创建请求处理线程池ThreadPoolExecutor, 请求接收线程启动前必须把请求处理线程池准备好

6.              createExecutor();  

7.          }  

8.    

9.          initializeConnectionLatch();  

10.   

11.         //创建并启动Acceptor接收线程

12.         startAcceptorThreads();  

13.     }  

14. }  

从以上的代码,可以看到,如果没有在server.xml中声明Executor的话,将会使用内部的一个容量为200的线程池用来后续的请求处理。并且按照参数acceptorThreadCount的设置,初始化线程来接受请求。而Acceptor就是正在接受请求并会分派给请求处理线程池:

Java代码 

1.  protected class Acceptor implements Runnable {  

2.          public void run() {  

3.              while (running) {  

4.                     // 默认监听8080端口请求,server.xml中的默认配置

5.                     Socket socket =serverSocketFactory.acceptSocket(serverSocket);

6.                      serverSocketFactory.initSocket(socket);  

7.                      // 处理Socket,把客户端请求Socket交给线程池来处理,当前Acceptor线程继续返回接收客户端Socket  

8.                      if (!processSocket(socket)) {  

9.                          //关闭连接  

10.                         try {  

11.                             socket.close();  

12.                         } catch (IOException e) {  

13.                         }  

14.                     }  

15.             }  

16.         }  

17. }  

从这里我们可以看到,Acceptor已经可以接收Socket请求了,并可以调用processSocket方法来对请求进行处理。至此,Tomcat的组件启动初始化完成,等待请求的到来。

Tomcat的启动流程具体序列图如下:

Tomcat7源码解析5


 2. Tomcat一次完整请求的处理流程

Tomcat一次完整请求处理的重点功能如下:

·        接收Socket请求,把请求转发到线程池,由线程池分配一个线程来处理。

·        获取Socket数据包之后,解析HTTP协议,翻译成Tomcat内部的Request和Response对象,再映射相应Container。

·        Request和Response对象进入Tomcat中的Container容器的各级Piepline管道去流式执行,最终会流到StandardWrapperValve这个阀门。

·        在 StandardWrapperValve这个阀门中,把当前请求的Filter及Servlet封装成FilterChain, 最终执行FilterChain, 回调Web应用,完成响应。

JIoEndpoint的Acceptor线程在接收到用户的请求之后,调用processSocket方法。该方法主要是从Executor请求处理线程池中获取一个线程,然后启用一个新线程执行Socket请求,JIoEndpoint的processSocket()方法的核心代码如下:

Java代码 

1.  protected boolean processSocket(Socket socket) {  

2.      try {  

3.          //把Socket包装成SocketWrapper

4.          SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);  

5.          //包装成SocketProcessor交给线程池处理,当前Acceptor线程不处理,以便接收下一个到达的请求

6.          getExecutor().execute(new SocketProcessor(wrapper));  

7.      } catch (RejectedExecutionException x) {  

8.          return false;  

9.      }

10.     return true;  

11. }  

接着请求处理线程池会起一个线程来处理请求来执行SocketProcessor,而SocketProcessor的run()方法中会调用Http11ConnectionHandler的process方法,SocketProcessor的run()方法核心代码如下:

Java代码 

1.   // Tomcat线程池中的请求处理线程

2.   public void run() {  

3.       boolean launch = false;  

4.       synchronized (socket) {  

5.           try {  

6.               SocketState state = SocketState.OPEN;  

7.               if ((state != SocketState.CLOSED)) {  

8.                   if (status == null) {  

9.                      //通过Http11Protocol$Http11ConnectionHandler处理请求,引用外部类对象的成员handler

10.                      state = handler.process(socket, SocketStatus.OPEN);  

11.                  } else {  

12.                      state = handler.process(socket, status);  

13.                  }  

14.              }  

15.              if (state == SocketState.CLOSED) {  

16.                  try {  

17.                     //关闭Socket  

18.                    socket.getSocket().close();  

19.                  } catch (IOException e) {  

20.                  }  

21.              }

22.          }

23.      }  

24.      //清空请求Socket  

25.      socket = null;  

26. }  

在Http11ConnectionHandler中会根据当前请求的协议类型去创建相应的协议处理器,我们这里分析的是HTTP协议,所以会创建Http11Processor去执行process()方法, 拿到Socket数据包后解析生成Tomcat内部的Request对象与Response对象。其中Request对象只是解析Header部分内容,请求参数等做延迟处理,接着就开始调用CoyoteAdapter类进入容器处理, Http11Processor的process方法核心代码如下:

Java代码 

1.  public SocketState process(SocketWrapper<S> socketWrapper)  

2.          throws IOException {  

3.          RequestInfo rp = request.getRequestProcessor();  

4.          //设置SocketWrapper  

5.          setSocketWrapper(socketWrapper);  

6.    

7.          //获取Socket中的inputStream设置到inputBuffer,也就是设置到Request中  

8.          getInputBuffer().init(socketWrapper, endpoint);  

9.          //获取Socket中的outputStream设置到outputBuffer,也就是设置到Response中

10.         getOutputBuffer().init(socketWrapper, endpoint);  

11. 

12.         while (!error && keepAlive && !comet && !isAsync() &&  

13.                 !endpoint.isPaused()) {  

14.             try {  

15.                 setRequestLineReadTimeout();  

16.   

17.                 //解析HTTP请求的method,requestURI,protocol等 

18.                 if (!getInputBuffer().parseRequestLine(keptAlive)) {  

19.                     if (handleIncompleteRequestLineRead()) {  

20.                         break;  

21.                     }  

22.                 }  

23.   

24.                 if (endpoint.isPaused()) {  

25.                     response.setStatus(503);  

26.                     error = true;  

27.                 } else {  

28.                     request.setStartTime(System.currentTimeMillis());  

29.                     keptAlive = true;  

30.   

31.                     //解析HTTP请求的报头headers  

32.                     if (!getInputBuffer().parseHeaders()) {  

33.                         openSocket = true;  

34.                         readComplete = false;  

35.                         break;  

36.                     }  

37.                 }  

38.             } catch (IOException e) {  

39.             } catch (Throwable t) {  

40.             }  

42.             if (!error) {  

43.                 try {  

44.                     //准备Request,根据已解析的信息做一些过滤 

45.                     prepareRequest();  

46.                 } catch (Throwable t) {  

47.                     ExceptionUtils.handleThrowable(t);  

48.                 }  

49.             }  

50.             if (!error) {  

51.                 try {  

52.                     //调用CoyoteAdapter的service方法,传入org.apache.coyote.Request对象及org.apache.coyote.Response对象

53.                     adapter.service(request, response);  

54.                 } catch (InterruptedIOException e) {  

55.                     error = true;  

56.                 } 

57.            } 

58.        } 

59. }  

紧接着CoyoteAdapter会调用Container容器的Pipeline管道一步一步的对Request,Response对象处理。Tomcat容器主要由4层组成,通过管道的模式将各个层的功能进行了独立,也有利于对各层插入需要的功能。如果需要,只要在server.xml中加入Value元素的配置即可完成扩展功能, CoyoteAdapter的service方法核心代码如下:

Java代码 

1.  public void service(org.apache.coyote.Request req,  

2.                      org.apache.coyote.Response res)  

3.      throws Exception {  

4.      Request request = (Request) req.getNote(ADAPTER_NOTES);  

5.      Response response = (Response) res.getNote(ADAPTER_NOTES);  

6.    

7.      if (request == null) {  

8.          // Tomcat容器中传递的Request和Response都是这里创建的Request及Response对象 

9.          //通过Connector创建org.apache.catalina.connector.Request对象

10.         request = connector.createRequest();  

11.         /** 设置org.apache.coyote.Request对象 */  

12.         request.setCoyoteRequest(req);  

13.   

14.         //通过Connector创建org.apache.catalina.connector.Response对象

15.         response = connector.createResponse();  

16.         //设置org.apache.coyote.Response对象

17.         response.setCoyoteResponse(res);  

18.   

19.         //把Request及Response对象相互关联起来  

20.         request.setResponse(response);  

21.         response.setRequest(request);  

22.     }  

24.     try {  

25.         //解析HTTP协议  

26.         //解析请求的Cookie和sessionId等信息  

27.         //从Connector中映射此请求对应的StandardHost及StandardContext及StandardWrapper */  

28.         boolean postParseSuccess = postParseRequest(req, request, res, response);  

29.   

30.         if (postParseSuccess) {  

31.             //开始调用Tomcat的容器,首先调用StandardEngine容器中管道Pipeline中的第一个Valve,传入connector.Request和connector.Response

32.             connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

33.         }  

35.         //完成此次请求 

36.         request.finishRequest();  

37.         //完成此次响应,并提交响应信息

38.         //如果response已经提交则直接返回,否则提交response  

39.         response.finishResponse();  

40.     } catch (IOException e) {  

41.     } 

43. }  

StandardEngin容器默认配置了StandardEnginValve阀门。它主要做负责选择相应的Host去处理请求,StandardEngineValve的invoke()方法的核心代码如下:

Java代码 

1.  public final void invoke(Request request, Response response)  

2.      throws IOException, ServletException {  

3.    

4.      // 得到此请求所对应的StandardHost容器

5.      Host host = request.getHost();  

6.  

7.      //调用StandardHost容器中管道Pipeline中的第一个Valve  

8.      host.getPipeline().getFirst().invoke(request, response);  

9.  }  

StandardHost默认情况下配置了ErrorReportValve阀门与StandardHostValue阀门。ErrorReportValve用于处理错误日志。StandardHostValue则选择相应的Context容器,并且把当前Web应用的WebappClassLoader设置为线程上下文类加载器,保证我们的Web应用中拿到的当前线程类加载器为此应用的类加载器, StandardHostValve的invoke()方法的核心代码如下:

Java代码 

1.  public final void invoke(Request request, Response response)  

2.      throws IOException, ServletException {  

4.      //得到此次请求所对应的StandardContext容器 

5.      Context context = request.getContext();  

7.      if( context.getLoader() != null ) {   

8.           //线程上下文类加载器切换成当前WebApp的类加载器,从Context的loader中获取

9.           Thread.currentThread().setContextClassLoader(context.getLoader().getClassLoader());  

10.     }  

12.     if (asyncAtStart || context.fireRequestInitEvent(request)) {  

13.         try {  

14.             //调用StandardContext容器中管道Pipeline中的第一个Valve

15.             context.getPipeline().getFirst().invoke(request, response);  

16.         } catch (Throwable t) {  

17.             ExceptionUtils.handleThrowable(t);  

18.         }  

19.     }

20.     //还原StandardClassLoader类加载器为线程上下文类加载器  

21.     Thread.currentThread().setContextClassLoader(StandardHostValve.class.getClassLoader());  

22. }  

StandardContext默认情况下配置了StandardContextValve阀门。对于WEB-INF与META-INF目录禁止访问的控制,之后获取一个此请求的Wrapper容器, StandardContextValve的invoke()方法核核心代码如下:

Java代码 

1.  public final void invoke(Request request, Response response)  

2.       throws IOException, ServletException {

3.       // 对于WEB-INF与META-INF目录禁止访问的控制

4.       MessageBytes requestPathMB = request.getRequestPathMB();  

5.       if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))  

6.               || (requestPathMB.equalsIgnoreCase("/META-INF"))  

7.               || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))  

8.               || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {  

9.           response.sendError(HttpServletResponse.SC_NOT_FOUND);  

10.          return;  

11.      }  

12.      //得到此请求所对应的StandardWrapper容器 

13.      Wrapper wrapper = request.getWrapper();  

15.      //调用StandardWrapper容器中管道Pipeline中第一个Valve的invoke()方法

16.      wrapper.getPipeline().getFirst().invoke(request, response);  

17.  }  

StandardWrapper容器默认情况下配置了StandardWrapperValve阀门,主要是找到当前请求的需要拦截的过滤器Filter及初始化当前请求的Servlet对象,最终会封装在FilterChain对象中,责任链方式执行, StandardWrapperValve的invoke()方法的核心代码如下:

Java代码 

1.  public final void invoke(Request request, Response response)  

2.       throws IOException, ServletException {  

3.    

4.       //得到StandardWrapper容器  

5.       //每个请求都会对应相应的StandardWrapper及StandardWrapperValve对象  

6.       StandardWrapper wrapper = (StandardWrapper) getContainer();  

7.    

8.       //得到此请求的StandardContext对象  

9.       Context context = (Context) wrapper.getParent();  

10.   

11.      //得到所请求的Servlet对象

12.      Servlet servlet = null;  

14.      try {  

15.          if (!unavailable) {  

16.             //从StandardWrapper容器获取一个Servlet对象, Servlet对象的创建及初始化init都在这里执行

17.              servlet = wrapper.allocate();  

18.          }  

19.      } catch (UnavailableException e) {}

20.   

21.      //创建ApplicationFilterFactory对象

22.      ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();  

23.   

24.      //创建此次请求的ApplicationFilterChain对象,并设置所请求的Servlet对象

25.      //每个请求都会创建一个ApplicationFilterChain对象,包装了所请求的Servlet对象及一系列拦截的过滤器Filter对象

26.      ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet);  

28.      try {  

29.          if ((servlet != null) && (filterChain != null)) {  

30.               //调用ApplicationFilterChain的doFilter方法

31.              //传入org.apache.catalina.connector.RequestFacade及org.apache.catalina.connector.ResponseFacade对象,开始进行请求处理 

32.              filterChain.doFilter(request.getRequest(),response.getResponse());  

33.          }

34.      } catch (ClientAbortException e) {  

35.          exception(request, response, e);  

36.      } 

38.      try {  

39.          if (servlet != null) {  

40.             //执行完后把Servlet实例回收到Servlet实例池  

41.              wrapper.deallocate(servlet);  

42.          }  

43.      } catch (Throwable e) {  

44.          ExceptionUtils.handleThrowable(e);  

45.      }  

46.  }  

最后就会进入ApplicationFilterChain,这个也是Tomcat管道处理请求的最后节点,在ApplicationFilterChain中会依次调用Web应用的Filter,然后最终调用当前请求映射的Servlet, ApplicationFilterChain的doFilter()方法核心代码如下:

Java代码 

1.  private void internalDoFilter(ServletRequest request,  

2.                                    ServletResponse response)  

3.          throws IOException, ServletException {  

4.    

5.          //如果还有过滤器Filter,则执行Filter  

6.          if (pos < n) {  

7.    

8.              //得到过滤器Filter  

9.              ApplicationFilterConfig filterConfig = filters[pos++];  

10.   

11.             Filter filter = null;  

12.             try {  

13.                 filter = filterConfig.getFilter();  

14.                 //执行过滤器Filter的doFilter(request,response,FilterChain)方法  

15.                filter.doFilter(request, response, this);  

16.   

17.            } catch (IOException e) {  

18.                 throw e;  

19.             } 

20.             //执行完Filter的doFilter()方法之后直接返回internalDoFilter方法

21.             return;  

22.         }  

24.         try {  

25.             //过滤器Filter全部执行完,最终调用Servlet的service(request,response)方法完成Web应用的业务逻辑 */  

26.            servlet.service(request, response);  

27.         } catch (IOException e) {

28.             throw e;  

29.         } 

30.     }  

 

Tomcat一次完成请求的处理流程具体序列图如下:

Tomcat7源码解析6

 3. Tomcat的关闭流程

       Tomcat的关闭流程和Tomcat启动流程类似,都是之前已经启动的组件依次关闭销毁,篇幅原因不赘述了。

Tomcat的关闭流程具体序列图如下:

Tomcat7源码解析7



 4. Tomcat的Connector组件

     前面也已经经介绍过,Connector组件是Service容器中的一部分。它主要是接收,解析HTTP请求,然后调用本Service下的相关Servlet。由于Tomcat从架构上采用的是一个分层结构,因此根据解析过的HTTP请求,定位到相应Servlet也是一个相对比较复杂的过程,整个Connector实现了从接收Socket到调用Servlet的全过程。先来看一下Connector的功能逻辑:

·        接收Socket

·        从Socket获取数据包,并解析成HttpServletRequest对象

·        从Engine容器开始走调用流程,经过各层Valve,最后调用Servlet完成业务逻辑

·        返回Response,关闭Socket

可以看出,整个Connector组件是Tomcat运行主干,目前Connector支持的协议是HTTP和AJP,本文主要是针对HTTP协议的Connector进行阐述。先来看一下Connector的配置,在server.xml里; 

Xml代码  

1. <Connector port="80" URIEncoding="UTF-8" protocol="HTTP/1.1"   

2.                connectionTimeout="20000"   

3.                redirectPort="7443" />  

熟悉的80端口不必说了。"protocol"这里是指这个Connector支持的协议。针对HTTP协议而言,这个属性可以配置的值有: 

·        HTTP/1.1

·        org.apache.coyote.http11.Http11Protocol ?BIO实现

·        org.apache.coyote.http11.Http11NioProtocol ?NIO实现

配置"HTTP/1.1"和"org.apache.coyote.http11.Http11Protocol"的效果是一样的,因此Connector的HTTP协议实现缺省是支持BIO的。无论是BIO还是NIO都是实现一个org.apache.coyote.ProtocolHandler接口,因此如果需要定制化,也必须实现这个接口。 接下来再来看看默认状态下HTTP Connector的架构及其消息流。

Tomcat7源码解析8

 可以看见Connector中三大块 

·        Http11Protocol

·        Mapper

·        CoyoteAdapter

Http11Protocol 
       类完全路径org.apache.coyote.http11.Http11Protocol,这是支持HTTP的BIO实现。Http11Protocol包含了JIoEndpoint对象及Http11ConnectionHandler对象。 
Http11ConnectionHandler对象维护了一个Http11Processor对象池,Http11Processor对象会调用CoyoteAdapter完成HTTPr Request的解析和分派。 
JIoEndpoint维护了两个线程,Acceptor请求接收线程及Executor请求处理线程池。Acceptor是接收Socket,然后从 Executor请求处理线程池中找出空闲的线程处理socket,如果 Executor请求处理线程池没有空闲线程,请求会被放入阻塞队列等待有空闲线程。

Mapper 
      类完全路径org.apache.tomcat.util.http.mapper.Mapper,此对象维护了一个从Host到Wrapper的各级容器的快照。它主要是为了,当HTTP Request被解析后,能够将HTTP Request绑定到相应的Host,Context,Wrapper(Servlet),进行业务处理;

CoyoteAdapter 
      类完全路径org.apache.catalina.connector.CoyoteAdapter,此对象负责将http request解析成HttpServletRequest对象,之后绑定相应的容器,然后从Engine开始逐层调用Valve直至该Servlet。比如根据Request中的JSESSIONID绑定服务器端的相应Session。这个JSESSIONID按照优先级或是从Request URL中获取,或是从Cookie中获取,然后再Session池中找到相应匹配的Session对象,然后将其封装到HttpServletRequest对象,所有这些都是在CoyoteAdapter中完成的。看一下将Request解析为HttpServletRequest对象后,开始调用Servlet的代码; 

Java代码  

1. connector.getContainer().getPipeline().getFirst().invoke(request, response);

Connector的拿到的容器就是StandardEngine,代码的可读性很强,获取StandardEngine的Pipeline,然后从第一个Valve开始调用逻辑。

 

Tomcat的Connector组件工作具体序列图入下:

Tomcat7源码解析9



 5. Tomcat运行过程中的线程概况及线程模型

       Tomcat在运行过程中会涉及到很多线程,主要的线程有Tomcat启动时的main主线程(Java进程启动时的那个线程), 子容器启动与关系线程池(用来启动关系子容器),Tomcat后台线程(Tomcat内置的后台线程,比如用来热部署Web应用), 请求接收线程(用来接收请求的线程), 请求处理线程池(用来处理请求的线程)。理解了Tomcat运行过程中的主要线程,有助于我们理解整个系统的线程模型。下面是每个线程的源码及功能的详细分析:

Java代码

1.  (1) main主线程:即从main开始执行的线程,在启动Catalina之后就一直在Server的await方法中等待关闭命令,如果未接收到关闭命令就一直等待下去。main线程会一直阻塞着等待关机命令。  

2.         启动过程:Bootstrap.main>>

3.                           Catalina.start >>

4.                                   Catalina.await() >>

5.                                               StandardServer.await()

6.  

7.  (2) 子容器启动与关闭线程:ContainerBase的protected ThreadPoolExecutor startStopExecutor容器线程池执行器[处理子容器的启动和停止]在ContainerBase.initInternal()方法中进行初始化       作用:在ContainerBase.startInternal()方法中启动子容器,在ContainerBase.stopInternal()方法中停止子容器,即处理子容器的启动和停止事件。  

8.         子容器线程池创建过程:StandardService.initInternal>>

9.                                        ContainerBase.initInternal >> 

10.                                                      new ThreadPoolExecutor()  

11.        启动过程:StandardService.startInternal>>

12.                               ContainerBase.startInternal >>

13.                                                startStopExecutor.submit(new StartChild(Child)或StopChild(Child))>>

14.                                                                                                        execute(ftask) >>           

15.                                                                                                                     addIfUnderCorePoolSize(command)             

16.        addIfUnderCorePoolSize内容如下:  

17. private boolean addIfUnderCorePoolSize(Runnable firstTask) {  

18.         Thread t = null;  

19.         final ReentrantLock mainLock = this.mainLock;  

20.         mainLock.lock();  

21.         try {  

22.             if (poolSize < corePoolSize && runState == RUNNING)  

23.                 t = addThread(firstTask);  

24.         } finally {  

25.             mainLock.unlock();  

26.         }  

27.         if (t == null)  

28.             return false;  

29.         t.start(); // StartChild.call()被调用,调用子容器的child.start()启动子容器   

30.         return true;  

31. }  

32. private Thread addThread(Runnable firstTask) {  

33.         Worker w = new Worker(firstTask);  

34.         Thread t = threadFactory.newThread(w); //创建一个线程

35.         if (t != null) {  

36.             w.thread = t;  

37.             workers.add(w);  

38.             int nt = ++poolSize;  

39.             if (nt > largestPoolSize)  

40.                 largestPoolSize = nt;  

41.         }  

42.         return t;  

43.     }

44. 

45. (3) 容器的后台处理线程:ContainerBackgroundProcessor是ContainerBase的一个内部类 

46.         启动过程:StandardService.startInternal>>

47.                                     ContainerBase.startInternal >> 

48.                                                    ContainerBase.threadStart()  

49.    ContainerBase.startInternal(){  

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

51.           startStopExecutor.submit(new StartChild(children[i]); //起新线程启动子容器  

52.         }  

53.         threadStart(); // 在启动容器的最后调用threadStart方法  

54.     }   

55. protected void threadStart() {  

56.         if (thread != null)  

57.             return;  

58.         // backgroundProcessorDelay默认为-1,即不产生后台线程,但StandardEngine的构造  

59.         // 函数中设置backgroundProcessorDelay = 10,即默认只有Engine产生后台线程,  

60.         // 其它子容器共享Engine的后台线程

61.         if (backgroundProcessorDelay <= 0)   

62.             return;  

63.         threadDone = false;  

64.         String threadName = "ContainerBackgroundProcessor[" + toString() + "]";  

65.         thread = new Thread(new ContainerBackgroundProcessor(), threadName);  

66.         thread.setDaemon(true);  

67.         thread.start(); // 启动容器后台线程

68. }

69. 

70. (4) 请求接收线程:即运行Acceptor的线程,启动Connector的时候产生  

71.         启动过程:StandardService.startInternal>>

72.                             Connector.startInternal() >>

73.                                            protocolHandler.start() >>

74.                                                      AbstractProtocol.start() >>

75.                                                                        endpoint.start() >>

76.                                                                                  AbstractEndpoint.Start() >>

77.                                                                                               JioEndpoint.startInternal() >>

78.                                                                                                              JIoEndpoint.startAcceptorThreads()  

79.     startAcceptorThreads内容如下:  

80.     protected final void startAcceptorThreads() {  

81.         int count = getAcceptorThreadCount(); // 默认只有一个接收线程Acceptor

82.         acceptors = new Acceptor[count];  

83.         for (int i = 0; i < count; i++) {  

84.             acceptors[i] = createAcceptor(); // org.apache.tomcat.util.net. JIoEndpoint.Acceptor

85.             Thread t = new Thread(acceptors[i], getName() + "-Acceptor-" + i);  //创建请求接收线程

86.             t.setPriority(getAcceptorThreadPriority());  

87.             t.setDaemon(getDaemon());  

88.             t.start(); // 启动请求接收线程来接收请求

89.         }  

90.     }  

91. 这样在Acceptor的run()中就可以接收请求了。

92. 
(5) 请求处理线程:即运行SocketProcessor的线程,从请求处理线程池中产生,org.apache.tomcat.util.net. AbstractEndpoint的成员变量private Executor executor用来处理请求,                        org.apache.tomcat.util.net.JioEndpoint(继承了AbstractEndpoint)在方法startInternal()中调用createExecutor()来创建executor为线程池对象ThreadPoolExecutor  

93.         请求处理线程池创建过程:StandardService.startInternal>>

94.                                            Connector.startInternal >>

95.                                                          protocolHandler.start >>

96.                                                                   AbstractProtocol.start >>

97.                                                                                 endpoint.start >>             

98.                                                                                       AbstractEndpoint.Start >>

99.                                                                                               JIoEndpoint.startInternal >>  

100.                                                                                                            JIoEndpoint.createExecutor>> 

101.                                                                                                                           new ThreadPoolExecutor()  

102.      public void createExecutor() {  

103.          internalExecutor = true;  

104.          TaskQueue taskqueue = new TaskQueue();  

105.          // 线程池线程产生的的工厂为java.util.concurrent.ThreadFactory.TaskThreadFactory

106.          TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());  

107.          executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);  

108.          taskqueue.setParent( (ThreadPoolExecutor) executor);  

109.      }  

110.          启动过程:Acceptor.run()>> // 请求接收线程接收到请求后

111.                             processSocket(socket)>>  

112.                                   getExecutor().execute(new SocketProcessor(wrapper))>> // 交给请求处理线程处理

113.                                                                   ThreadPoolExecutor.execute (command,0,TimeUnit.MILLISECONDS)>>  

114.                                                                                                                 super.execute(command) >>  

115.                                                                                                                            addIfUnderCorePoolSize(command)  

116.  addIfUnderCorePoolSize内容如下:  

117.  private boolean addIfUnderCorePoolSize(Runnable firstTask) {  

118.          Thread t = null;  

119.          final ReentrantLock mainLock = this.mainLock;  

120.          mainLock.lock();  

121.          try {  

122.              if (poolSize < corePoolSize && runState == RUNNING)  

123.                  t = addThread(firstTask);  

124.          } finally {  

125.              mainLock.unlock();  

126.          }  

127.          if (t == null)  

128.              return false;  

129.          t.start();  // SocketProcessor.run运行

130.          return true;  

131.  }  

132.  private Thread addThread(Runnable firstTask) {  

133.          Worker w = new Worker(firstTask);  

134.          Thread t = threadFactory.newThread(w); //创建一个线程

135.          if (t != null) {  

136.              w.thread = t;  

137.              workers.add(w);  

138.              int nt = ++poolSize;  

139.              if (nt > largestPoolSize)  

140.                  largestPoolSize = nt;  

141.          }  

142.          return t;  

143.  }  

144.  至此SocketProcessor.run运行起来了,这个线程负责处理请求。 

 

 Tomcat运行过程中的线程概况具体序列图入下:

Tomcat7源码解析10

        大家都知道Java中的IO模型分阻塞式的IO模型(BIO)及非阻塞式的IO模型(NIO),Tomcat中的对请求接收网络IO模块中也同样实现了基于上述两种IO的线程模型,具体的实现如下,

 Tomcat基于阻塞式IO(BIO)的线程模型序列图如下:

Tomcat7源码解析11

Tomcat基于非阻塞式IO(NIO)的线程模型序列图如下:

Tomcat7源码解析12



6. Tomcat的类加载机制

   主流的JavaWeb服务器如(Tomcat,Jetty,WebLogic)都实现了自己定义的类加载器(一般不止一个),因为Web服务器一般要解决如下几个问题:

          1、部署在同一个服务器上的两个Web应用程序所使用的Java类库可以实现相互隔离。

          2、部署在同一个服务器上的两个应用程序所使用的Java类库可以相互共享。

          3、服务器需要尽可能地保证自身的安全不受部署的Web应用程序的影响

          为了解决这几个问题,Tomcat实现了如下类加载器的层级结构:

Tomcat7源码解析13

Tomcat7运行时类的加载说明:

1)Bootstrap Class Loader是JVM的内核由C实现的,加载了JVM的核心包rt.jar。rt.jar中的所有类执行其class的getClassLoader()方法都将返回null,例如Integer.class.getClassLoader()。

 

2)Extension Class Loader主要加载了JVM扩展包中相关的jar包。例如运行下列代码将System.out.println(ZipPath.class.getClassLoader());将得到如下的运行结果:sun.misc.Launcher$ExtClassLoader。

 

3)System Class Loader加载CLASSPATH相关的类,例如在Tomcat的Bootstrap的main方法中执行System.out.println(Bootstrap.class.getClassLoader());则将得到:sun.misc.Launcher$AppClassLoader。

 

4)Common Class Loader,Tomcat7中的CATALINA_HOME/lib下的jar包。注意Tomcat在启动文件中将启动时配置了-classpath "%CATALINA_HOME%\lib\catalina.jar"因此catalina.jar中的类虽然指定使用类加载器Common Class Loader,但是按JVM的委托加载原则System.out.println(Bootstrap.class.getClassLoader());得到的类加载器是:sun.misc.Launcher$AppClassLoader。

 

5)Webapp Class Loader, 主要负责加载Context容器中的所有的类。实际上该加载器提供了参数delegateLoad供用户设定是否使用parent-first加载。默认该值为false,默认用parent-last加载。出于安全性的考虑对于核心类WebappClassLoader是不允许加载的。包括:java.,javax.servlet.jsp.jstl,javax.servlet.,javax.el。


Tomcat类加载器的相关类图 

Tomcat7源码解析14

Tomcat类加载器的相关源代码分析

(一) ClassLoader的load方法

Java代码  

1. protected Class<?> loadClass(String name, boolean resolve)  

2.         throws ClassNotFoundException  

3.     {  

4.         synchronized (getClassLoadingLock(name)) {  

5.             // First, check if the class has already been loaded  

6.             Class c = findLoadedClass(name);

7.             if (c == null) {  

8.                 try {  

9.                     if (parent != null) {  

10.                        c = parent.loadClass(name, false);  

11.                    } else {  

12.                        c = findBootstrapClassOrNull(name);  

13.                    }  

14.                }   

15.                if (c == null) {  

16.                    // If still not found, then invoke findClass in order  

17.                    // to find the class.  

18.                    long t1 = System.nanoTime();  

19.                    c = findClass(name);  

20.                }  

21.            }  

22.            if (resolve) {  

23.                resolveClass(c);  

24.            }  

25.            return c;  

26.        }  

27.    }  

1)在该方法中,首先检查是否已经加载了该类,这里有个问题JVM如何判断一个类是否被加载过的?这里涉及到了类的命名空间问题。在JAVA中判断一个类是否相同不仅看类名是否相同,还要看其类加载器是否相同。同一个类可以被不同的类加载器所加载,并且认为是不同的。该问题可以分解下面两个方面看。

  a) 单一加载原则:在加载器链中,一个类只会被链中的某一个加载器加载一次。而不会被重复加载。实现类的共享,Tomcat多个应用,如果需要共享一些jar包,那么只需要交给                   commonClassLoader加载,那么所有的应用就可以共享这些类。

  b) 可见性原则:父加载器加载的类,子加载器是可以访问的。而自加载器所加载的类,父加载器无法访问。不同加载器链之间其是相互不可见,无法访问的。实现隔离,Tomcat就是应         用该特性,为每一个Context容器创建一个WebappClassLoader类加载器对象,从而实现了应用间的相互隔离。应用间的类是不可见的所以无法相互访问。

2) 如果步骤一中无缓存,查看该类父加载器,如果存在那么委托给付加载器。如果没有父加载器那么认为BootstrapClassLoader是其父加载器,委托进行加载。

3)如果父加载器无法加载则抛出ClassNotFoundException,调用抽象方法findClass方法。

4)此处的resolveClass方法指的是上文类加载过程中连接的第三步操作。resolve该类的形式引用等等。

 

(二)类URLClassLoader的findClass方法

Java代码  

1. protected Class<?> findClass(final String name)  

2.          throws ClassNotFoundException  

3.     {  

4.         try {  

5.             return AccessController.doPrivileged(  

6.                 new PrivilegedExceptionAction<Class>() {  

7.                     public Class run() throws ClassNotFoundException {  

8.                         String path = name.replace('.', '/').concat(".class");  

9.                         Resource res = ucp.getResource(path, false);  

10.                        if (res != null) {  

11.                            try {  

12.                                return defineClass(name, res);  

13.                            } catch (IOException e) {  

14.                                throw new ClassNotFoundException(name, e);  

15.                            }  

16.                        } else {  

17.                            throw new ClassNotFoundException(name);  

18.                        }  

19.                    }  

20.                }, acc);  

21.        } catch (java.security.PrivilegedActionException pae) {  

22.            throw (ClassNotFoundException) pae.getException();  

23.        }  

24.    }  

  该方法的核心是加载获取Java类字节码文件的字节流,然后调用父类的defineClass方法完成类的构造过程。defineClass是由JVM实现的,不允许被覆写,因此用户类文件就必须遵循     JVM的文件规范才能被正确的解析。

 

(三)WebappClassLoader重写了ClassLoader的loadClass方法

Java代码  

1. @Override  

2. public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {  

3.         Class<?> clazz = null;  

4. 

5.         // (0) 当前对象缓存中检查是否已经加载该类  

6.         clazz = findLoadedClass0(name);  

7.   

8.         // (0.1) 检查JVM的缓存,是否已经加载过该类  

9.         clazz = findLoadedClass(name);  

10.  

11.        // (0.2) 防止加载一些系统相关的类  

12.        try {  

13.            clazz = system.loadClass(name);  

14.            if (clazz != null) {  

15.                if (resolve)  

16.                    resolveClass(clazz);  

17.                return (clazz);  

18.            }  

19.        } catch (ClassNotFoundException e) {}  

20.  

21.        boolean delegateLoad = delegate || filter(name);  

22.  

23.        // (1) 如果配置了parent-first模式,那么委托给父加载器  

24.        if (delegateLoad) {  

25.            ClassLoader loader = parent;  

26.            if (loader == null) loader = system;  

27.            try {  

28.                clazz = Class.forName(name, false, loader);  

29.                if (clazz != null) {  

30.                    if (resolve) resolveClass(clazz);  

31.                    return (clazz);  

32.                }  

33.            } catch (ClassNotFoundException e) {}  

34.        }  

35.  

36.        // (2) 从WebApp中去加载类,主要是WebApp下的/classes目录与/lib目录  

37.        try {  

38.            clazz = findClass(name);  

39.            if (clazz != null) {  

40.                if (resolve)  

41.                    resolveClass(clazz);  

42.                return (clazz);  

43.            }  

44.        } catch (ClassNotFoundException e) {}  

45.  

46.        // (3) 如果在当前WebApp中无法加载到,委托给StandardClassLoader从$catalina_home/lib中去加载

47.        if (!delegateLoad) {  

48.            ClassLoader loader = parent;  

49.            if (loader == null)  

50.                loader = system;  

51.            try {  

52.                clazz = Class.forName(name, false, loader);  

53.                if (clazz != null) {  

54.                    if (resolve)  

55.                        resolveClass(clazz);  

56.                    return (clazz);  

57.                }  

58.            } catch (ClassNotFoundException e) {}  

59.        }  

60.  

61.        throw new ClassNotFoundException(name);  

62.    }  



(四)WebappClassLoader的findClass()方法>>findClassInternal()方法>>findResourceInternal()方法

Java代码 

1.      //从WebappClassLoader的classpath中加载类或资源文件 

2.      protected ResourceEntry findResourceInternal(String name, String path) {  

3.          if (!started) {  

4.              log.info(sm.getString("webappClassLoader.stopped", name));  

5.              return null;  

6.          }  

7.          ResourceEntry entry = resourceEntries.get(name);  

8.          if (entry != null)  

9.              return entry;  

10.   

11.         int contentLength = -1;  

12.         InputStream binaryStream = null;  

13.   

14.         int jarFilesLength = jarFiles.length;  

15.         int repositoriesLength = repositories.length;  

16.   

17.         int i;  

18.         Resource resource = null;  

19.         boolean fileNeedConvert = false;  

20.   

21.        //首先从/WEB-INF/classes路径中加载类

22.         for (i = 0; (entry == null) && (i < repositoriesLength); i++) {  

23.             try {  

24.                 String fullPath = repositories[i] + path;  

25.                 //获取资源的绝对路径 

26.                 Object lookupResult = resources.lookup(fullPath);  

27.                 if (lookupResult instanceof Resource) {  

28.                     resource = (Resource) lookupResult;  

29.                 }  

30. 

31.                 ResourceAttributes attributes =  

32.                     (ResourceAttributes) resources.getAttributes(fullPath);  

33.                 contentLength = (int) attributes.getContentLength();  

34.                 String canonicalPath = attributes.getCanonicalPath();  

35.                 if (canonicalPath != null) {  

36.                     entry = findResourceInternal(new File(canonicalPath), "");  

37.                 } else {  

38.                     //获取类或文件的ResourceEntry  

39.                     entry = findResourceInternal(files[i], path);  

40.                 }  

41.                 entry.lastModified = attributes.getLastModified();  

42.   

43.                 if (resource != null) {  

44.                     try {  

45.                         //得到类或资源的输入流InputStream  

46.                         binaryStream = resource.streamContent();  

47.                     } catch (IOException e) {  

48.                         return null;  

49.                     }  

50.                     if (needConvert) {  

51.                         if (path.endsWith(".properties")) {  

52.                             fileNeedConvert = true;  

53.                         }  

54.                     }  

55.                 }  

56.             } catch (NamingException e) {  

57.             }  

58.         }  

59.   

60.        //然后再从/WEB-INF/lib路径中加载类

61.         if ((entry == null) && (notFoundResources.containsKey(name)))  

62.             return null;  

63.         JarEntry jarEntry = null;  

64.         synchronized (jarFiles) {  

65.             try {  

66.                 for (i = 0; (entry == null) && (i < jarFilesLength); i++) {  

67.                     //获取JarFile下的JarEntry 

68.                     jarEntry = jarFiles[i].getJarEntry(path);  

69.   

70.                     if (jarEntry != null) {  

71.                         entry = new ResourceEntry();  

72.                         try {  

73.                             //设置类或文件的URL  

74.                             entry.codeBase = getURL(jarRealFiles[i], false);  

75.                             String jarFakeUrl = getURI(jarRealFiles[i]).toString();  

76.                             jarFakeUrl = "jar:" + jarFakeUrl + "!/" + path;  

77.                             //设置URL  

78.                             entry.source = new URL(jarFakeUrl);  

79.                             entry.lastModified = jarRealFiles[i].lastModified();  

80.                         } catch (MalformedURLException e) {  

81.                             return null;  

82.                         }  

83.                         contentLength = (int) jarEntry.getSize();  

84.                         try {  

85.                             entry.manifest = jarFiles[i].getManifest();  

86.                             //从JarFile中根据JarEntry获取jar包中类的输入流InputStream  

87.                             binaryStream = jarFiles[i].getInputStream(jarEntry);  

88.                         } catch (IOException e) {  

89.                             return null;  

90.                         }  

91.                     }  

92.                 }  

94.                 if (binaryStream != null) {  

95.                     byte[] binaryContent = new byte[contentLength];  

96.                     int pos = 0;  

97.                     try {  

98.                         while (true) {  

99.                             //从输入流InputStream中读取类或文件的二进制流  

100.                              int n = binaryStream.read(binaryContent, pos, binaryContent.length - pos); 

101.                              if (n <= 0)  

102.                                  break;  

103.                              pos += n;  

104.                          }  

105.                      } catch (IOException e) {  

106.                          return null;  

107.                      }  

108.                      //设置二进制设置到ResourceEntry 

109.                      entry.binaryContent = binaryContent;  

110.                  }  

111.              } 

112.          }  

113.          synchronized (resourceEntries) {

114.              ResourceEntry entry2 = resourceEntries.get(name);

115.              if (entry2 == null) {  

116.                  //向本地资源缓存这注册ResourceEntry

117.                  resourceEntries.put(name, entry);  

118.              } else {  

119.                  entry = entry2;  

120.              }  

121.          }  

122.          return entry;  

123.      }  

(五)Web应用中经常使用到的线程上下文类加载器的在Tomcat中的设置实现

 在Web应用中我们经常用到线程上下文类加载器,拿到的肯定是当前WebApp的WebAppClassLoader, 如下

Java代码  

1.  ClassLoader classLoader= Thread.currentThread().getContextClassLoader();

 

我们用到的线程上下文类加载器其实是在StandardHostValve的invoke()方法中被设置的,

Java代码 

1.  public final void invoke(Request request, Response response)  

2.      throws IOException, ServletException {  

4.      //得到此次请求所对应的StandardContext容器  

5.      Context context = request.getContext();  

7.      if( context.getLoader() != null ) {  

8.              //线程上下文类加载器切换成当前WebApp的类加载器,从Context的loader中获取  

9.              Thread.currentThread().setContextClassLoader(context.getLoader().getClassLoader());  

10.     }  

11.  

12.     if (asyncAtStart || context.fireRequestInitEvent(request)) {  

13.         try {  

14.             //调用StandardContext容器中管道Pipeline中的第一个Valve,直到调用Servlet  

15.             context.getPipeline().getFirst().invoke(request, response);  

16.         } catch (Throwable t) {  

17.             ExceptionUtils.handleThrowable(t);  

18.             request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);  

19.             throwable(request, response, t);  

20.         }  

21.     }  

22.     if (!Globals.IS_SECURITY_ENABLED) {  

23.         //还原StandardClassLoader类加载器为线程上下文类加载器 

24.         Thread.currentThread().setContextClassLoader(StandardHostValve.class.getClassLoader();  

25.     }  

26. }  


7、Tomcat所涉及的设计模式

         Tomcat虽然代码比较庞大,但是整体还是设计的比较优雅,特别是很多组件化的设计思路,其中涉及到的一些常用的设计模式值得我们学习及借鉴:

责任链模式
        Tomcat中有两个地方比较明显的使用了责任链模式,一、Tomcat中的ApplicationFilterChain实现了Filter拦截和实际Servlet的请求,是典型的责任链模式。其他开源框架中类似的设计还有Struts2中的DefaultActionInvocation实现Interceptor拦截和Action的调用。Spring AOP中ReflectiveMethodInvocation实现MethodInceptor方法拦截和target的调用。二、Tomcat中的Pipeline-Valve模式也是责任链模式的一种变种,从Engine到Host再到Context一直到Wrapper都是通过一个链来传递请求。

观察者模式
        Tomcat通过LifecycleListener对组件生命周期组件Lifecycle进行监听就是典型的观察者模式,各个组件在其生命期中会有各种各样行为,而这些行为都会触发相应的事件,Tomcat就是通过侦听这些事件达到对这些行为进行扩展的目的。在看组件的init和start过程中会看到大量如:lifecycle.fireLifecycleEvent(AFTER_START_EVENT,null);这样的代码,这就是对某一类型事件的触发,如果你想在其中加入自己的行为,就只用注册相应类型的事件即可。

门面模式
      门面设计模式在 Tomcat 中有多处使用,在 Request 和 Response 对象封装中(RequestFacade,ResponseFacade)、ApplicationContext 到ApplicationContextFacade等都用到了这种设计模式。这种设计模式主要用在一个大的系统中有多个子系统组成时,这多个子系统肯定要涉及到相互通信,但是每个子系统又不能将自己的内部数据过多的暴露给其它系统,不然就没有必要划分子系统了。每个子系统都会设计一个门面,把别的系统感兴趣的数据封装起来,通过这个门面来进行访问。

模板方法模式

  模板方法模式是我们平时开发当中常用的一种模式,把通用的骨架抽象到父类中,子类去实现特地的某些步骤。Tomcat及Servlet规范API中也大量的使用了这种模式,比如Tomcat中的ContainerBase中对于生命周期的一些方法init,start,stop和Servlet规范API中的GenericServlet中的service抽象骨架模板方法均使用了模板方法模式。

 

参考资源
Tomcat官网:http://tomcat.apache.org/
<How Tomcat Wroks>:Tomcat5的架构分析
Tomcat源码分析:http://zddava.iteye.com/blog/305944

Tomat的源码环境搭建:http://mabusyao.iteye.com/blog/1198557

<Servlet2.5规范>:JSR制定的规Servlet范

0 0
原创粉丝点击