Tomcat启动流程
Tomcat源码目录
catalina目录
catalina包含所有的Servlet容器实现,以及涉及到安全、会话、集群、部署管理Servlet容器的各个方面,同时,它还包含了启动入口。
coyote目录
coyote是Tomcat链接器框架的名称,是Tomcat服务器提供的客户端访问的外部接口,客户端通过Coyote与服务器建立链接、发送请求并接收响应。
El目录,提供java表达式语言
Jasper模块提供JSP引擎
Naming模块提供JNDI的服务
Juli提供服务器日志的服务
tomcat提供外部调用的接口api
Tomcat启动流程分析
- 启动流程解析:注意是标准的启动,也就是从bin目录下的启动文件中启动Tomcat
我们可以看到这个流程非常的清晰,同时注意到,Tomcat的启动非常的标准,除去Boostrap和Catalin,我们可以对照一下Server.xml的配置文件。Server,service等等这些组件都是一一对照,同时又有先后顺序。
基本的顺序是先init方法,然后再start方法。
- 加入调试信息():注意是标准的启动,也就是从bin目录下的启动文件中启动Tomcat
可以看到,在源码中加入调试的信息和流程图是一致的。
我们可以看到,除了Bootstrap和catalina类,其他的Server,service等等之类的都只是一个接口,实现类均为StandardXXX类。
我们来看下StandardServer类,
源码分析
Tomcat主要有两个核心的组件,一个是Connector(连接器)和容器。所谓连接器就是当有HTTP请求到来时,连接器负责接收这个请求,然后将该请求转发到容器。容器有Engine,Host,Context,Wrapper。Engine:表示整个Catalina servlet引擎;Host:表示包含一个或多个Context容器的虚拟主机;Context:表示一个Web应用程序。一个Context可以有多个Wrapper;Wrapper:表示一个独立的servlet。一个容器可以有0个或多个低层级的子容器。例如,一般情况下,一个Context实例会有一个或多个Wrapper实例。一个Host实例中会有0个或多个Context实例。但是,Wrapper类型处于层级结构的最底层,因此,它无法再包含子容器了。
Bootstrap启动类
main
一般启动Tomcat会是运行startup.bat或者startup.sh文件,这两个文件最后都会调用,org.apache.catalina.startup包下面Bootstrap类的main方法。main方法具体实现如下:
1 | public static void main(String args[]) { |
main方法中,首先执行init方法初始化了Tomcat自己的类加载器,并通过类加载器创建Catalina实例,然后赋给catalinaDaemon变量,后续操作都使用catalinaDaemon来执行。
setAwait
后面默认执行start命令,将调用setAwait(true),load(args)和start()这三个方法,这三个方法内部都通过反射调用了Catalina的相应方法。
1 | // org.apache.catalina.startup.Catalina |
setAwait方法用于设置Server启动完成后是否进入等待状态的标志,如果为true则进入,否则不进入。
init
main方法先实例化了一个Bootstrap实例,接着调用了init方法。init方法是生命周期方法,以后不再累述。接着看init的具体实现。
1 | public void init() throws Exception { |
initClassLoaders
init方法,先初始化了类加载器。initClassLoaders方法具体实现如下:
1 | //初始化三个类加载器以及确定父子关系 |
createClassLoader
createClassLoader是通过工厂模式创建类加载器,从conf目录中的catalina.properties属性文件中读取配置来设置某一个类加载器加载那些jar包的文件
1 | //创建类加载器 |
createClassLoader需要传入一个父加载器。从具体的代码中可以看出,commonLoader类加载器是catalinaLoader类加载器和sharedLoader类加载器的父加载器。初始化完类加载器后,使用反射机制调用org.apache.catalina.startup.Catalina类下的setParentClassLoader方法。具体代码是:
1 | // Load our startup class and call its process() method |
因为Tomcat执行的是start操作,调用完init方法后,会执行load方法。
1 | if (command.equals("startd")) { |
load
load方法通过反射调用Catalina类的load方法。
1 | private void load(String[] arguments) throws Exception { |
Catalina
load
Catalina的load方法根据conf/server.xml创建了Server对象,并赋值给server属性(具体是通过开源项目Digester完成的),然后调用了server的init方法。
1 | public void load() { |
createStartDigester
load方法中比较重要的方法是createStartDigester(),createStartDigester方法主要的作用就是帮我们实例化了所有的服务组件包括server,service和connect。具体的实例化方法
Digester 查相关资料:Java解析xml主要由DOM4J(一次读取到内存并解析)、SAX(一次解析一部分),digester本身采用SAX的解析方式,并提供了一层包装,对使用者更加友好,后来独立出来成为apache的Commons下面的一个单独的子项目。
1 | protected Digester createStartDigester() { |
start
初始化操作完成后,接下来会执行catalina实例的start方法。
1 | public void start() { |
StandardServer
从上面加载的组件中,Tomcat会默认加载org.apache.catalina.core.StandardServer作为Server的实例类。
initInternal
因为StandardServer使用了Lifecycle生命周期管理,调用init会调用到LifecycleBase的init方法,并会调用到StandardServer的initInternal方法。
1 |
|
startInternal
因为StandardServer使用了Lifecycle生命周期管理,调用start会调用到LifecycleBase的start方法,并会调用到StandardServer的startInternal方法。
1 |
|
await
1 |
|
StandardService
initInternal
1 |
|
startInternal
1 |
|
ContainerBase
是容器的抽象父类,定义了容器生命周期中公共方法。Engine、Host、Context、Wrapper继承此父类。
继承关系
容器的作用
Container的4个容器是逐层包含的关系。它们之间的关系如下图:
1、Engine:用来管理多个站点,一个Service最多只能有一个Engine。
2、Host:代表一个站点,通过配置Host可以添加站点。
3、Context:代表一个应用程序,对应一个WEB-INF目录。
4、Wrapper:每个Wrapper封装一个Servlet。
initInternal
1 |
|
startInternal
1 |
|
threadStart
开启后台线程,定时检查 session 超时、
1 | protected void threadStart() { |
processChildren
内部类 ContainerBackgroundProcessor, 默认 10s一次
在 StandardEngine 构造函数中定义
1 | protected void processChildren(Container container) { |
backgroundProcess
1 |
|
容器的配置
Server配置
1 | <Server port="8005" shutdown="SHUTDOWN"> |
- Server 在 8005端口监听关闭命令“SHUTDOWN”;
- Server 中定义了一个 Catalina的Service;
- Service 中定义了两个Connector:
- 一个是HTTP协议;
- 一个是AJP协议(用于集成);
- Service 中还定义了了一个Catalina的Engine;
- Engine 中定义了 localhost 的 Host;
- defaultHost:请求的域名如果在所有的Host的name和Alias中都找不到使用的默认值
- Host:
- name:表示域名;
- appBase:站点的位置;
- unpackWARS:是否自动解压war包;
- autoDeploy:是否自动部署;
- 子标签: excelib.com:给localhost定义别名;
Context配置
Context通过文件配置的方式一共有5个位置可以配置:
- conf/server.xml中的Context标签;
- conf/[enginename]/[hostname]/目录下以应用命名的 xml 文件。
- 应用自己的 /META-INT/context.xml;
- conf/context.xml 文件
- conf/[enginename]/[hostname]/context.xml.default文件;
用于全局配置
前三个用于配置单独的应用,后面2种是Context共享的。第4种是 整个 Tomcat 共享,第5种配置的内容在对应的站点(Host)中共享。第1种方式只有在Tomcat重启才会重新加载,不推荐使用。
1 | <!-- |
Wrapper的配置
Wrapper的配置,在web.xml中配置的Servlet,一个Servlet对应一个Wrapper、可以在 conf/web.xml 中配置全局的 Wrapper,处理 Jsp的 JspServlet的配置等。
1 | <servlet> |
LifecycleBase
fireLifecycleEvent
1 | protected void fireLifecycleEvent(String type, Object data) { |
HostConfig
lifecycleEvent
1 |
|
StandardEngine
构造方法
1 | public StandardEngine() { |
initInternal
1 |
|
startInternal
1 |
|
StandardHost
构造方法
1 | public StandardHost() { |
startInternal
1 |
|
StandardContext
构造方法
1 | public StandardContext() { |
startInternal
1 |
|
StandardWrapper
构造方法
1 | public StandardWrapper() { |
startInternal
1 |
|
StandardPipeline
initInternal
1 |
|
startInternal
1 |
|