- 浏览: 222689 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
Wangwei86609:
非常好的规则引擎框架,支持决策树和多线程运行规则https:/ ...
规则引擎 -
hzxlb910:
真详细,收藏哈
maven setting.xml配置说明 -
东方胜:
[b][/b]
脚本语言 Tcl -
345161974:
hyw520110 写道345161974 写道这个Visua ...
Visual Tcl Binary 完整版(完美中文支持) -
hyw520110:
345161974 写道这个Visual Tcl Binary ...
Visual Tcl Binary 完整版(完美中文支持)
译者 jarfield
博客 http://jarfield.iteye.com
概述
就像《简介》中介绍的,Catalina 中有两个主要模块:Connector (连接器)和Container (容器)。本章,你将编写一个连接器 来增强第2章的应用,该连接器 能够创建更好的Request 和Response 对象。符合Servlet 2.3 和2.4 规范的连接器 必须创建javax.servlet.http.HttpServletRequest 实例和javax.servlet.http.HttpServletResponse 实例,并将它们作为参数传递给servlet 的service 方法。第2章的Servlet 容器只能运行实现了javax.servlet.Servlet 接口的servlet ,并传递javax.servlet.ServletRequest 实例和javax.servlet.ServletResponse 实例给servlet 的service 方法。连接器 并不知道servlet 的类型(例如,是否实现了javax.servlet.Servlet 接口, 继承了javax.servlet.GenericServlet ,或继承了javax.servlet.http.HttpServlet ), 因此它必须始终提供HttpServletRequest 实例和HttpServletResponse 实例。
本章的应用程序中,连接器 解析HTTP 请求的headers ,使得servlet 可以获得headers 、cookies 、参数名/值,等等。我们也会完善第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.properties 和LocalStrings_ja.properties 。
当类需要在属性文件中查找错误消息时,它首先获取一个StringManager 实例。但是,同一个包中很多类都可能需要一个StringManager 实例,如果为每个需要错误消息的类对象创建一个StringManager 实例,则是对资源的浪费。因此StringManager 类被设计成,同一个包中所有类对象可以共享一个StringManager 实例。如果熟悉设计模式,你可能会猜到StringManager 是一个单例类(singleton class )。StringManager 类唯一的构造函数是私有的(private ),因此你不能使用new 关键字在类外部创建该类的实例。以包名为参数,调用StringManager 类的公开静态方法getManager ,就可以获得一个StringManager 实例。每个实例被存储在一个Hashtable 中,key 就是包的名称。
- 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;
- }
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 类,传递包名给StringManager 的getManager 方法:
StringManager sm = StringManager.getManager("ex03.pyrmont.connector.http");
在 ex03.pyrmont.connector.http 包中,你可以找到三个属性文件:LocalStrings.properties 、LocalStrings_es.properties 和LocalStrings_ja.properties 。StringManager 实例根据应用程序运行时所在机器的区域(local )来决定使用哪个文件。如果你打开LocalStrings.properties ,非注释的第一行应该是这样的:
httpConnector.alreadyInitialized=HTTP connector has already been initialized
要得到一条错误消息,你需要以错误码(error code )为参数调用StringManager 类的getString 方法。下面是该方法的多个重载之一:
public String getString(String key)
以“httpConnector.alreadyInitialized ” 为参数调用getString 方法,就会返回“HTTP connector has already been initialized ”。
应用程序
从本章开始,每章附带的应用程序被化分成模块。本章的应用包括三个模块:connector 、startup 和core 。
startup 模块只包括一个类:Bootstrap ,其作用是启动整个应用。connector 模块的类可以分成5个类别:
- connector 和它的支持(supporting )类(HttpConnector 和HttpProcessor )
- 代表HTTP 请求的类(HttpRequest )及其支持类
- 代表HTTP 响应的类(HttpResponse )及其支持类
- 门面(Facade )类(HttpRequestFacade 和HttpResponseFacade )
- Constant 类
core 模块包括两个类:ServletProcessor 和StaticResourceProcessor 。
Figure 3.1 是本应用的类图。为了让类图更具可读性,HttpRequest 和HttpResponse 相关的类都被省略了。我们后面讨论Request 和Response 对象时,会给出更加详细的类图。
我们把Figure 3.1 和Figure 2.1 做个比较。第2 章的HttpServer 类被拆分成两个类:HttpConnector 和HttpProcessor ,Request 类被HttpRequest 类替换,Response 类被HttpResponse 类替换。而且,本章的应用使用了更多其他的类。
第2章中的HttpServer 类负责等待HTTP 请求,创建请求对象和响应对象。本章应用中,等待HTTP 请求的任务交给了HttpConnector 实例,创建请求对象和响应对象的任务分配给了HttpProcessor 实例。
本章中,HTTP 请求对象由实现了javax.servlet.http.HttpServletRequest 接口的HttpRequest 类来代表。HttpRequest 对象被转型为HttpServletRequest 实例,并传递给servlet 的service 方法。因此,每个HttpRequest 实例必须拥有适当的域,以便servlet 使用它们。需要赋给HttpRequest 对象的值包括URI 、query string 、参数、cookies 和其他headers 等等。因为连接器 不知道servlet 需要哪些值,所以它 必须解析所有能够从HTTP 请求获得的值。但是,解析HTTP 请求会带来昂贵(开销巨大)的字符串操作和其他操作。如果只解析servlet 需要的值,那么就可能节省大量的CPU 周期。例如,如果servlet 不需要任何请求(也就是,不调用javax.servlet.http.HttpServletRequest 的getParameter 、getParameterMap 、 getParameterNames或getParameterValues 方法),连接器 就不需要从query string 或HTTP request body 中解析出请求参数。Tomcat 的默认连接器 (包括本章应用中的连接器 )尝试通过“直到真正需要时才解析请求参数”的方式来提高效率。Tomcat 的默认连接器 和我们的连接器 使用SocketInputStream 类从Socket 的InputStream 中读取字节流。SocketInputStream 实例包装了Socket 的getInputStream 返回的java.io.InputStream 实例。SocketInputStream 类提供了两个重要方法:readRequestLine 和readHeader 。readRequestLine 返回HTTP 请求的第一行,即包括URI 、HTTP 方法(method )和HTTP 版本的那一行。处理套接字输入流中的字节流就意味着,从第一个字节读取到最后一个字节(从不回退),因此readRequestLine 必须只能被调用一次,而且必须在readHeader 方法之前调用。每调用一次readHeader 就可以读取一个header 名 /值对,而且应该重复调用直到所有的headers 都被读取。readRequestLine 的返回值是一个HttpRequestLine 实例,readHeader 的返回值是一个HttpHeader 对象。我们将在下面讨论HttpRequestLine 和HttpHeader 。
HttpProcessor 对象负责创建HttpRequest 实例,因此必须填充HttpRequest 实例的每个成员变量。HttpProcess 类使用它的parse 方法来解析HTTP 请求的request line 和headers 。parse 方法的返回被赋值给HttpProcessor 对象的成员变量。但是,parse 方法并不解析query string 和request body 中的请求参数。这个任务留给了HttpRequest 对象自己(译者注:这就是延迟解析)。只有servlet 需要一个参数时,query stirng 或request body 才会被解析。
在前一章基础上的另一个改进,就是引入了启动类ex03.pyrmont.startup.Bootstrap 来启动整个应用。
我们将在下面这些小节中,详细解释本章的应用:
- 启动应用
- 连接器
- 创建HttpRequest 对象
- 创建HttpResponse 对象
- 静态资源处理器和serlvet 处理器
- 运行应用
启动应用
我们从ex03.pyrmont.startup.Bootstrap 类启动整个应用。Listing 3.1 列出了该类的代码。
Listing 3.1: The Bootstrap class
- 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();
- }
- }
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
- 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 ();
- }
- }
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 实例
- 调用HttpProcessor 的process 方法
马上你就能看到,HttpConnector 类和ex02.pyrmont.HttpServer1 类非常相似。从java.net.ServerSocket 类的accept 方法获得一个socket 之后发生了变化,HttpConnector 类创建了一个HttpProcessor 实例,并以socket 为参数调用其process 方法。
提示: HttpConnector 类拥有另一个名为 getSchema 的方法,该方法返回网络请求的 schema ( HTTP )。
HttpProcessor 类的process 方法接受HTTP 请求的socket 为参数。对于每个HTTP 请求,process 方法会做如下处理:
- 创建一个HttpRequest 对象
- 创建一个HttpResponse 对象
- 解析HTTP 请求的第一行和headers ,并填充HttpRequest 对象
- 传递HttpRequest 对象和HttpResponse 对象给ServletProcessor 或StaticResourceProcessor
就像第2 章里那样,ServletProcessor 调用了被请求的servlet 的service 方法,StaticResourceProcessor 发送静态资源的内容(给客户端)。
Listing 3.3 列出了process 方法的代码。
Listing 3.3: The HttpProcessor class's process method.
- 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 ();
- }
- }
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 (); } }
- 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);
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 。
response.setHeader("Server", "Pyrmont Servlet Container");
parseRequest(input, output); parseHeaders (input);
然后,process方法根据请求URI的模式(pattern),将HttpRequest对象和HttpResponse对像甩给(hand off ... to)一个 ServletProcessor对象和一个 StaticResourceProcessor对象。
- if (request.getRequestURI().startsWith("/servlet/")) {
- ServletProcessor processor = new ServletProcessor();
- processor.process(request, response);
- }
- else {
- StaticResourceProcessor processor =
- new StaticResourceProcessor();
- processor.process(request, response);
- }
if (request.getRequestURI().startsWith("/servlet/")) { ServletProcessor processor = new ServletProcessor(); processor.process(request, response); } else { StaticResourceProcessor processor = new StaticResourceProcessor(); processor.process(request, response); }
最后,process 方法关闭socket 。
socket.close();
protected StringManager sm = StringManager.getManager("ex03.pyrmont.connector.http");
HttpProcessor 类的私有方法——parseRequest 、parseHeaders 和normalize —— 被调用来帮助填充HttpRequest 对象。在下一节“创建HttpRequest 对象”,我们将讨论这些方法。
创建HttpRequest 对象
HttpRequest 类实现了javax.servlet.http.HttpServletRequest 接口。附带还有一个叫做HttpRequestFacade 的门面类。Figure 3.2 展现了HttpRequest 和相关类的类图。HttpRequest 类的许多方法都是留空的(等待第4章才会全部实现),但是servlet 程序员已经可以从HTTP 请求中获得headers 、cookies 和请求参数。这三种值被存储在下面的引用变量中:
- protected HashMap headers = new HashMap();
- protected ArrayList cookies = new ArrayList();
- protected ParameterMap parameters = null;
protected HashMap headers = new HashMap(); protected ArrayList cookies = new ArrayList(); protected ParameterMap parameters = null;
提示:我们会在“获取参数”小节解释 ParameterMap 类。
因此,servlet 程序员可以从javax.servlet.http.HttpServletRequest 下面这些方法中获取正确的值:getCookies 、getDateHeader 、getHeader 、getHeaderNames 、getHeaders 、getParameter 、getPrameterMap 、getParameterNames 和getParameterValues 。正如你在HttpRequest类中看到的,一旦获得headers 、cookies 和请求参数,相关方法的实现就很简单了。
不用说,这里主要的挑战就是解析HTTP请求和填充HttpRequest 对象。对于headers 和cookies ,HttpRequest 类提供了addHeader 和addCookie 方法,
发表评论
-
pushlet
2012-05-31 14:56 1180基于pushlet的文件监控系统的研究与实现 http ... -
@Transactional spring 配置事务
2012-04-25 11:15 2104@Transactional spring 配置事 ... -
Spring的组件自动扫描机制
2012-04-09 17:47 0Spring将所有的bean都纳入到IOC中创建、管理和维护。 ... -
struts&rest
2012-04-03 00:11 801深入浅出REST http://www.infoq. ... -
文件转码
2011-11-16 09:55 2035工程项目太多,各工程或各文件编码不统一时,可运行本工具类,把工 ... -
安装和使用SpringIDE-------III
2011-07-29 10:40 8722. 编写类文件 · ... -
安装和使用SpringIDE-------II
2011-07-29 10:39 690显示图表,如图: 发表于 @ 2006 ... -
安装和使用SpringIDE
2011-07-29 10:36 1138这篇文章谈谈如何安装与使用SpringIDE。作为辅助Sp ... -
使用AJDT简化AspectJ开发
2011-07-29 10:05 1068面向方面编程(AOP)可用来解决当今的 许多 应用需求 ... -
利用Apache的CLI来处理命令行
2011-05-16 17:02 992CLI是Jakarta Commons中的一个子类。如果你仅仅 ... -
CGlib简单介绍
2011-04-28 08:37 886CGlib概述:cglib(Code Generation L ... -
Java ClassLoader
2011-04-25 18:24 1021当Java编译器编译好.class ... -
Template模式与Strategy模式
2011-04-20 16:23 698template method模式和stra ... -
Ibatis读写CLOB数据
2011-03-21 14:21 1063转载:http://www.iteye.com/topic/7 ... -
轻松构建和运行多线程的单元测试
2011-03-18 22:09 1011背景 并行程序 并行程序是指控制计算机系统中两个或多个分别 ... -
Cairngorm3中文简介
2011-03-18 22:07 1036官方原文地址:http://opensource.adobe. ... -
ibator改造之返回数据库注释和数据库分页
2010-12-23 17:24 2267转载:http://www.iteye.com ... -
quatrz 任务监控管理 (2)
2010-10-28 23:28 1451在《Quartz 任务监控管理 (1)》http://www. ... -
Quartz任务监控管理 (1)
2010-10-28 23:27 1331转载:http://sundoctor.iteye.com/b ... -
Quartz 在 Spring 中如何动态配置时间
2010-10-28 23:25 1724转载: http://sundoctor.iteye.com ...
相关推荐
《How Tomcat Works》中文版一书详细剖析了Tomcat服务器的内部工作机制。该书基于Tomcat 4.1.12和5.0.18两个版本,深入讲解了其servlet容器的架构和运作原理,尤其是代号为Catalina的核心组件。 Tomcat是一个开源的...
《How Tomcat Works》是一本深入探讨Apache Tomcat工作原理的书籍,中文版的提供使得国内开发者能够更方便地理解这一流行的开源Java Servlet容器。这本书不仅涵盖了Tomcat的基础知识,还详细解析了其内部机制,对于...
1. **架构概述**:Tomcat的架构基于服务器-客户端模型,主要由Catalina(Servlet容器)、 Coyote(HTTP/HTTPS连接器)和Jasper(JSP引擎)三个主要组件构成。Catalina处理Servlet,Coyote处理网络通信,Jasper编译和...
《How Tomcat Works》是一本深入解析Apache Tomcat工作原理的书籍,同时也包含了源码,为读者提供了理论与实践相结合的深入学习体验。Tomcat是一款广泛使用的开源Java Servlet容器,它是Apache软件基金会 Jakarta...
《译How Tomcat Works(第二章)》这篇文章主要讲解了Apache Tomcat服务器的工作原理,它是一个开源的Java Servlet容器,广泛用于部署Web应用程序。在这一章中,我们将深入探讨Tomcat如何处理HTTP请求,以及其内部架构...
《How Tomcat Works中文版》这本书是一本深入探讨Apache Tomcat服务器工作原理的专著。Apache Tomcat服务器,或简称为Tomcat,是世界上广泛使用的Java Servlet容器和JavaServer Pages(JSP)引擎,负责处理基于Java...
《How Tomcat Works》是一本针对Apache Tomcat服务器内部工作机制进行深入剖析的专业书籍。本书详细介绍了Tomcat 4.1.12和5.0.18两个版本的内部结构与运作原理,尤其着重于解释Catalina——Tomcat的Servlet容器的...
总的来说,《How Tomcat Works》是一本深度解析Tomcat的权威指南,无论你是初学者还是经验丰富的开发者,都能从中获得宝贵的洞见。通过学习这本书,你将能够更有效地管理和维护你的Tomcat服务器,同时提高你的Web...
《How Tomcat Works》这本书是理解Apache Tomcat服务器工作原理的重要资源,第三章主要探讨了Tomcat的架构和核心组件。以下是对这部分内容的详细解读: Tomcat作为一款开源的Java Servlet容器,其核心功能是解析...
《How Tomcat Works》这本书深入浅出地介绍了Apache Tomcat这款广泛应用的Java Servlet容器的工作原理。Tomcat作为开源软件,是许多Web应用的基础,尤其在轻量级开发和测试环境中非常常见。以下是对Tomcat核心知识点...
《HowTomcatWorks》是一本深入解析Apache Tomcat工作原理的书籍,中文版的发布使得更多的中国开发者能够理解和掌握这款广泛应用的开源Java Servlet容器的工作机制。Tomcat是Apache软件基金会Jakarta项目的一部分,它...
《How Tomcat Works》是一本深入探讨Apache Tomcat工作原理的书籍,包含了中英文两个版本。这本书对于理解Java Servlet和JavaServer Pages(JSP)容器的运作方式具有极高的价值,特别是对于那些想要深入理解Web应用...
《how tomcat works》是一本深入探讨Apache Tomcat内部工作原理的专业书籍。Apache Tomcat是一个开源的Java Servlet容器,它实现了Java Servlet和JavaServer Pages技术规范,提供了Java Web服务器的功能。对于Java ...
《How Tomcat Works》是一本深入解析Apache Tomcat服务器内部工作原理的重要参考资料,它提供了对Tomcat架构的全面理解,包括其设计、配置和优化。这本书的中文版和英文版都为读者提供了便利,无论你是母语为中文...
《How Tomcat Works》是一本深入探讨Apache Tomcat工作原理的专业书籍,对于任何希望深入了解Java Servlet和JavaServer Pages (JSP)容器的人来说,都是一份宝贵的资源。Tomcat作为最流行的开源Servlet容器,其内部...