/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package javax.servlet;
import java.io.IOException;
/**
* Defines methods that all servlets must implement.
*
* <p>A servlet is a small Java program that runs within a Web server.
* Servlets receive and respond to requests from Web clients,
* usually across HTTP, the HyperText Transfer Protocol.
*
* <p>To implement this interface, you can write a generic servlet
* that extends
* <code>javax.servlet.GenericServlet</code> or an HTTP servlet that
* extends <code>javax.servlet.http.HttpServlet</code>.
*
* <p>This interface defines methods to initialize a servlet,
* to service requests, and to remove a servlet from the server.
* These are known as life-cycle methods and are called in the
* following sequence:
* <ol>
* <li>The servlet is constructed, then initialized with the <code>init</code> method.
* <li>Any calls from clients to the <code>service</code> method are handled.
* <li>The servlet is taken out of service, then destroyed with the
* <code>destroy</code> method, then garbage collected and finalized.
* </ol>
*
* <p>In addition to the life-cycle methods, this interface
* provides the <code>getServletConfig</code> method, which the servlet
* can use to get any startup information, and the <code>getServletInfo</code>
* method, which allows the servlet to return basic information about itself,
* such as author, version, and copyright.
*
* @author Various
* @version $Version$
*
* @see GenericServlet
* @see javax.servlet.http.HttpServlet
*
*/
public interface Servlet {
/**
* Called by the servlet container to indicate to a servlet that the
* servlet is being placed into service.
*
* <p>The servlet container calls the <code>init</code>
* method exactly once after instantiating the servlet.
* The <code>init</code> method must complete successfully
* before the servlet can receive any requests.
*
* <p>The servlet container cannot place the servlet into service
* if the <code>init</code> method
* <ol>
* <li>Throws a <code>ServletException</code>
* <li>Does not return within a time period defined by the Web server
* </ol>
*
*
* @param config a <code>ServletConfig</code> object
* containing the servlet's
* configuration and initialization parameters
*
* @exception ServletException if an exception has occurred that
* interferes with the servlet's normal
* operation
*
* @see UnavailableException
* @see #getServletConfig
*
*/
public void init(ServletConfig config) throws ServletException;
/**
*
* Returns a {@link ServletConfig} object, which contains
* initialization and startup parameters for this servlet.
* The <code>ServletConfig</code> object returned is the one
* passed to the <code>init</code> method.
*
* <p>Implementations of this interface are responsible for storing the
* <code>ServletConfig</code> object so that this
* method can return it. The {@link GenericServlet}
* class, which implements this interface, already does this.
*
* @return the <code>ServletConfig</code> object
* that initializes this servlet
*
* @see #init
*
*/
public ServletConfig getServletConfig();
/**
* Called by the servlet container to allow the servlet to respond to
* a request.
*
* <p>This method is only called after the servlet's <code>init()</code>
* method has completed successfully.
*
* <p> The status code of the response always should be set for a servlet
* that throws or sends an error.
*
*
* <p>Servlets typically run inside multithreaded servlet containers
* that can handle multiple requests concurrently. Developers must
* be aware to synchronize access to any shared resources such as files,
* network connections, and as well as the servlet's class and instance
* variables.
* More information on multithreaded programming in Java is available in
* <a href="http://java.sun.com/Series/Tutorial/java/threads/multithreaded.html">
* the Java tutorial on multi-threaded programming</a>.
*
*
* @param req the <code>ServletRequest</code> object that contains
* the client's request
*
* @param res the <code>ServletResponse</code> object that contains
* the servlet's response
*
* @exception ServletException if an exception occurs that interferes
* with the servlet's normal operation
*
* @exception IOException if an input or output exception occurs
*
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
/**
* Returns information about the servlet, such
* as author, version, and copyright.
*
* <p>The string that this method returns should
* be plain text and not markup of any kind (such as HTML, XML,
* etc.).
*
* @return a <code>String</code> containing servlet information
*
*/
public String getServletInfo();
/**
*
* Called by the servlet container to indicate to a servlet that the
* servlet is being taken out of service. This method is
* only called once all threads within the servlet's
* <code>service</code> method have exited or after a timeout
* period has passed. After the servlet container calls this
* method, it will not call the <code>service</code> method again
* on this servlet.
*
* <p>This method gives the servlet an opportunity
* to clean up any resources that are being held (for example, memory,
* file handles, threads) and make sure that any persistent state is
* synchronized with the servlet's current state in memory.
*
*/
public void destroy();
}
在Servlet接口中声明了5个方法里,init(),service(),destory()方法是与servlet的生命周期相关的方法,当实例化某个servlet类后,Servlet容器会调用其init()方法进行初始化,至于Servlet容器是如何调用,后面会分析。后面会被类与类之间的衔接来做一定的分析。在servlet接收任何请求之前,必须是经过正确初始化的。一般情况下init()方法可以留空。
当servlet的一个客户端请求到达后,servlet容器就调用相应的servlet的service()方法,并将javax.serlvet.servletRequest对象和javax.servlet.servletResponse对象作为参数传入,ServletResquest对象包含客户端的HTTP请求的信息,ServletResponse对象则封装servlet的请求信息。这两个类的设计十分有趣,在后面就具体分析这两个类。在servlet对象的整个生命周期内,service()方法会被多次调用。
在将servlet实例从服务中移除前,servlet容器会调用servlet实例的destory()方法,一般当Servlet容器关闭或servlet容器要释放内存时,才会将servlet实例移除,而且只有当servlet实例的service()方法中的线程都退出或执行超时后,才会调用destory()方法,当servlet容器调用了某个servlet实例的destory()方法后,它就不再调用该servlet实例的service()方法了,调用destory()方法让servlet对象有机会去清理自身持有的资源,如内存,文件句柄和线程等,确保有所的持久化与内存中该servlet对象的当前状态同步。
下面<how tomcat work>中的一个servlet类:
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
public class PrimitiveServlet implements Servlet {
public void init(ServletConfig config) throws ServletException {
System.out.println("init");
}
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
System.out.println("from service");
PrintWriter out = response.getWriter();
out.println("Hello. Roses are red.");
out.print("Violets are blue.");
}
public void destroy() {
System.out.println("destroy");
}
public String getServletInfo() {
return null;
}
public ServletConfig getServletConfig() {
return null;
}
}
一个功能齐全的servlet容器有以下几件事要做:
1,当第一次调用某个servlet,要载入该servlet类,并调用其init()方法(仅此一次);
2,针对每个request请求,创建一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例;
3,调用该servlet的service()方法,将servletRequest对象和servletResponse对象作为参数传入;
4,当关闭该servlet类时,调用其destory()方法,并卸载该servlet类。
那么从客户连接到tomcat上来,最重要要做的事就是如何处理客户的请求并且把请求传递给Servlet容器,虽然看起来很简单,但是这整个过程确是如此的繁杂,包括字符串的解析,以及cookies,session,参数请求等等的工作。那么我们首先的工作就是分析客户连接到tomcat上来这一块,也就是tomcat的默认连接器。
Tomcat的连接器必须实现org.apache.catalina.Connecor接口,在接口中声明了很多方法,其中最重要的是getContainer(),setContainer(),createRequest(),createResponse()方法。为什么这么说呢?看看以下分析:
setContainer()方法用于将连接器和某个servlet容器相关联。如果不跟特定的servlet容器关联起来,我靠,你准备要把包装好的HttpRequest和HttpResponse对象传给谁阿??
getContainer()方法返回与当前连接器相关联的servlet容器。createRequest()方法会引入的HTTP请求创建request对象,相应的,createResponse()方法会创建一个response对象。
org.apache.catalina.connector.http.HttpConnector类实现了Connector接口,首先大概说以下HttpConnector类做的一些工作:
1,首先它实现了org.apache.catalina.Connector接口(使其可以成为Catalina中的连接器),又实现了java.lang.Runnble接口(确保它的实例在自己的线程中运行)和实现了org.apache.catalina.Lifecycle接口,Lifecycle接口用于维护每个实现了该接口的每个Catalina组件的生命周期。但是这个接口目前不是重点,在后面会讲到。
2,由于HttpConnector实现了Lifecycle接口,因此当创建一个HttpConnector实例后,就应该调用其initialize()方法和start()方法,这两个方法如何调用在后面会详细讲到,现在就记住一点:这两个方法只应该被调用一次。
3,HttpConnector类还负责创建服务器套接字,这是最重要的一点,没有这个,啥事都不用做了。HttpConnector类的initialize()方法会调用一个私有方法open(),后者返回一个java.net.ServerSocket实例,赋值给成员变量serverSocket。但是,这里没有直接调用ServerSocket类的构造函数,而是通过open()方法从一个服务器套接字工厂得到一个实例。
4,维护HttpProcessor实例:HttpProcessor实例是用来处理HTTP请求的,在Tomcat的默认连接器中,HttpConnector实例有一个HttpProcessor对象池,每个HttpProcessor实例都运行在其自己的线程中。这样HttpConnector实例就可以同时处理多个HTTP请求了。
好了,接下来就是具体看看它们这几个步骤中tomcat源码的实现:
首先我们从连接用户开始入手:
org.apache.catalina.connector.http.HttpConnector
/**
* The shutdown signal to our background thread
*/
private boolean stopped = false;
/**
* Use TCP no delay ?
*/
private boolean tcpNoDelay = true;
/**
* Timeout value on the incoming connection.
* Note : a value of 0 means no timeout.
*/
private int connectionTimeout = Constants.DEFAULT_CONNECTION_TIMEOUT;
/**
* Has this component been started yet?
*/
private boolean started = false;
/**
* The thread synchronization object.
*/
private Object threadSync = new Object();
//该方法运行在一个线程中
// ---------------------------------------------- Background Thread Methods
/**
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
*/
public void run() {
// Loop until we receive a shutdown command
//如果tomcat接收到shutdown的命令,那么将会关闭连接器
while (!stopped) {
// Accept the next incoming connection from the server socket
Socket socket = null;
try {
// if (debug >= 3)
// log("run: Waiting on serverSocket.accept()");
socket = serverSocket.accept();//服务器等待用户的连接
// if (debug >= 3)
// log("run: Returned from serverSocket.accept()");
if (connectionTimeout > 0)
socket.setSoTimeout(connectionTimeout);//设置超时时间
socket.setTcpNoDelay(tcpNoDelay);//启用/禁用 TCP_NODELAY
} catch (AccessControlException ace) {
log("socket accept security exception", ace);
continue;
} catch (IOException e) {
// if (debug >= 3)
// log("run: Accept returned IOException", e);
try {
// If reopening fails, exit
synchronized (threadSync) {
if (started && !stopped)//如何其他组件还在运行或者socket还没关闭
log("accept error: ", e);
if (!stopped) {//如果socket还没关闭
// if (debug >= 3)
// log("run: Closing server socket");
serverSocket.close();//关闭服务器
// if (debug >= 3)
// log("run: Reopening server socket");
serverSocket = open();//创建新的serverSocket对象
}
}
// if (debug >= 3)
// log("run: IOException processing completed");
} catch (IOException ioe) {
log("socket reopen, io problem: ", ioe);
break;
} catch (KeyStoreException kse) {
log("socket reopen, keystore problem: ", kse);
break;
} catch (NoSuchAlgorithmException nsae) {
log("socket reopen, keystore algorithm problem: ", nsae);
break;
} catch (CertificateException ce) {
log("socket reopen, certificate problem: ", ce);
break;
} catch (UnrecoverableKeyException uke) {
log("socket reopen, unrecoverable key: ", uke);
break;
} catch (KeyManagementException kme) {
log("socket reopen, key management problem: ", kme);
break;
}
continue;
}
// Hand this socket off to an appropriate processor
HttpProcessor processor = createProcessor();//从HttpProcessor获得一个HttpProcessor对象或者是当池中没有HttpProcessor对象。
if (processor == null) {
try {
log(sm.getString("httpConnector.noProcessor"));
socket.close();//关闭socket
} catch (IOException e) {
;
}
continue;
}
// if (debug >= 3)
// log("run: Assigning socket to processor " + processor);
processor.assign(socket);//处理HTTP请求
// The processor will recycle itself when it finishes
}
// Notify the threadStop() method that we have shut ourselves down
// if (debug >= 3)
// log("run: Notifying threadStop() that we have shut down");
synchronized (threadSync) {
threadSync.notifyAll();//唤醒有所的线程
}
}
这段代码衍生出来的需要注意的东西有很多,但是先说一点,上面的log()方法是日志记录,这里暂时不说它,后面会详细的说它。好了,现在要从这段代码开始来把握几点需要注意的地方。有些地方需要加上UML来进行解释会比较好!
第一点需要关注的地方:serverSocket = open();我们来看看open()方法做了些什么事情先。首先它是一个私有的方法,也就是用户不能去重写它的方法。
org.apache.catalina.connector.http.HttpConnector
/**
* The IP address on which to bind, if any. If <code>null</code>, all
* addresses on the server will be bound.
*/
private String address = null;
/**
* The port number on which we listen for HTTP requests.
*/
private int port = 8080;
/**
* The accept count for this Connector.
*/
private int acceptCount = 10;
private ServerSocket open()
throws IOException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException,
KeyManagementException
{
// Acquire the server socket factory for this Connector
ServerSocketFactory factory = getFactory();利用工厂方法来创建一个套接字
// If no address is specified, open a connection on all addresses
if (address == null) {
log(sm.getString("httpConnector.allAddresses"));
try {
return (factory.createSocket(port, acceptCount));//返回创建好了的套接字
} catch (BindException be) {
throw new BindException(be.getMessage() + ":" + port);
}
}
// Open a server socket on the specified address
try {
InetAddress is = InetAddress.getByName(address);//在给定主机名的情况下确定主机的IP地址
log(sm.getString("httpConnector.anAddress", address));
try {
return (factory.createSocket(port, acceptCount, is));
} catch (BindException be) {
throw new BindException(be.getMessage() + ":" + address +
":" + port);
}
} catch (Exception e) {
log(sm.getString("httpConnector.noAddress", address));
try {
return (factory.createSocket(port, acceptCount));
} catch (BindException be) {
throw new BindException(be.getMessage() + ":" + port);
}
}
}
看看open()方法:很简单,不多说。主要要加索,保证线程安全
/**
* Return the server socket factory used by this Container.
*/
public ServerSocketFactory getFactory() {
if (this.factory == null) {
synchronized (this) {
this.factory = new DefaultServerSocketFactory();
}
}
return (this.factory);
}
那么从上面的代码用到了工厂设计模式,具体继续跟踪源码的实现:
apache.catalina.net.ServerSocketFactory,该类是一个接口,提供了三个方法:
其实也就是java.net.ServerSocket的三个方法构造,只不过换了以下名字调用而已,它的子类DefaultServerSocketFactory实现了这个接口,并且这个类是fianl类,不可以被集成。具体看看这个类的实现:
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.net;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.KeyManagementException;
import java.security.cert.CertificateException;
import org.apache.catalina.net.ServerSocketFactory;
/**
* Default server socket factory, which returns unadorned server sockts.
*
* @author db@eng.sun.com
* @author Harish Prabandham
* @author Craig R. McClanahan
*/
public final class DefaultServerSocketFactory implements ServerSocketFactory {
// --------------------------------------------------------- Public Methods
/**
* Returns a server socket which uses all network interfaces on
* the host, and is bound to a the specified port. The socket is
* configured with the socket options (such as accept timeout)
* given to this factory.
*
* @param port the port to listen to
*
* @exception IOException input/output or network error
* @exception KeyStoreException error instantiating the
* KeyStore from file (SSL only)
* @exception NoSuchAlgorithmException KeyStore algorithm unsupported
* by current provider (SSL only)
* @exception CertificateException general certificate error (SSL only)
* @exception UnrecoverableKeyException internal KeyStore problem with
* the certificate (SSL only)
* @exception KeyManagementException problem in the key management
* layer (SSL only)
*/
public ServerSocket createSocket (int port)
throws IOException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException,
KeyManagementException {
return (new ServerSocket(port));
}
/**
* Returns a server socket which uses all network interfaces on
* the host, is bound to a the specified port, and uses the
* specified connection backlog. The socket is configured with
* the socket options (such as accept timeout) given to this factory.
*
* @param port the port to listen to
* @param backlog how many connections are queued
*
* @exception IOException input/output or network error
* @exception KeyStoreException error instantiating the
* KeyStore from file (SSL only)
* @exception NoSuchAlgorithmException KeyStore algorithm unsupported
* by current provider (SSL only)
* @exception CertificateException general certificate error (SSL only)
* @exception UnrecoverableKeyException internal KeyStore problem with
* the certificate (SSL only)
* @exception KeyManagementException problem in the key management
* layer (SSL only)
*/
public ServerSocket createSocket (int port, int backlog)
throws IOException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException,
KeyManagementException {
return (new ServerSocket(port, backlog));
}
/**
* Returns a server socket which uses only the specified network
* interface on the local host, is bound to a the specified port,
* and uses the specified connection backlog. The socket is configured
* with the socket options (such as accept timeout) given to this factory.
*
* @param port the port to listen to
* @param backlog how many connections are queued
* @param ifAddress the network interface address to use
*
* @exception IOException input/output or network error
* @exception KeyStoreException error instantiating the
* KeyStore from file (SSL only)
* @exception NoSuchAlgorithmException KeyStore algorithm unsupported
* by current provider (SSL only)
* @exception CertificateException general certificate error (SSL only)
* @exception UnrecoverableKeyException internal KeyStore problem with
* the certificate (SSL only)
* @exception KeyManagementException problem in the key management
* layer (SSL only)
*/
public ServerSocket createSocket (int port, int backlog,
InetAddress ifAddress)
throws IOException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException,
KeyManagementException {
return (new ServerSocket(port, backlog, ifAddress));
}
是吧,是java.nio.ServerSocket类的三个构造方法吧。port端口号,backlog可接收的队列长度,ifAddress要将服务器绑定到的InetAddress
相关推荐
《Spring源码分析》这份资料深入探讨了Spring框架的核心机制,尤其聚焦于Spring5版本。...Tom老师的这份资料无疑是一份宝贵的资源,它将引导我们逐步揭开Spring的奥秘,让我们在编程旅程中更进一步。
总的来说,"会说话的Tom猫"的源码是一个很好的Android音频处理和游戏开发的实践案例。通过研究和分析,开发者可以深入理解Android音频API的使用、UI交互设计以及多媒体资源的管理,进一步提升自己的开发技能。
易语言TOM猫动画易语言源码.rar 易语言TOM猫动画易语言源码.rar 易语言TOM猫动画易语言源码.rar 易语言TOM猫动画易语言源码.rar 易语言TOM猫动画易语言源码.rar 易语言TOM猫动画易语言源码.rar
《Spring5 源码分析(第 2 版)》是某Tom老师精心编写的深度解析文档,旨在帮助读者全面理解Spring5的核心机制和设计理念。Spring作为Java领域最为广泛应用的框架之一,其源码的深入理解对于开发者来说至关重要。这篇...
在这个压缩包中,包含了四个基于Scratch的小游戏源码,分别是“方块跑酷1.0.sb2”、“管道Tom.sb2”、“归家弹球.sb2”以及“飞机大战1.0.sb2”。这些源码是学习和理解Scratch编程逻辑的绝佳资源。 1. **Scratch...
4. **JDBC 支持**:Spring 提供了一个简单的 JDBC 抽象层,减少了直接使用 JDBC 的繁琐,同时支持与第三方持久层框架(如 Hibernate、JPA)的集成。 5. **Java EE 整合**:Spring 可以很好地与 Java EE 技术(如 ...
易语言POST注册Tom邮箱源码.rar
//序列帧动画 播放一组图片 //指定动画图片的数组 NSMutableArray *arrayM = [NSMutableArray array]; //添加动画播放的素材 for (int i = 0; i; i++) { NSString *imageName = [NSString stringWithFormat:@...
"TOM猫动画"源码示例是用易语言编写的,可能是实现了一个类似经典手机应用"会说话的Tom猫"的动画效果。在这个项目中,开发者可能利用了易语言的基础语法、图形用户界面(GUI)设计、事件驱动编程等技术来创建TOM猫的...
易语言POST注册Tom邮箱源码.zip易语言项目例子源码下载易语言POST注册Tom邮箱源码.zip易语言项目例子源码下载 1.合个人学习技术做项目参考 2.适合学生做毕业设计参考 3.适合小团队开发项目参考
### Tom Spring5源码分析 #### 一、Spring框架中常用的设计模式 ##### 1. 设计模式概览 在软件工程领域,设计模式是指针对某一类问题的最优解决方案。通常所说的23种经典设计模式涵盖了面向对象设计的各个方面,...
本篇文章将针对“Tom_深入分析Spring源码doc”中的关键知识点进行详细的阐述。 1. **依赖注入(Dependency Injection,DI)** Spring的核心特性之一就是依赖注入,它使得组件间的依赖关系由Spring容器管理,而不是...
【标题】"TOM舞曲网(DIV+CSS) -ASP源码.zip"指的是一个基于ASP编程语言构建的网站源代码,它采用了现代网页设计技术DIV+CSS进行布局。这个源码可能是为一个名为"TOM舞曲网"的在线音乐平台设计的,允许用户浏览、播放...
scratch2源码管道Tom提取方式是百度网盘分享地址
【H5游戏源码 Tom猫.zip】是一款基于HTML5技术开发的游戏源代码,它提供了创建互动体验的基础,尤其是移动端的轻量级娱乐应用。H5游戏源码是使用HTML、CSS和JavaScript等Web技术编写的,可在现代浏览器上运行,无需...
本模块是基于discuz程序开发的功能插件,UTF-8版本 新版特色功能: 1、增加了全部砍价活动列表; 2、增加了使用认证服务号自动获取用户信息的功能; 3、增加了允许开启关闭帮砍价必须关注授权认证服务号的功能,...
5、用户在转发助力时,同一用户多次转发、助力,只有记录一次; 6、后台可以修改用户转发和助力数据,商家和站长可以控制中奖排名(允许作弊,内定中奖); 7、用户报名参加后 >> 分享转发 >> 好友点击和转发该用户...
Spring框架是Java开发中的核心组件,它为应用程序提供了一个全面的基础设施,支持...通过分析源码,开发者还可以了解到如何设计和实现一个可扩展、可维护的框架,这对于提升自身的技术水平和解决问题的能力大有裨益。
在TomTom HOME软件中,登录您的账户,然后在主菜单的第一页选择“新增地图”。这里会显示出您已购买的所有地图数据。找到需要分割的地图,点击其下方的“更多信息”链接。 此时,TomTom HOME会显示地图的详细信息,...
《How Tomcat Works》中文版及源码的结合,为学习者提供了一条深入了解Tomcat的途径。通过阅读书籍,我们可以理解Tomcat的基本概念和工作流程;而源码分析则能帮助我们深入到细节,理解其设计思想和实现方式。对于...