前言
在上篇文章 Tomcat - 源码阅读环境搭建 中,我们搭建了 Tomcat
源码阅读的环境,并且我们知道了启动类是 org.apache.catalina.startup.Bootstrap
,接下来我们就来分析一下 Tomcat
的核心组件都有哪些,以及它们之间的依赖关系
核心组件
Tomcat
的默认配置文件是 conf/server.xml
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<!-- APR library loader. Documentation at /docs/apr.html -->
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<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>
// 处理请求
<Service name="Catalina">
<-- 配置监听端口 -->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
// 真正处理请求的是 Engine
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
从配置文件中,我们知道 Tomcat
都包含了哪些组件,以及各个组件之间的依赖关系:
下面是 Tomcat
的架构图(这个图我也忘记是从哪个网站保存的了,侵删),包含了 Tomcat
的核心组件,以及它们之间的依赖关系:
接下来我们从代码中来看一下关于上面这些组件的定义及其依赖关系
代码分析
Server
- 首先我们来看一下
Server
的代码实现:
public interface Server extends Lifecycle {
public NamingResourcesImpl getGlobalNamingResources();
// server 中包含 GlobalNamingResources
public void setGlobalNamingResources(NamingResourcesImpl globalNamingResources);
// server 中包含 Service,默认是有一个 Catalina Service
public void addService(Service service);
// Service 可以有很多,是一个 Set 集合
public Service[] findServices();
...
}
Service
- 接着查看
Service
的实现,其中会包含Engine
、Connector
、Executor
:
public interface Service extends Lifecycle {
// Service 中可以有一个 Engine
public Engine getContainer();
public void setContainer(Engine engine);
public void addConnector(Connector connector);
// Service 中有多个 Connector,监听不同的端口
public Connector[] findConnectors();
public void addExecutor(Executor ex);
// Service 中有多个 Executor
public Executor[] findExecutors();
...
}
Connector
Connector
是接收请求的,请求进来一定是 Http
协议,所以 Connector
中包含了协议处理器 ProtocolHandler
public class Connector extends LifecycleMBeanBase {}
Engine
Engine
中可以有很多的 Host
public interface Engine extends Container {
public String getDefaultHost();
// 设置默认的 Host
public void setDefaultHost(String defaultHost);
...
}
Host
Host
代表虚拟 DNS
,根据 Host
可以进行虚拟主机隔离
public interface Host extends Container {}
查看 Host
的实现类 `StandardHost:
public class StandardHost extends ContainerBase implements Host {
...
@Override
public void addChild(Container child) {
// 如果不是 Context 类型的直接抛出异常
if (!(child instanceof Context)) {
throw new IllegalArgumentException
(sm.getString("standardHost.notContext"));
}
child.addLifecycleListener(new MemoryLeakTrackingListener());
// Avoid NPE for case where Context is defined in server.xml with only a
// docBase
Context context = (Context) child;
if (context.getPath() == null) {
ContextName cn = new ContextName(context.getDocBase(), true);
context.setPath(cn.getPath());
}
super.addChild(child);
}
}
Context
public interface Context extends Container, ContextBind {}
一个 Web
应用就是一个 Context
,Context
也有一个标准实现类 StandardContext
:
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
...
@Override
public void addChild(Container child) {
// Global JspServlet
Wrapper oldJspServlet = null;
// 如果不是 Wrapper,直接抛出异常
if (!(child instanceof Wrapper)) {
throw new IllegalArgumentException
(sm.getString("standardContext.notWrapper"));
}
boolean isJspServlet = "jsp".equals(child.getName());
// Allow webapp to override JspServlet inherited from global web.xml.
if (isJspServlet) {
oldJspServlet = (Wrapper) findChild("jsp");
if (oldJspServlet != null) {
removeChild(oldJspServlet);
}
}
super.addChild(child);
if (isJspServlet && oldJspServlet != null) {
/*
* The webapp-specific JspServlet inherits all the mappings
* specified in the global web.xml, and may add additional ones.
*/
String[] jspMappings = oldJspServlet.findMappings();
for (int i=0; jspMappings!=null && i<jspMappings.length; i++) {
addServletMappingDecoded(jspMappings[i], child.getName());
}
}
}
...
}
Context
中添加的子容器是 Wrapper
Wrapper
public interface Wrapper extends Container {}
Wrapper
中不能再添加子组件了,默认实现类是 StandardWrapper
,主要是跟 Servlet
相关的一些操作,每一个 Servlet
会封装为一个 Wrapper
生命周期
通过以上代码分析,我们对 Tomcat
的组件有了一个大致的了解,其中我们可以看到一个共同点,就是它们都继承了 Lifecycle
这一个接口:
Lifecycle
定义了几个生命周期相关的方法,比如:init
、start
、stop
、destroy
等,每一个实现这个接口的组件都具有这几个生命周期。
接着我们看一下 Ligecycle
的子接口 Container
,同时也被 Engine
、Host
等组件所实现
public interface Container extends Lifecycle {
...
public void addChild(Container child);
public Pipeline getPipeline();
...
}
Pipeline
中又可以添加很多的 valve
,Pipeline
用于数据流转,Valve
用于数据处理:
public interface Pipeline extends Contained {
...
public void addValve(Valve valve);
public Valve[] getValves();
...
}
比如上面 serve.xml
中 Host
节点默认的 Valve
:org.apache.catalina.valves.AccessLogValve
,用于记录访问日志。
LifecycleBase
、ContainerBase
利用模板模式,定义好核心步骤,具体逻辑留给组件自己实现。
总结
以上,我们对 Tomcat
的各个组件及其之间的依赖关系有了一个大致的了解,接下来我们就来看一下 Tomcat
的初始化流程、启动流程、请求处理流程。