/**
*作者:annegu
*日期:2009-06-16
*/
对于engine, host, context来说,它们都属于容器,当接收到客户端请求的时候,请求会被传递到容器中,在一个容器中处理完毕之后,会被传递给下一个容器处理。因此,我们可以这样理解tomcat,总的来说,tomcat就是一种自上而下,一个容器里面又嵌套包含了另一个子容器的结构。所以,在tomcat启动的时候,我们也可以想象,它必定要先启动父容器,然后再启动子容器,在启动每一层容器的时候,还会启动容器中的一些相关组件,当所有的容器与组件都安装启动完毕,那么tomcat就启动完毕了。
因此,很容易理解,tomcat 启动的第一步就是进行容器的装配,就是把父容器和子容器拼装起来,并且安装上相关的组件,这很像一个车间装配的过程。
当一切装配齐全,机器已经在各个工人的手中完全组装好了,那么接下来的一步,我们只需要按下开关,机器就可以工作啦。多么方便哪!
1、 一切事情的起点都源于org.apache.catalina.startup.Bootstrap的“引导”。Bootstrap负责对catalina的配置文件路径进行了一番指导,指定了三种类型的classLoader,接下来catalina就可以用这三种类型的classLoader来负责装配容器了。然后Bootstrap用反射机制调用了org.apache.catalina.startup.Catalina的process方法,引导catalina进行启动。
2、 Catalina的工作首先是用digester来装配各个容器与组件(degester是Jakarta子项目Commons下的一个模块,支持基于规则的对任意XML文档的处理,提供了一种将XML与Java对象进行映射的方便方法),这个装配就像我们上面说的那样,就是把server下的service进行安装,然后依次把service下的engine,host,context这些容器以及容器中的各种组件按照父子关系一一拼装。这些配置文件的来源都是Bootstrap之间就已经告知了的。在这里它只负责组装。
接着,catalina会对server进行初始化工作,主要就是把service中配置的connector进行初始化(HTTP与AJP)。
然后调用server的start方法,启动tomcat server。
最后,为server注册一个hook程序,检测当server shutdown的时候,关闭tomcat的各个容器。
3、 进入server的start方法。
启动server的容器的三个lifecycle事件:BEFORE_START_EVENT,START_EVENT,AFTER_START_EVENT。
启动server的子容器service。
4、 进入service的start方法。启动engine与connector。
5、 下面就开始进入engine了。
之前说过,engine, host与context都是容器,它们都继承自Container类。它们既然都是一种container,那么在处理手法上一定又很多类似的地方,因此,tomcat使用了ContainerBase这个类,把它作为engine, host与context的父类,让这些容器都可以通过super.start()方法来达到大部分主要逻辑的复用。
那么,我们首先就来看一下这个ContainerBase中都做了些什么,也就可以知道容器大致都怎么处理请求的。
a) 触发启动前事件(BEFORE_START_EVENT)。
b) 设置标签,表示该容器已启动。
c) 启动容器中的各个组件,如loader, logger, manager, cluster, realm, resources等。
d) 启动当前容器的子容器。
e) 启动当前容器的管道pipeline*。
f) 触发启动中事件(START_EVENT)。
g) 触发启动后事件(AFTER_START_EVENT)。
*:pipeline:当一个容器要把从上一级传递过来的需求转交给子容器的时候,它就会把这个需求放进容器的管道(pipeline)里面去,这个管道里面呢有多个阀门机关(value),而需求在管道里面流动的时候,就会被管道里面的各个阀门拦截下来,只有满足了过关的要求,阀门才会放行。比如管道里面放了两个阀门,第一个阀门叫做“access_allow_vavle”,也就是说需求流过来的时候,它会看这个需求是哪个IP过来的,如果这个IP已经在黑名单里面了,OK,立马拦截,这个需求最远就只能走到这里了,不可能再往下走了!第二个阀门叫做“defaul_access_valve”,它会做例行的检查,如果通过的话,OK,把需求传递给当前容器的子容器。 就是通过这种方式, 需求就在各个容器里面传递,流动, 最后抵达目的地的了。
以上就是ContainerBase中进行的一些处理。尽管大部分内容都是共用的,但每个容器还是有一些自己特别的处理的,这些各个容器特有的任务都会放在调用ContainerBase之前进行处理。在engine中的特别处理包括engine自己的log以及mbean的处理等等。
6、 Host是engine的子容器,所以host也会调用ContainerBase的start()方法。
而host的特殊处理主要就是往pipeline里面安装了一个errorReportValue的阀门。这个errorReportValue的作用主要就是用来检查response的。需求在被Engine传递给Host后, 会继续传递给Context做具体的处理。 这里需求其实就是作为参数传递的Request, Response。所以在context把需求处理完后,通常会改动response。而这个org.apache.catalina.valves.ErrorReportValve的作用就是检察response是否包含错误, 如果有就做相应的处理。
7、 终于到了Context了。Context的启动是从 StandardContext的start()开始的。下面我们一步一步来看StandardContext的start()中都做了些什么。
a) 触发启动前事件(BEFORE_START_EVENT)。
b) 设置web app的具体目录webappResources。
c) 为context指定loader,Loader就是用来指定这个context会用到哪些类啊,哪些jar包啊这些什么的。
d) GetCharsetMapper(),得到字符编码格式,tomcat自己有一个默认的配置文件用来设置默认情况下的字符编码格式,如果用户没有自定义的话,就采用默认的配置,一般为为/org/apache/catalina/util/CharsetMapperDefault.properties。
e) postWorkDirectory (),创建临时文件目录。Tomcat下面有一个work目录,用来存放临时文件。这个步骤就是在那里创建一个目录,一般说来会在%CATALINA_HOME%/work/Standalone\localhost\这个地方生成一个目录。
f) Binding thread(),负责绑定当前线程与context。首先要转换class loader,因为之前需要的是tomcat下的所有class和lib,接下来需要的就是当前context,也就是web app的class和lib了,所以要重新设置当前的的contextClassLoader,同时要记录下旧的class loader。然后就要进行线程的绑定了。
threadBindings和threadNameBindings都是HashTable,这两步操作把当前线程与当前的这个context绑定起来。接下来这个线程就作为这个web app的主线程了。
g) 启动当前context的loader。
h) 重置logger并启动它。
i) 若存在子容器,启动子容器,并启动其管道pipeline。
j) 触发START_EVENT事件监听,
在这个事件监听里面会启动ContextConfig的start()事件,ContextConfig是用来配置web.xml的。比如这个Context有多少Servlet,又有多少Filter,就是在这里给Context装上去的。ContextConfig主要做了这些工作:
k) 为context创建welcome files,通常是这三个启动文件:index.html、index.htm、index.jsp,它们就被默认地绑在了这个context上。
l) 触发AFTER_START_EVENT事件。
m) 配置listener。
n) 启动manager。Manager是用来管理session的。对于服务器来说,每个请求传递过来的时候,会在request里面加上一个叫做sessionId的属性,服务器就通过这个sessionId来知道这个请求到底是属于哪一个session的。
o) 启动context的后台主线程。
p) 配置filter。
q) 启动带有<load-on-startup>的Servlet。如<load-on-startup>1</load-on-startup>,启动的顺序从1开始按照数字从小到大,1, 2, 3 ……,最后才是0。
默认情况下,至少会启动如下3个的Servlet:
org.apache.catalina.servlets.DefaultServlet 负责处理静态资源的Servlet,例如图片、html、css、js等等。
org.apache.catalina.servlets.InvokerServlet负责处理没有做Servlet Mapping的那些Servlet。
org.apache.jasper.servlet.JspServlet负责处理JSP文件。
r) 标识context已经启动完毕,如果在启动的时候发生错误,则stop server。
s) 注册JMX。registerJMX();
t) 关闭所有JAR,以免在启动的时候打开的文件数量总是很高。
如果文字看不下来的话,可以看看下面的流程图,如果你坚持看完了上面一大段话的话,呃,也可以再看看下面的图。
[img]/upload/attachment/117762/9653c318-d5b2-342f-a624-3a8f30c60c87.jpg[/img]
到这里tomcat就算启动完毕了,我们可以看到它的启动过程是一环套一环的过程,先是父容器,然后是子容器,一层层往下递进。
*作者:annegu
*日期:2009-06-16
*/
对于engine, host, context来说,它们都属于容器,当接收到客户端请求的时候,请求会被传递到容器中,在一个容器中处理完毕之后,会被传递给下一个容器处理。因此,我们可以这样理解tomcat,总的来说,tomcat就是一种自上而下,一个容器里面又嵌套包含了另一个子容器的结构。所以,在tomcat启动的时候,我们也可以想象,它必定要先启动父容器,然后再启动子容器,在启动每一层容器的时候,还会启动容器中的一些相关组件,当所有的容器与组件都安装启动完毕,那么tomcat就启动完毕了。
因此,很容易理解,tomcat 启动的第一步就是进行容器的装配,就是把父容器和子容器拼装起来,并且安装上相关的组件,这很像一个车间装配的过程。
当一切装配齐全,机器已经在各个工人的手中完全组装好了,那么接下来的一步,我们只需要按下开关,机器就可以工作啦。多么方便哪!
1、 一切事情的起点都源于org.apache.catalina.startup.Bootstrap的“引导”。Bootstrap负责对catalina的配置文件路径进行了一番指导,指定了三种类型的classLoader,接下来catalina就可以用这三种类型的classLoader来负责装配容器了。然后Bootstrap用反射机制调用了org.apache.catalina.startup.Catalina的process方法,引导catalina进行启动。
2、 Catalina的工作首先是用digester来装配各个容器与组件(degester是Jakarta子项目Commons下的一个模块,支持基于规则的对任意XML文档的处理,提供了一种将XML与Java对象进行映射的方便方法),这个装配就像我们上面说的那样,就是把server下的service进行安装,然后依次把service下的engine,host,context这些容器以及容器中的各种组件按照父子关系一一拼装。这些配置文件的来源都是Bootstrap之间就已经告知了的。在这里它只负责组装。
接着,catalina会对server进行初始化工作,主要就是把service中配置的connector进行初始化(HTTP与AJP)。
然后调用server的start方法,启动tomcat server。
最后,为server注册一个hook程序,检测当server shutdown的时候,关闭tomcat的各个容器。
3、 进入server的start方法。
启动server的容器的三个lifecycle事件:BEFORE_START_EVENT,START_EVENT,AFTER_START_EVENT。
启动server的子容器service。
4、 进入service的start方法。启动engine与connector。
5、 下面就开始进入engine了。
之前说过,engine, host与context都是容器,它们都继承自Container类。它们既然都是一种container,那么在处理手法上一定又很多类似的地方,因此,tomcat使用了ContainerBase这个类,把它作为engine, host与context的父类,让这些容器都可以通过super.start()方法来达到大部分主要逻辑的复用。
那么,我们首先就来看一下这个ContainerBase中都做了些什么,也就可以知道容器大致都怎么处理请求的。
a) 触发启动前事件(BEFORE_START_EVENT)。
b) 设置标签,表示该容器已启动。
c) 启动容器中的各个组件,如loader, logger, manager, cluster, realm, resources等。
d) 启动当前容器的子容器。
e) 启动当前容器的管道pipeline*。
f) 触发启动中事件(START_EVENT)。
g) 触发启动后事件(AFTER_START_EVENT)。
*:pipeline:当一个容器要把从上一级传递过来的需求转交给子容器的时候,它就会把这个需求放进容器的管道(pipeline)里面去,这个管道里面呢有多个阀门机关(value),而需求在管道里面流动的时候,就会被管道里面的各个阀门拦截下来,只有满足了过关的要求,阀门才会放行。比如管道里面放了两个阀门,第一个阀门叫做“access_allow_vavle”,也就是说需求流过来的时候,它会看这个需求是哪个IP过来的,如果这个IP已经在黑名单里面了,OK,立马拦截,这个需求最远就只能走到这里了,不可能再往下走了!第二个阀门叫做“defaul_access_valve”,它会做例行的检查,如果通过的话,OK,把需求传递给当前容器的子容器。 就是通过这种方式, 需求就在各个容器里面传递,流动, 最后抵达目的地的了。
以上就是ContainerBase中进行的一些处理。尽管大部分内容都是共用的,但每个容器还是有一些自己特别的处理的,这些各个容器特有的任务都会放在调用ContainerBase之前进行处理。在engine中的特别处理包括engine自己的log以及mbean的处理等等。
6、 Host是engine的子容器,所以host也会调用ContainerBase的start()方法。
而host的特殊处理主要就是往pipeline里面安装了一个errorReportValue的阀门。这个errorReportValue的作用主要就是用来检查response的。需求在被Engine传递给Host后, 会继续传递给Context做具体的处理。 这里需求其实就是作为参数传递的Request, Response。所以在context把需求处理完后,通常会改动response。而这个org.apache.catalina.valves.ErrorReportValve的作用就是检察response是否包含错误, 如果有就做相应的处理。
7、 终于到了Context了。Context的启动是从 StandardContext的start()开始的。下面我们一步一步来看StandardContext的start()中都做了些什么。
a) 触发启动前事件(BEFORE_START_EVENT)。
b) 设置web app的具体目录webappResources。
c) 为context指定loader,Loader就是用来指定这个context会用到哪些类啊,哪些jar包啊这些什么的。
d) GetCharsetMapper(),得到字符编码格式,tomcat自己有一个默认的配置文件用来设置默认情况下的字符编码格式,如果用户没有自定义的话,就采用默认的配置,一般为为/org/apache/catalina/util/CharsetMapperDefault.properties。
e) postWorkDirectory (),创建临时文件目录。Tomcat下面有一个work目录,用来存放临时文件。这个步骤就是在那里创建一个目录,一般说来会在%CATALINA_HOME%/work/Standalone\localhost\这个地方生成一个目录。
f) Binding thread(),负责绑定当前线程与context。首先要转换class loader,因为之前需要的是tomcat下的所有class和lib,接下来需要的就是当前context,也就是web app的class和lib了,所以要重新设置当前的的contextClassLoader,同时要记录下旧的class loader。然后就要进行线程的绑定了。
threadBindings.put(Thread.currentThread(), context);
threadNameBindings.put(Thread.currentThread(), name);
threadBindings和threadNameBindings都是HashTable,这两步操作把当前线程与当前的这个context绑定起来。接下来这个线程就作为这个web app的主线程了。
g) 启动当前context的loader。
h) 重置logger并启动它。
i) 若存在子容器,启动子容器,并启动其管道pipeline。
j) 触发START_EVENT事件监听,
lifecycle.fireLifecycleEvent(START_EVENT, null);
在这个事件监听里面会启动ContextConfig的start()事件,ContextConfig是用来配置web.xml的。比如这个Context有多少Servlet,又有多少Filter,就是在这里给Context装上去的。ContextConfig主要做了这些工作:
defaultWebConfig(); //每个context要配置一个默认的web.xml,就是omcat/conf/web.xml,这样container servlet才能被加载。
applicationWebConfig(); //配置web app自己的web.xml
validateSecurityRoles(); //验证访问角色的安全性。就是web app的部署权限,通常我们会通过访问/admin 或者/manager来进入应用的部署界面,一般用户是admin或者manager才能访问。访问的用户以及可以访问的资源都是可以限制的,这些都可以通过权限验证来实现。
authenticatorConfig(); //配置自动认证
k) 为context创建welcome files,通常是这三个启动文件:index.html、index.htm、index.jsp,它们就被默认地绑在了这个context上。
l) 触发AFTER_START_EVENT事件。
m) 配置listener。
n) 启动manager。Manager是用来管理session的。对于服务器来说,每个请求传递过来的时候,会在request里面加上一个叫做sessionId的属性,服务器就通过这个sessionId来知道这个请求到底是属于哪一个session的。
o) 启动context的后台主线程。
p) 配置filter。
q) 启动带有<load-on-startup>的Servlet。如<load-on-startup>1</load-on-startup>,启动的顺序从1开始按照数字从小到大,1, 2, 3 ……,最后才是0。
默认情况下,至少会启动如下3个的Servlet:
org.apache.catalina.servlets.DefaultServlet 负责处理静态资源的Servlet,例如图片、html、css、js等等。
org.apache.catalina.servlets.InvokerServlet负责处理没有做Servlet Mapping的那些Servlet。
org.apache.jasper.servlet.JspServlet负责处理JSP文件。
r) 标识context已经启动完毕,如果在启动的时候发生错误,则stop server。
s) 注册JMX。registerJMX();
t) 关闭所有JAR,以免在启动的时候打开的文件数量总是很高。
如果文字看不下来的话,可以看看下面的流程图,如果你坚持看完了上面一大段话的话,呃,也可以再看看下面的图。
[img]/upload/attachment/117762/9653c318-d5b2-342f-a624-3a8f30c60c87.jpg[/img]
到这里tomcat就算启动完毕了,我们可以看到它的启动过程是一环套一环的过程,先是父容器,然后是子容器,一层层往下递进。