今天,我们一起学习一下Tomcat,本文内容主要参考与 Apache Tomcat 7 官方文档,以及Tomcat 7源码。内容主要分为一下五个方面:
一、什么是Tomcat?
Apache Tomcat 是一个Java Servlet、JavaServer Pages、Java Expression Language 和Java WebSocket技术的开源实现。
二、Tomcat安装和配置
1、下载Tomcat 7安装包,进入 Apache Tomcat 7 下载页面,选择适合你系统的版本,这里下载的是window版本。
2、下载成功后,解压。这里解压到了D:\Tomcat\apache-tomcat-7目录底下。
3、配置JAVA_HOME,在系统环境变量中新建JAVA_HOME环境变量并指定为系统JDK的安装目录。
4、点击\bin目录底下的startup.bat 可以看到Tomcat成功运行。
5、编写Servlet简单demo
1)、在\webapps 目录下创建HelloWeb目录,进入该目录,创建\classes和\lib目录,并新建web.xml文件。
2)、打开IDEA,创建maven工程,引入Servlet依赖:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
3)、构建HelloServlet类:
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.println("Hello World");
writer.close();
}
}
4)、在maven工具点击package,打包项目,并将生成的\target\classes拷贝到\HelloWeb\classes目录底下,将javax.servlet-api-3.1.0.jar拷贝到\HelloWeb\lib目录底下
5)、配置web.xml文件:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
6)、重新启动Tomcat 可以看到浏览器输出Hello World.
三、Tomcat的目录结构
1、\bin 里面含有启动tomcat所有要的jar包和脚本文件。
bootstrap.jar、commons-daemon.jar、tomcat-juli.jar这三个包是tomcat在启动时加载的类,其中,bootstrap.jar含有tomcat启动类,在catalina.bat启动脚本文件中, 我们可以看到如下脚本:
%_EXECJAVA% %LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
其中
%_EXECJAVA%代表JAVA命令
%LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" 代表JAVA 命令运行时的JVM参数和系统变量
-classpath 代表指定JAVA文件的路径
"%CLASSPATH%"代表D:\Tomcat\apache-tomcat-7\apache-tomcat-7.0.103\bin\bootstrap.jar.
rem Add on extra jar file to CLASSPATH
rem Note that there are no quotes as we do not want to introduce random
rem quotes into the CLASSPATH
if "%CLASSPATH%" == "" goto emptyClasspath
set "CLASSPATH=%CLASSPATH%;"
:emptyClasspath
set "CLASSPATH=%CLASSPATH%%CATALINA_HOME%\bin\bootstrap.jar"
%MAINCLASS%代表在"%CLASSPATH%"路径下的类,这里是
set MAINCLASS=org.apache.catalina.startup.Bootstrap
%CMD_LINE_ARGS%代表main函数的入参
%ACTION%代表最后一个入参,也就是Tomcat运行的动作,org.apache.catalina.startup.Bootstrap类会根据最后一个参数决定Tomcat的状态.下图是org.apache.catalina.startup.Bootstrap#main()方法
Tomcat的启动脚本简化来看是这样的:
java org.apache.catalina.startup.Bootstrap start
2、\conf包含Tomcat Web服务的配置文件,其中最重要的是server.xml文件:
<?xml version='1.0' encoding='utf-8'?>
<!-- 每一个Tomcat服务只有一个Server port是指TCP/IP端口,用于接收shutdown命令 shutdown是指shutdown命令的String值-->
<Server port="8005" shutdown="SHUTDOWN">
<!-- 代表Server的JNDI资源 所有的web项目都可以访问到 -->
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<!-- tomcat 服务 包含 一个Engine 和 至少一个 Connector -->
<Service name="Catalina">
<!-- 连接器 它确保Tomcat可以作为一个独立的web服务 主要包含ProtocolHandler 和 Adapter ProtocolHandler又包含EndPoint和Processor EndPoint主要用于接受请求 Processor 主要用于处理请求 Adapter 主要用于将处理过的请求交给具体的servlet处理 -->
<!-- port 连接器监听的端口号 -->
<!-- protocol 连接器使用的传输协议 -->
<!-- connectionTimeout 连接建立后 等待请求的时间 否则超时 单位是微秒 -1代表不设置超时时间-->
<!-- redirectPort 假如该连接器不支持SSL请求 那么它会将请求转发到该端口的连接器 -->
<!-- acceptCount 可接收的请求数目 超过该值 请求将阻塞 -->
<!-- maxThreads 连接器可以创造的用于处理请求的最大线程数目 -->
<!-- minSpareThreads 最小空闲处理请求的线程数目 -->
<!-- URIEncoding 用于解码URI字节的字符编码 -->
<Connector port="8080"
protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
acceptCount="100"
maxThreads="200"
minSpareThreads="10"
URIEncoding="ISO-8859-1" />
<!-- 引擎,用来管理多个站点,一个Service最多只能有一个Engine -->
<!-- name 引擎的名字 -->
<!-- localhost 默认Host 必须 用于处理没有匹配的请求 -->
<Engine name="Catalina" defaultHost="localhost">
<!-- 代表一个站点,也可以叫虚拟主机,通过配置Host就可以添加站点 -->
<!-- name 站点名称 跟站点域名对应 -->
<!-- appBase 应用的基础路径 默认是webapps -->
<!-- unpackWARs 是否自动解压war包 -->
<!-- autoDeploy 是否开启自动部署 -->
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- 代表一个应用程序,对应着平时开发的一套程序,或者一个WEB-INF目录以及下面的web.xml文件 -->
<!-- path 请求的路径 -->
<!-- docBase 应用的存放路径 HelloWeb -->
<Context path="HelloWeb" docBase="HelloWeb">
</Context >
</Host>
</Engine>
</Service>
</Server>
3、\lib目录主要存放tomcat的依赖类.
4、\webapps应用存放Servlet应用.
四、Tomcat的整体架构。
我们先通过一张图来整体的了解一下Tomcat。
我们来解释一下各个部分,Tomcat的核心部件主要是Connector和Container。
Connector:主要负责接受外部的网络链接,并将请求交给Container处理,主要组件由Connector,ProtocalHandler,EndPoint,Handler,Adapter。
Container:主要负责管理Servlet,处理业务逻辑。主要组件由Engine,Host,Context,Wrapper。
此外,Tomcat还提供了JMX(Java Management Extensions,即Java管理扩展)管理和监控Tomcat组件,日志服务,JNDI和SESSION管理等。
下面对途中各个名词做简单介绍:
组件 | 实现类 | 父组件 | 是否是容器 | 说明 |
---|---|---|---|---|
Server | StandardServer | 无 | 否 | 每个Tomcat服务只有一个Server,负责管理Tomcat服务和Service的生命周期。 |
Service | StandardService | Server | 否 | 可以看作一个完整的对外服务,主要由Connector和Engine组成并负责管理他们的生命周期。Connector负责处理外部请求,Engine负责处理业务逻辑。 |
Engine | StandardEngine | Service | 是 | 是一种Container,主要负责管理Host的生命周期。 |
Host | StandardHost | Engine | 是 | 是一种Container,主要负责管理Context的生命周期。 |
Context | StandardContext | Host | 是 | 是一种Container,主要负责管理wrapper的生命周期。 |
Wrapper(Servlet) | StandardWrapper | Context | 是 | 是一种Container,主要负责管理Servlet的生命周期。 |
Connector | Connector | Service | 否 | 一个Service可以由一个或多个Connector组成,主要负责监听外部请求,并将请求转发到Engine。 |
ProtocalHandler | Http11NioProtocol(还有其他实现这是最常用的) | Connector | 否 | 主要由EndPoint和Handler组成,负责处理请求,并将请求交给Adapter处理。 |
EndPoint | NioEndpoint(还有其他实现这是最常用的) | ProtocalHandler | 否 | 创建守护进程监听端口请求。解析请求并将请求交给Handler处理。 |
Handler | ConnectionHandler | ProtocalHandler | 否 | 建立ProtocalHandler和Adapter之间的连接,并将请求转发发到Adapter上。 |
Adapter | CoyoteAdapter | Connector | 否 | 将请求转换成ServletRequest,ServletResponse,并根据请求的路径解析对应的Host、Context、Wrapper。并将ServletRequest,ServletResponse交给Engine处理。 |
5、Tomcat的类加载机制:
在java程序中,java虚拟机规范推荐采用父子委托的类加载机制,即子类先请求父类加载器加载,加载不到则使用子类加载器加载。但tomcat的类加载过程打破了这一机制。我们先来看一下tomcat涉及哪些类加载器。
Bootstrap:负责加载java运行时类和Ext包下的类。
Common:以应用类加载器为父类,是tomcat顶层的公用类加载器,其路径由conf/catalina.properties中的common.loader指定,默认指向${catalina.base}/lib;${catalina.home}/lib下的包。
Catalina:以Common类加载器为父类,是用于加载Tomcat应用服务器的类加载器,其路径由server.loader指定,默认为空,此时tomcat使用Common类加载器加载应用服务器。
Shared:以Common类加载器为父类,是用于加载Tomcat应用服务器的类加载器,其路径由shared.loader指定,默认为空,此时tomcat使用Common类加载器加载应用服务器。
WebappX:以 Shared类加载器为父类,是用于加载Tomcat应用的类加载器。
Web应用类加载器默认的加载顺序是:
(1).先从缓存中加载;
(2).如果没有,则从JVM的Bootstrap类加载器加载;
(3).如果没有,则从当前类加载器加载(按照WEB-INF/classes、WEB-INF/lib的顺序);
(4).如果没有,则从父类加载器加载,由于父类加载器采用默认的委派模式,所以加载顺序是AppClassLoader、Common、Shared。
tomcat提供了delegate属性用于控制是否启用java委派模式,默认false(不启用),当设置为true时,tomcat将使用java的默认委派模式,这时加载顺序如下:
(1).先从缓存中加载;
(2).如果没有,则从JVM的Bootstrap类加载器加载;
(3).如果没有,则从父类加载器加载,加载顺序是AppClassLoader、Common、Shared。
(4).如果没有,则从当前类加载器加载(按照WEB-INF/classes、WEB-INF/lib的顺序);
6、tomcat的初始化过程:
7、请求过程: