Tomcat主要功能
Apache Tomcat 是一个开源的轻量级 Web 服务器软件,主要用于部署和运行 Java Servlet 和 JavaServer Pages (JSP) 应用程序。Tomcat 由 Apache 软件基金会维护,并且是根据 Sun Microsystems(现在是 Oracle)制定的 JavaEE 规范实现的。
Tomcat最初是由Sun Microsystems公司开发的,后来捐赠给了Apache软件基金会。它的名字来源于Tomcat这个吉祥物,也反映了它作为一个轻量级、灵活且强大的Web服务器的能力。
Tomcat的主要功能有:
- Servlet容器:Tomcat的核心是一个Servlet容器,它负责管理Servlet的生命周期,处理客户端请求,并将响应返回给客户端。
- JSP支持:Tomcat能够编译JSP文件为Servlet,并执行这些Servlet。
- Websocket支持:从Tomcat 7.0.5版本开始,支持Websocket协议,允许服务器和客户端之间进行全双工通信。
- 安全性:Tomcat提供了一系列的安全特性,包括SSL加密、登录认证、角色授权等。
- 可扩展性:Tomcat设计为模块化,可以通过添加或自定义组件来扩展其功能。
- 跨平台:由于基于 Java,因此可以在任何安装了 JDK 的平台上运行。
Tomcat作为Servlet容器,提供了运行Servlet的环境,而Servlet则是实现Web应用程序逻辑的核心组件。两者相辅相成,共同构成了Java Web开发的基础。
Tomcat架构
Tomcat 的主要组件包括 Catalina、Coyote、Jasper、Naming 和 Juli,这些组件共同构成了 Tomcat 的核心架构。
Catalina
Catalina 是 Tomcat 的 Servlet 容器,负责处理 Servlet 请求。当一个 HTTP 请求到达时,Catalina 将其映射到相应的 Servlet,通过调用 Servlet 的 service() 方法来处理请求并生成响应。Catalina 还负责管理 Servlet 的生命周期,包括初始化、启动、停止等操作。这种高度模块化的设计使得 Catalina 能够灵活地处理各种复杂的应用场景。
- Server:代表整个Catalina Servlet容器,可以包含多个Service。
- Service:是Server中的一个抽象,将一个或多个Connector与一个Engine关联起来,以处理请求。
- Connector:负责接收客户端请求并将其转发给Engine,同时将Engine的响应返回给客户端。可以配置多个Connector,以支持不同协议或端口。
- Engine:Tomcat中的请求处理器,可以包含多个Host。
- Host:代表虚拟主机,即一个域名对应一个Host,每个Host可以包含多个Web应用程序(Context)。
- Context:代表一个Web应用程序,是Servlet运行时的环境。
- Wrapper:代表单个Servlet,是Catalina中的最小处理单位。
Coyote
Coyote 是 Tomcat 的连接器组件,主要负责处理 HTTP 协议。它接收来自客户端的请求,并将请求转换成 Tomcat 能识别的 Request 对象,再传递给 Catalina 进行处理。同时,Coyote 还将处理完的 Response 对象转换回网络字节流,发送给客户端。Coyote 支持多种 I/O 模型和应用层协议,以适应不同的性能需求。
- 协议处理:支持多种协议,如HTTP/1.1、AJP(Apache JServ Protocol)等。
- 连接管理:管理客户端与服务器之间的连接,包括创建、维护和关闭连接。
- 请求处理:将收到的原始HTTP请求转换为可以被Servlet引擎理解的请求对象。
- 响应处理:将Servlet引擎生成的响应对象转换为HTTP响应,并发送给客户端。
Jasper
Jasper是Tomcat的JSP引擎,它负责将JSP文件转换为Servlet。以下是Jasper的工作流程:
- 编译:当浏览器请求一个JSP页面时,Jasper将JSP文件转换为Java源代码(.java文件)。
- 编译Java源代码:将生成的Java源代码编译成字节码(.class文件)。
- 加载和实例化:加载编译后的Servlet类,并创建其实例。
- 执行:执行Servlet的
service
方法来处理请求并生成响应。
Naming
Naming组件提供JNDI(Java Naming and Directory Interface)服务,允许Java应用程序通过名称查找和访问各种资源。在Tomcat中,Naming主要用于以下目的:
- 数据源:通过JNDI查找数据库连接池。
- 其他资源:如JavaMail会话、JMS队列等。
Juli
Juli是Tomcat的日志记录工具,它是基于Java Util Logging API的。Juli负责记录Tomcat的运行日志,包括系统信息、错误信息、警告信息等。
Tomcat自定义类加载器
类加载器的基础知识
在Java中,类加载器负责将类的字节码加载到Java虚拟机(JVM)中。Java默认提供了三种类型的类加载器:Bootstrap、Extension和System类加载器。
Bootstrap类加载器
- 职责:Bootstrap类加载器是JVM启动时创建的,负责加载Java核心库(
rt.jar
等),这些库位于JVM的lib
目录中。它通常是用C/C++编写的,是Java中最顶层的类加载器。 - 加载路径:它从
<JAVA_HOME>/jre/lib
或-Xbootclasspath
参数指定的路径中加载类。 - 特点:Bootstrap类加载器是JVM的一部分,它不是一个Java类,因此无法在Java代码中直接访问。
Extension类加载器
- 职责:Extension类加载器负责加载JVM扩展目录(
lib/ext
)中的类库。它是在Java中实现的,是Bootstrap类加载器的子加载器。 - 加载路径:它从
<JAVA_HOME>/jre/lib/ext
或java.ext.dirs
系统属性指定的目录中加载类。 - 特点:Extension类加载器是由
sun.misc.Launcher$ExtClassLoader
类实现的,是Java代码可以访问的。
System类加载器(Application类加载器)
- 职责:System类加载器,也称为Application类加载器,负责加载用户类路径(
ClassPath
)上的类库。它是Extension类加载器的子加载器。 - 加载路径:它从环境变量
CLASSPATH
或-classpath
或-cp
命令行参数指定的路径中加载类。 - 特点:System类加载器是由
sun.misc.Launcher$AppClassLoader
类实现的,是Java代码可以访问的。
类加载器的层次结构
这些类加载器构成了Java中的类加载器层次结构。当一个类需要被加载时,JVM会从顶层类加载器开始,依次尝试加载该类。如果某个类加载器无法找到该类,它会委托给它的父加载器尝试加载。这种机制称为“委派模型”。
类加载器的隔离性
每个类加载器都有其自己的命名空间,一个类由哪个类加载器加载,就在哪个类加载器的命名空间中。这提供了类加载的隔离性,防止了不同类库之间的冲突。
自定义类加载器的必要性
为了满足上述需求,Tomcat实现了自己的类加载器层次结构:
- Common类加载器:加载Tomcat内部和所有Web应用程序共享的类库。
- Catalina类加载器:加载Tomcat自身运行所需的类库。
- Shared类加载器:加载所有Web应用程序共享的类库,但不在Tomcat内部使用。
- WebApp类加载器:为每个Web应用程序创建一个实例,加载特定Web应用程序的类库。
类加载器的委派模型
Tomcat的类加载器遵循委派模型,但有一个重要的例外:WebApp类加载器首先尝试自己加载类,如果找不到,再委派给父类加载器。这与Java默认的委派模型不同,后者是先委派给父类加载器。
自定义类加载器的实现
Tomcat通过继承Java的ClassLoader
类来实现自定义类加载器。它重写了findClass
和loadClass
方法,以实现特定的类加载逻辑。例如,WebApp类加载器会首先在Web应用程序的类路径中查找类,如果找不到,再委托给父类加载器。
Java的ClassLoader
类
在Java中,ClassLoader
是一个抽象类,用于加载类的字节码到Java虚拟机(JVM)中。它提供了几个重要的方法,包括:
loadClass(String name)
: 加载一个类,它首先委托给父类加载器尝试加载,如果父类加载器无法加载,则自己尝试加载。findClass(String name)
: 在当前类加载器的类路径中查找并加载类。defineClass(byte[] b, int off, int len)
: 将字节数组转换为Class
对象。
Tomcat的自定义类加载器实现
Tomcat通过继承ClassLoader
类并重写其部分方法来实现自定义类加载器。以下是Tomcat中自定义类加载器的一些关键实现细节:
- 重写
loadClass
方法:Tomcat的自定义类加载器通常会重写loadClass
方法,以实现特定的类加载逻辑。例如,WebApp类加载器首先尝试自己加载类,如果找不到,再委派给父类加载器。这与Java默认的委派模型不同,后者是先委派给父类加载器。 - 重写
findClass
方法:在Tomcat的自定义类加载器中,findClass
方法通常被重写,以在特定的位置(如Web应用程序的类路径)中查找类。如果找到类,它将使用defineClass
方法将字节数组转换为Class
对象。 - 资源加载:除了类加载,Tomcat的自定义类加载器还负责加载资源(如属性文件、HTML文件等)。这通常是通过重写
getResource
和getResourceAsStream
方法实现的。 - 类加载器的委派模型:Tomcat的类加载器遵循委派模型,但有一个重要的例外:WebApp类加载器首先尝试自己加载类,如果找不到,再委派给父类加载器。这允许Web应用程序使用自己的类库版本,而不会影响Tomcat或其他Web应用程序。
Tomcat请求处理流程
1. 请求接收与解析
当客户端(如浏览器)向Tomcat服务器发送HTTP请求时,这个请求首先到达Tomcat的连接器(Connector)。连接器负责监听特定端口(如8080端口),接收来自客户端的请求,并将这些请求转换为Tomcat内部使用的Request
对象。此外,连接器还负责解析HTTP请求头和请求体,为后续处理做准备。
连接器的角色:
- 接收HTTP请求。
- 解析请求行、请求头和请求体。
- 将请求转换为Tomcat内部的
Request
对象。
2. 请求处理线程与协议解析
接收到的请求被交给一个线程池(Executor),这个线程池管理着一组线程,每个线程负责处理具体的请求。然后,**协议处理器(Processor)**会进一步解析HTTP请求,确保请求信息被正确理解和准备用于下一步处理。
3. 请求映射与容器选择
一旦请求被解析,接下来就是确定哪个组件应该处理该请求。这一步骤由Mapper完成,它根据请求的URL和Host信息来决定请求应由哪个Host
(虚拟主机)、Context
(Web应用)和Wrapper
(Servlet)处理。请求随后被传递给相应的容器进行处理。容器包括Engine
、Host
、Context
和Wrapper
,它们形成一个树状结构,用以组织和管理Web应用程序。
- Engine:顶层组件,负责处理请求并将其分发到合适的
Host
。 - Host:代表一个虚拟主机,允许在同一Tomcat实例上托管多个Web应用程序。
- Context:表示Web应用程序,负责管理应用程序的生命周期、配置和请求处理。
- Wrapper:封装了Servlet,是容器层次结构中最底层的元素。
4. 管道与阀门机制
在容器中,请求会通过一个称为管道(Pipeline)的路径,其中可以配置多个阀门(Valve)。每个阀门可以执行特定的逻辑,如日志记录、认证检查等。请求会按顺序通过这些阀门,直到到达基础阀门(Basic Valve),它负责实际的Servlet调用。这种设计使得请求处理过程既灵活又可扩展。
5. Servlet与过滤器链
请求最终到达Wrapper
容器,这里会调用对应的Servlet来处理请求。但在调用Servlet之前,Tomcat会先检查是否有配置的过滤器(Filter)。过滤器可以在请求到达Servlet之前对请求进行预处理,或在响应返回给客户端之前对响应进行后处理。过滤器链中的每个过滤器都可以对请求和响应进行操作,最后才调用目标Servlet的service
方法。Servlet根据请求的HTTP方法(如GET、POST)调用相应的方法。
6. 响应生成与返回
Servlet处理完请求后,会生成一个响应对象(HttpServletResponse
),并将其返回给Tomcat。Tomcat会将响应发送回客户端。在这个过程中,响应可能会再次经过管道中的各个阀门,以便它们有机会对响应做出最后的调整,例如添加额外的HTTP头部信息。
7. 请求结束与资源清理
请求处理完成后,Tomcat会清理相关资源,包括请求和响应对象,确保系统资源的有效利用。此时,整个请求-响应周期结束。