`
hyw520110
  • 浏览: 222689 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

[How Tomcat Works]第3章 连接器(一)

    博客分类:
  • java
阅读更多

译者 jarfield  

博客 http://jarfield.iteye.com

概述

    就像《简介》中介绍的,Catalina 中有两个主要模块:Connector (连接器)和Container (容器)。本章,你将编写一个连接器 来增强第2章的应用,该连接器 能够创建更好的RequestResponse 对象。符合Servlet 2.32.4 规范的连接器 必须创建javax.servlet.http.HttpServletRequest 实例和javax.servlet.http.HttpServletResponse 实例,并将它们作为参数传递给servletservice 方法。第2章的Servlet 容器只能运行实现了javax.servlet.Servlet 接口的servlet ,并传递javax.servlet.ServletRequest 实例和javax.servlet.ServletResponse 实例给servletservice 方法。连接器 并不知道servlet 的类型(例如,是否实现了javax.servlet.Servlet 接口, 继承了javax.servlet.GenericServlet ,或继承了javax.servlet.http.HttpServlet ), 因此它必须始终提供HttpServletRequest 实例和HttpServletResponse 实例。

    本章的应用程序中,连接器 解析HTTP 请求的headers ,使得servlet 可以获得headerscookies 、参数名/值,等等。我们也会完善第2章中Response 类的getWriter 方法,修正它的行为(译者注:第2 章中的实现是有问题的)。由于这些改进,我们可以从PrimitiveServlet 得到完整的响应,同时也能够运行更加复杂的ModernServlet 。本章构建的连接器Tomcat 4 默认连接器 的一个简化版本,我们会在第4 章详细讨论Tomcat 4 的默认Connecotr 。虽然Tomcat 默认连接器Tomcat 4 中已经不推荐使用了,但是它仍是一个很好的学习工具。在本章接下来的讨论中,凡是提到的连接器 ,都是指本章构建的连接器 ,而不是Tomcat 的默认连接器

    提示:与上一章的应用不同,本章应用中连接器 和容器是分离的。
 
    本章应用的代码在ex03.pyrmont 包及其子包中。构成连接器 的类是ex03.pyrmont.connector 包和ex03.pyrmont.connector.http 包的一部分。从本章开始,每个应用都有一个bootstrap 类,用于启动整个应用。不过,目前还没有停止应用的机制。应用一旦运行起来,你必须通过关闭控制台(Windows 平台)或杀死进程(UNIX/Linux 平台)的粗鲁的方式来停止应用。

    在解释应用之前,请允许我先介绍org.apache.catalina.util 包中的StringManager 类。该类负责处理本应用及Catalina 自身各模块的错误消息的国际化。然后,我们会讨论整个应用。

StringManager

    像Tomcat 这样的大型程序,都需要仔细地处理错误消息。在Tomcat 中,错误消息对系统管理员和Servlet 程序员都很重要。例如,通过Tomcat 的错误日志,系统管理员可以轻松定位任何异常。Tomcat 为内部抛出的每个javax.servlet.ServletException 打印出一条特定错误日志,这样Servlet 程序员就可以知道自己写的servlet 哪里出了问题。

    Tomcat 采用的方法是将错误消息存储在一个属性(properties )文件中,这样就可以方便地编辑错误消息。但是,Tomcat 有数百个类,如果将所有类的错误消息都存储在一个巨大的属性文件中,那么维护这些错误消息就是一个恶梦。为了避免这个问题,Tomcat 为每个包定义了一个属性文件。例如,org.apache.catalina.connector 包中的属性文件包括了该包所有类抛出的错误消息。每个属性文件都会被一个特定的org.apache.catalina.util.StringManager 实例处理。Tomcat 运行的时侯,会有很多StringManager 实例,每个实例都会读取对应包中的属性文件。而且,由于Tomcat 十分流行,提供多语言版本的错误消息是很有意义的。目前,Tomcat 共支持三种语言。英语的属性文件名都是LocalStrings.properties 。其他两种语言是西班牙语和日语,其属性文件分别为LocalStrings_es.propertiesLocalStrings_ja.properties

    当类需要在属性文件中查找错误消息时,它首先获取一个StringManager 实例。但是,同一个包中很多类都可能需要一个StringManager 实例,如果为每个需要错误消息的类对象创建一个StringManager 实例,则是对资源的浪费。因此StringManager 类被设计成,同一个包中所有类对象可以共享一个StringManager 实例。如果熟悉设计模式,你可能会猜到StringManager 是一个单例类(singleton class )。StringManager 类唯一的构造函数是私有的(private ),因此你不能使用new 关键字在类外部创建该类的实例。以包名为参数,调用StringManager 类的公开静态方法getManager ,就可以获得一个StringManager 实例。每个实例被存储在一个Hashtable 中,key 就是包的名称。

Java代码 复制代码
  1. private static Hashtable managers = new Hashtable();   
  2. public synchronized static StringManager   
  3.     getManager(String packageName) {   
  4.    StringManager mgr = (StringManager)managers.get(packageName);      
  5.    if (mgr == null) {   
  6.      mgr = new StringManager(packageName);   
  7.      managers.put(packageName, mgr);   
  8.    }   
  9.    return mgr;   
  10. }   
private static Hashtable managers = new Hashtable();
public synchronized static StringManager
    getManager(String packageName) {
   StringManager mgr = (StringManager)managers.get(packageName);   
   if (mgr == null) {
     mgr = new StringManager(packageName);
     managers.put(packageName, mgr);
   }
   return mgr;
} 

 

    提示:在附带的zip 文件中,可以找到一篇题为“The Singleton Pattern ”、关于单例模式的文章。

    举个例子,为了使用ex03.pyrmont.connector.http 包中的StringManager 类,传递包名给StringManagergetManager 方法:

Java代码 复制代码
  1. StringManager sm = StringManager.getManager("ex03.pyrmont.connector.http");  
StringManager sm = StringManager.getManager("ex03.pyrmont.connector.http");

   

    ex03.pyrmont.connector.http 包中,你可以找到三个属性文件:LocalStrings.propertiesLocalStrings_es.propertiesLocalStrings_ja.propertiesStringManager 实例根据应用程序运行时所在机器的区域(local )来决定使用哪个文件。如果你打开LocalStrings.properties ,非注释的第一行应该是这样的:

Java代码 复制代码
  1. httpConnector.alreadyInitialized=HTTP connector has already been initialized   
httpConnector.alreadyInitialized=HTTP connector has already been initialized 


    要得到一条错误消息,你需要以错误码(error code )为参数调用StringManager 类的getString 方法。下面是该方法的多个重载之一:

Java代码 复制代码
  1. public String getString(String key)   
public String getString(String key) 

 

   以“httpConnector.alreadyInitialized ” 为参数调用getString 方法,就会返回“HTTP connector has already been initialized ”。

应用程序

    从本章开始,每章附带的应用程序被化分成模块。本章的应用包括三个模块:connectorstartupcore

    startup 模块只包括一个类:Bootstrap ,其作用是启动整个应用。connector 模块的类可以分成5个类别:

  • connector 和它的支持(supporting )类(HttpConnectorHttpProcessor
  • 代表HTTP 请求的类(HttpRequest )及其支持类
  • 代表HTTP 响应的类(HttpResponse )及其支持类
  • 门面(Facade )类(HttpRequestFacadeHttpResponseFacade )
  • Constant

 
    core 模块包括两个类:ServletProcessorStaticResourceProcessor


    Figure 3.1 是本应用的类图。为了让类图更具可读性,HttpRequestHttpResponse 相关的类都被省略了。我们后面讨论RequestResponse 对象时,会给出更加详细的类图。
 
    我们把Figure 3.1Figure 2.1 做个比较。第2 章的HttpServer 类被拆分成两个类:HttpConnectorHttpProcessorRequest 类被HttpRequest 类替换,Response 类被HttpResponse 类替换。而且,本章的应用使用了更多其他的类。

    第2章中的HttpServer 类负责等待HTTP 请求,创建请求对象和响应对象。本章应用中,等待HTTP 请求的任务交给了HttpConnector 实例,创建请求对象和响应对象的任务分配给了HttpProcessor 实例。

    本章中,HTTP 请求对象由实现了javax.servlet.http.HttpServletRequest 接口的HttpRequest 类来代表。HttpRequest 对象被转型为HttpServletRequest 实例,并传递给servletservice 方法。因此,每个HttpRequest 实例必须拥有适当的域,以便servlet 使用它们。需要赋给HttpRequest 对象的值包括URIquery string 、参数、cookies 和其他headers 等等。因为连接器 不知道servlet 需要哪些值,所以 必须解析所有能够从HTTP 请求获得的值。但是,解析HTTP 请求会带来昂贵(开销巨大)的字符串操作和其他操作。如果只解析servlet 需要的值,那么就可能节省大量的CPU 周期。例如,如果servlet 不需要任何请求(也就是,不调用javax.servlet.http.HttpServletRequestgetParametergetParameterMap 、 getParameterNames或getParameterValues 方法),连接器 就不需要从query stringHTTP request body 中解析出请求参数。Tomcat 的默认连接器 (包括本章应用中的连接器 )尝试通过“直到真正需要时才解析请求参数”的方式来提高效率。Tomcat 的默认连接器 和我们的连接器 使用SocketInputStream 类从SocketInputStream 中读取字节流。SocketInputStream 实例包装了SocketgetInputStream 返回的java.io.InputStream 实例。SocketInputStream 类提供了两个重要方法:readRequestLinereadHeaderreadRequestLine 返回HTTP 请求的第一行,即包括URIHTTP 方法(method )和HTTP 版本的那一行。处理套接字输入流中的字节流就意味着,从第一个字节读取到最后一个字节(从不回退),因此readRequestLine 必须只能被调用一次,而且必须在readHeader 方法之前调用。每调用一次readHeader 就可以读取一个header 名 /值对,而且应该重复调用直到所有的headers 都被读取。readRequestLine 的返回值是一个HttpRequestLine 实例,readHeader 的返回值是一个HttpHeader 对象。我们将在下面讨论HttpRequestLineHttpHeader

    HttpProcessor 对象负责创建HttpRequest 实例,因此必须填充HttpRequest 实例的每个成员变量。HttpProcess 类使用它的parse 方法来解析HTTP 请求的request lineheadersparse 方法的返回被赋值给HttpProcessor 对象的成员变量。但是,parse 方法并不解析query stringrequest body 中的请求参数。这个任务留给了HttpRequest 对象自己(译者注:这就是延迟解析)。只有servlet 需要一个参数时,query stirngrequest body 才会被解析。

    在前一章基础上的另一个改进,就是引入了启动类ex03.pyrmont.startup.Bootstrap 来启动整个应用。

    我们将在下面这些小节中,详细解释本章的应用:

  • 启动应用
  • 连接器
  • 创建HttpRequest 对象
  • 创建HttpResponse 对象
  • 静态资源处理器和serlvet 处理器
  • 运行应用

启动应用

   我们从ex03.pyrmont.startup.Bootstrap 类启动整个应用。Listing 3.1 列出了该类的代码。

Listing 3.1: The Bootstrap class   

Java代码 复制代码
  1. package ex03.pyrmont.startup;   
  2. import ex03.pyrmont.connector.http.HttpConnector;   
  3.     
  4. public final class Bootstrap {   
  5.    public static void main(String[] args) {   
  6.      HttpConnector connector = new HttpConnector();   
  7.      connector.start();   
  8.    }   
  9. }   
package ex03.pyrmont.startup;
import ex03.pyrmont.connector.http.HttpConnector;
 
public final class Bootstrap {
   public static void main(String[] args) {
     HttpConnector connector = new HttpConnector();
     connector.start();
   }
} 

 

    Bootstrap 类的main 方法创建了一个HttpConnector 实例,并调用了它的start 方法。Listing 3.2 列出了HttpConnector 类的代码。

Listing 3.2: The HttpConnector class's start method   
 

Java代码 复制代码
  1. package ex03.pyrmont.connector.http;   
  2.     
  3. import java.io.IOException;   
  4. import java.net.InetAddress;   
  5. import java.net.ServerSocket;   
  6. import java.net.Socket;   
  7.     
  8. public class HttpConnector implements Runnable {   
  9.    boolean stopped;   
  10.    private String scheme = "http";   
  11.     
  12.    public String getScheme() {   
  13.      return scheme;   
  14.    }   
  15.     
  16.    public void run() {   
  17.      ServerSocket serverSocket = null;   
  18.      int port = 8080;   
  19.      try {   
  20.         serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));   
  21.      }   
  22.      catch (IOException e) {   
  23.        e.printStackTrace();   
  24.        System.exit(1);   
  25.      }   
  26.      while (!stopped) {   
  27.        // Accept the next incoming connection from the server socket   
  28.        Socket socket = null;   
  29.        try {   
  30.          socket = serverSocket.accept();   
  31.        }   
  32.     
  33.        catch (Exception e) {   
  34.          continue;   
  35.        }   
  36.        // Hand this socket off to an HttpProcessor   
  37.        HttpProcessor processor = new HttpProcessor(this);   
  38.        processor.process(socket);   
  39.      }   
  40.    }   
  41.     
  42.    public void start() {   
  43.      Thread thread = new Thread(this);   
  44.      thread.start ();   
  45.    }   
  46. }   
package ex03.pyrmont.connector.http;
 
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
 
public class HttpConnector implements Runnable {
   boolean stopped;
   private String scheme = "http";
 
   public String getScheme() {
     return scheme;
   }
 
   public void run() {
     ServerSocket serverSocket = null;
     int port = 8080;
     try {
        serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
     }
     catch (IOException e) {
       e.printStackTrace();
       System.exit(1);
     }
     while (!stopped) {
       // Accept the next incoming connection from the server socket
       Socket socket = null;
       try {
         socket = serverSocket.accept();
       }
 
       catch (Exception e) {
         continue;
       }
       // Hand this socket off to an HttpProcessor
       HttpProcessor processor = new HttpProcessor(this);
       processor.process(socket);
     }
   }
 
   public void start() {
     Thread thread = new Thread(this);
     thread.start ();
   }
} 

连接器

    ex03.pyrmont.connector.http.HttpConnector 类代表了连接器 ,其职责是创建等待HTTP 请求的服务器套接字。Listing 3.2 给出了该类的代码。

    HttpConnector 类实现了java.lang.Runnable 接口,因此它可以被自己的线程使用。当启动应用时,HttpConnector 的一个实例被创建,并执行其run 方法。

    提示:你可以阅读文章“ Working with Threads ”来回忆如何创建 Java 线程。

    run 方法包括了一个while 循环,用来做下面的事情:

  • 等待HTTP 请求
  • 为每个请求创建HttpProcessor 实例
  • 调用HttpProcessorprocess 方法

 

    提示: run 方法和第 2 章中 HttpServer1 类的 await 方法是相同的。

    马上你就能看到,HttpConnector 类和ex02.pyrmont.HttpServer1 类非常相似。从java.net.ServerSocket 类的accept 方法获得一个socket 之后发生了变化,HttpConnector 类创建了一个HttpProcessor 实例,并以socket 为参数调用其process 方法。

    提示: HttpConnector 类拥有另一个名为 getSchema 的方法,该方法返回网络请求的 schema HTTP )。

    HttpProcessor 类的process 方法接受HTTP 请求的socket 为参数。对于每个HTTP 请求,process 方法会做如下处理:
  1. 创建一个HttpRequest 对象
  2. 创建一个HttpResponse 对象
  3. 解析HTTP 请求的第一行和headers ,并填充HttpRequest 对象
  4. 传递HttpRequest 对象和HttpResponse 对象给ServletProcessorStaticResourceProcessor

    就像第2 章里那样,ServletProcessor 调用了被请求的servletservice 方法,StaticResourceProcessor 发送静态资源的内容(给客户端)。

    Listing 3.3 列出了process 方法的代码。

Listing 3.3: The HttpProcessor class's process method.   
 
Java代码 复制代码
  1. public void process(Socket socket) {   
  2.    SocketInputStream input = null;   
  3.    OutputStream output = null;   
  4.    try {   
  5.      input = new SocketInputStream(socket.getInputStream(), 2048);   
  6.      output = socket.getOutputStream();   
  7.     
  8.      // create HttpRequest object and parse   
  9.      request = new HttpRequest(input);   
  10.     
  11.       // create HttpResponse object   
  12.      response = new HttpResponse(output);   
  13.      response.setRequest(request);   
  14.      response.setHeader("Server""Pyrmont Servlet Container");   
  15.     
  16.      parseRequest(input, output);   
  17.      parseHeaders(input);   
  18.     
  19.      //check if this is a request for a servlet or a static resource   
  20.      //a request for a servlet begins with "/servlet/"   
  21.      if (request.getRequestURI().startsWith("/servlet/")) {   
  22.        ServletProcessor processor = new ServletProcessor();   
  23.        processor.process(request, response);   
  24.      }   
  25.      else {   
  26.        StaticResourceProcessor processor = new  
  27.          StaticResourceProcessor();   
  28.        processor.process(request, response);   
  29.      }   
  30.     
  31.      // Close the socket   
  32.      socket.close();   
  33.      // no shutdown for this application   
  34.    }   
  35.    catch (Exception e) {   
  36.      e.printStackTrace ();   
  37.    }   
  38. }   
public void process(Socket socket) {
   SocketInputStream input = null;
   OutputStream output = null;
   try {
     input = new SocketInputStream(socket.getInputStream(), 2048);
     output = socket.getOutputStream();
 
     // create HttpRequest object and parse
     request = new HttpRequest(input);
 
      // create HttpResponse object
     response = new HttpResponse(output);
     response.setRequest(request);
     response.setHeader("Server", "Pyrmont Servlet Container");
 
     parseRequest(input, output);
     parseHeaders(input);
 
     //check if this is a request for a servlet or a static resource
     //a request for a servlet begins with "/servlet/"
     if (request.getRequestURI().startsWith("/servlet/")) {
       ServletProcessor processor = new ServletProcessor();
       processor.process(request, response);
     }
     else {
       StaticResourceProcessor processor = new
         StaticResourceProcessor();
       processor.process(request, response);
     }
 
     // Close the socket
     socket.close();
     // no shutdown for this application
   }
   catch (Exception e) {
     e.printStackTrace ();
   }
} 
     
     process 方法首先从获取socket 的输入流和输出流。注意,该方法使用的SocketInputStream 继承自java.io.InputStream
Java代码 复制代码
  1. SocketInputStream input = null;   
  2.      OutputStream output = null;   
  3.      try {   
  4.        input = new SocketInputStream(socket.getInputStream(), 2048);   
  5.        output = socket.getOutputStream();   
  6.        // Then, it creates an HttpRequest instance and an HttpResponse instance and assigns   
  7.        // the HttpRequest to the HttpResponse.   
  8.        // create HttpRequest object and parse   
  9.        request = new HttpRequest(input);   
  10.        // create HttpResponse object   
  11.        response = new HttpResponse(output);   
  12.        response.setRequest(request);   
SocketInputStream input = null;
     OutputStream output = null;
     try {
       input = new SocketInputStream(socket.getInputStream(), 2048);
       output = socket.getOutputStream();
       // Then, it creates an HttpRequest instance and an HttpResponse instance and assigns
       // the HttpRequest to the HttpResponse.
       // create HttpRequest object and parse
       request = new HttpRequest(input);
       // create HttpResponse object
       response = new HttpResponse(output);
       response.setRequest(request); 

    本章应用的HttpResponse 类比第2 章的Response 类要复杂很多。举例来说,你可以通过调用HttpResponse 类的setHeader 方法向客户端发送headers
Java代码 复制代码
  1. response.setHeader("Server""Pyrmont Servlet Container");   
response.setHeader("Server", "Pyrmont Servlet Container"); 
     
    接下来,process 方法调用HttpProcessor 类的两个私有方法来解析请求。
Java代码 复制代码
  1. parseRequest(input, output);   
  2. parseHeaders (input);  
      parseRequest(input, output);
      parseHeaders (input);
 
    然后,process方法根据请求URI的模式(pattern),将HttpRequest对象和HttpResponse对像甩给(hand off ... to)一个 ServletProcessor对象和一个 StaticResourceProcessor对象。
Java代码 复制代码
  1. if (request.getRequestURI().startsWith("/servlet/")) {   
  2.          ServletProcessor processor = new ServletProcessor();   
  3.          processor.process(request, response);   
  4.        }   
  5.        else {   
  6.          StaticResourceProcessor processor =   
  7.             new StaticResourceProcessor();   
  8.          processor.process(request, response);   
  9.        }   
if (request.getRequestURI().startsWith("/servlet/")) {
         ServletProcessor processor = new ServletProcessor();
         processor.process(request, response);
       }
       else {
         StaticResourceProcessor processor =
            new StaticResourceProcessor();
         processor.process(request, response);
       } 
 
    最后,process 方法关闭socket
Java代码 复制代码
  1. socket.close();   
 socket.close(); 
     
    同样注意,HttpProcessor 类使用org.apache.catalina.util.StringManager 类来发送错误消息:
Java代码 复制代码
  1. protected StringManager sm = StringManager.getManager("ex03.pyrmont.connector.http");  
protected StringManager sm = StringManager.getManager("ex03.pyrmont.connector.http");
 
    HttpProcessor 类的私有方法——parseRequestparseHeadersnormalize —— 被调用来帮助填充HttpRequest 对象。在下一节“创建HttpRequest 对象”,我们将讨论这些方法。

创建HttpRequest 对象

    HttpRequest 类实现了javax.servlet.http.HttpServletRequest 接口。附带还有一个叫做HttpRequestFacade 的门面类。Figure 3.2 展现了HttpRequest 和相关类的类图。
    HttpRequest 类的许多方法都是留空的(等待第4章才会全部实现),但是servlet 程序员已经可以从HTTP 请求中获得headerscookies 和请求参数。这三种值被存储在下面的引用变量中:
Java代码 复制代码
  1. protected HashMap headers = new HashMap();   
  2. protected ArrayList cookies = new ArrayList();   
  3. protected ParameterMap parameters = null;   
   protected HashMap headers = new HashMap();
   protected ArrayList cookies = new ArrayList();
   protected ParameterMap parameters = null; 
 
    提示:我们会在“获取参数”小节解释 ParameterMap 类。

    因此,servlet 程序员可以从javax.servlet.http.HttpServletRequest 下面这些方法中获取正确的值:getCookiesgetDateHeadergetHeadergetHeaderNamesgetHeadersgetParametergetPrameterMapgetParameterNamesgetParameterValues 。正如你在HttpRequest类中看到的,一旦获得headerscookies 和请求参数,相关方法的实现就很简单了。

    不用说,这里主要的挑战就是解析HTTP请求和填充HttpRequest 对象。对于headerscookiesHttpRequest 类提供了addHeaderaddCookie 方法,
分享到:
评论

相关推荐

    How Tomcat Works 中文版.pdf

    《How Tomcat Works》中文版一书详细剖析了Tomcat服务器的内部工作机制。该书基于Tomcat 4.1.12和5.0.18两个版本,深入讲解了其servlet容器的架构和运作原理,尤其是代号为Catalina的核心组件。 Tomcat是一个开源的...

    How Tomcat Works 中文版+例程源码

    《How Tomcat Works》是一本深入探讨Apache Tomcat工作原理的书籍,中文版的提供使得国内开发者能够更方便地理解这一流行的开源Java Servlet容器。这本书不仅涵盖了Tomcat的基础知识,还详细解析了其内部机制,对于...

    How Tomcat Works【英文PDF+中文HTML+源码】.zip

    1. **架构概述**:Tomcat的架构基于服务器-客户端模型,主要由Catalina(Servlet容器)、 Coyote(HTTP/HTTPS连接器)和Jasper(JSP引擎)三个主要组件构成。Catalina处理Servlet,Coyote处理网络通信,Jasper编译和...

    HowTomcatWorks(书和源码)

    《How Tomcat Works》是一本深入解析Apache Tomcat工作原理的书籍,同时也包含了源码,为读者提供了理论与实践相结合的深入学习体验。Tomcat是一款广泛使用的开源Java Servlet容器,它是Apache软件基金会 Jakarta...

    译How Tomcat Works(第二章)

    《译How Tomcat Works(第二章)》这篇文章主要讲解了Apache Tomcat服务器的工作原理,它是一个开源的Java Servlet容器,广泛用于部署Web应用程序。在这一章中,我们将深入探讨Tomcat如何处理HTTP请求,以及其内部架构...

    How Tomcat Works 中文版

    《How Tomcat Works中文版》这本书是一本深入探讨Apache Tomcat服务器工作原理的专著。Apache Tomcat服务器,或简称为Tomcat,是世界上广泛使用的Java Servlet容器和JavaServer Pages(JSP)引擎,负责处理基于Java...

    How Tomcat Works中文

    《How Tomcat Works》是一本针对Apache Tomcat服务器内部工作机制进行深入剖析的专业书籍。本书详细介绍了Tomcat 4.1.12和5.0.18两个版本的内部结构与运作原理,尤其着重于解释Catalina——Tomcat的Servlet容器的...

    How Tomcat Works 英文书及源码

    总的来说,《How Tomcat Works》是一本深度解析Tomcat的权威指南,无论你是初学者还是经验丰富的开发者,都能从中获得宝贵的洞见。通过学习这本书,你将能够更有效地管理和维护你的Tomcat服务器,同时提高你的Web...

    How Tomcat Works 读书笔记(第三章)

    《How Tomcat Works》这本书是理解Apache Tomcat服务器工作原理的重要资源,第三章主要探讨了Tomcat的架构和核心组件。以下是对这部分内容的详细解读: Tomcat作为一款开源的Java Servlet容器,其核心功能是解析...

    How Tomcat works(PDF)

    《How Tomcat Works》这本书深入浅出地介绍了Apache Tomcat这款广泛应用的Java Servlet容器的工作原理。Tomcat作为开源软件,是许多Web应用的基础,尤其在轻量级开发和测试环境中非常常见。以下是对Tomcat核心知识点...

    HowTomcatWorks 中文版+源码.rar

    《HowTomcatWorks》是一本深入解析Apache Tomcat工作原理的书籍,中文版的发布使得更多的中国开发者能够理解和掌握这款广泛应用的开源Java Servlet容器的工作机制。Tomcat是Apache软件基金会Jakarta项目的一部分,它...

    how tomcat works中英文版

    《How Tomcat Works》是一本深入探讨Apache Tomcat工作原理的书籍,包含了中英文两个版本。这本书对于理解Java Servlet和JavaServer Pages(JSP)容器的运作方式具有极高的价值,特别是对于那些想要深入理解Web应用...

    how tomcat works

    《how tomcat works》是一本深入探讨Apache Tomcat内部工作原理的专业书籍。Apache Tomcat是一个开源的Java Servlet容器,它实现了Java Servlet和JavaServer Pages技术规范,提供了Java Web服务器的功能。对于Java ...

    How Tomcat Works 中文版/英文版 + 源码

    《How Tomcat Works》是一本深入解析Apache Tomcat服务器内部工作原理的重要参考资料,它提供了对Tomcat架构的全面理解,包括其设计、配置和优化。这本书的中文版和英文版都为读者提供了便利,无论你是母语为中文...

    How Tomcat Works以及案例的项目源码

    《How Tomcat Works》是一本深入探讨Apache Tomcat工作原理的专业书籍,对于任何希望深入了解Java Servlet和JavaServer Pages (JSP)容器的人来说,都是一份宝贵的资源。Tomcat作为最流行的开源Servlet容器,其内部...

Global site tag (gtag.js) - Google Analytics