Tomcat中部署应用的方式

部署应用分为定义应用和启动应用。在Tomcat中定义一个应用分为四种:

  1. server.xml中配置节点
  2. 描述符部署:通过/tomcat-7/conf/Catalina/localhost/应用名.xml文件来描述一个应用
  3. War部署:通过在/tomcat-7/webapps/目录下存放一个应用名.war文件来定义一个应用,war包的名字则为应用的名字
  4. 文件夹部署:通过在/tomcat-7/webapps/目录下存放一个应用名文件夹来定义一个应用,文件夹的名字则为应用的名字

Tomcat在启动的过程中会启动应用,但是对于第一种和后面三种启动的时间节点是不一样的,对于第一种在server.xml中定义了一个应用,那么Tomcat在解析server.xml时就会解析出来该应用,然后在启动Host的过程中,会去启动Host的孩子节点,这时就可以发现该Context节点,然后直接启动该Context。

而对于后面三种是稍微有点不一样,也是在启动Host的过程中,只不过是通过发布_START_EVENT事件来触发的,_HostConfig会监听到此事件,然后开始对使用描述符、War、文件夹所定义的应用进行部署。

需要注意的是,部署应用都是通过线程池异步部署的

对于后面三种方式定义的应用,在启动应用之前,首先得生成一个Context对象,然后再调用Context对象的start方法。还有一个,后面这三种方式定义的应用才支持热部署,所以,还需要生成一个redeployResources列表,该列表里保存了对于一个支持热部署的应用,应该要去监听哪些资源发生了变化之后就进行热部署。

在生成一个Context对象时,要注意去识别文件夹或war中是否存在META-INF/context.xml文件,应该这个文件代表着应用的一些配置信息,所以:

  • 如果存在,需要先解析该配置文件,解析出来一个Context对象。
  • 如果不存在,则直接生成一个Context对象。

对于一个Context对象在启动之前还要设置一个非常重要的事件监听器-ContextConfig,对于在server.xml中定义的context会在解析server.xml时就会默认生成一个ContextConfig并绑定到Context对象中去,对于后面的三种方式都会在构造Context对象后,生成一个ContextConfig对象然后设置到Context对象中去。

Context启动流程

当Context对象创建好后,一些属性和监听器都设置好了之后,接下来就需要初始化并启动Context,并且把Context添加到它的父容器Host中了。

要讲Context的初始化和启动,那么就需要先理解一下Context的意义,首先Context表示一个应用上下文,其实就表示一个应用,所以Context是一个Servlet容器,所以Context至少要做一件事件,要解析web.xml文件,或扫描@WebServlet注解,从而找出当前应用中的Servlet并添加到Context中。

对于Context的初始化从功能上来说并不需要做什么事情,我们直接来看Context的启动过程。

生成DirContext
在启动之初,会生成一个目录上下文-DirContext与Context关联起来,DirContext是JNDI规范下的一个接口,表示一个目录或War,通过使用DirContext可以使用统一的规范来操作目录或War下的资源。

生成WebappLoader
Context表示一个应用,在Tomcat中是通过一个WebappLoader来负责加载应用资源的。

生成WorkDirectory
Tomcat会为每个应用生成一个临时的工作目录,只在应用正常远行过程中存在,目录路径为:Tomcat根目录/work/Engine名/Host名/Context名

进行DependencyCheck**
依赖检查,在一个应用中,我们可能通过MANIFEST.MF来依赖应用外部的一个类,所以一个应用在启动时要先检查所依赖的这些类是否存在。

启动WebappLoader
WebappLoader在Tomcat中非常重要,表示一个应用的资源加载器,包括热加载功能都是由它负责的,同时在它的内部有一个非常重要的属性就是类加载器。
在Tomcat中每个应用都有一个单独的类加载器,该类加载器是Tomcat自定义实现的,默认WebappClassLoader。

所以,WebappLoader在启动时会生成这个自定义类加载器,并且设置它的一些属性,比如设置它的父类加载器为Context的父类加载器,设置它的可用资源为DirContext,并且还可以设置用户自定义的其他参数,包括delegate,它表示当使用WebappClassLoader加载器资源时是否先委托给父类加载器进行加载,也就是双亲委派机制的开关,delegate默认是false,表示不委托给父类加载器进行加载。

创建完类加载器之后,就可以这是类加载器的仓库了,主要包括DirContext下的/WEB-INF/classes目录和/WEB-INF/lib目录,将来使用这个类加载器加载类时就会从这两个目录中去寻找和加载类。

启动Cluster、Realm
Cluster表示集群,Realm表示用户验证,这里不讨论。

发布CONFIGURE_START_EVENT事件
这个就比较重要了,虽然说是发布事件,我们正常的理解是事件监听器异步消费这个事件,但是Tomcat中不是这样的,它是同步的,发布一个事件时就直接调用监听器的事件触发逻辑。

上文的第二步中,Context添加了一个监听器ContextConfig,它就会监听这个事件,ContextConfig监听到这个事件时就会开始解析web.xml文件,并且生成Servlet、Filter等对象添加到Context中。这里不去进行详细的讨论。

启动Pipeline
pipeline表示管道,管道里可以设置很多阀门-Valve,所以启动Pipeline重点就是启动管道中的每个Valve

设置Manager
Manager表示管理器,一个应用的管理器,负责管理Session的。

启动ServletContainerInitializer
ServletContainerInitializer是Servlet规范中的,表示Servlet容器初始化器,具体可参考Servlet规范。

开启后台线程
实际上开启后台线程也有一套逻辑,这里不深入,可以告诉大家的是,后台线程负责一些周期性任务,比如集群心跳,检查资源是否发生变动以判断是否需要进行热加载

至此,一个应用部署的主要流程就差不多了。