看透SpringMVC读书笔记-Tomcat篇(3)

Tomcat Container分析

Container是Tomcat中容器的接口,通常使用的servlet就封装在其子接口Wrapper中。

Container一共有4个子接口Engine,Host,Context,Wapper和一个默认实现类ContainerBase,每个接口都是一个容器,这4个容器都一个对应的StandardXXX的实现类,并且这些类都继承ContainerBase类。

另外Container还继承了Lifecycle接口,而且ContainerBase间接继承了LifecycleMBeanBase类,所以Engine,Host,Context,WapperWapper,这四个子容器都符合Tomcat的生命周期管理模式。

Container 的4个子容器

Engine,Host,Context,Wapper这四个子容器是逐层包含的关系,Engine是最顶层,每个service最多只能有一个Engine,Engine里面可以有多个Host,每个Host下可以有多个Context,每个Context里面可以包含多个Wapper。

 4个容器的作用

Engine:引擎,用来管理多个Host,一个Service组多只能有一个Engine。

Host:站点,也可以叫虚拟主机,通过配置Host就可以添加。

Context:代表一个应用程序,或者一个WEB-INF目录及下面的web,xml文件。

Wrapper:每个Wrapper里面封装着一个Servlet. 

Host与Context的区别,Host代表一个站点也就是一个网络地址

Context和Host的区别是Context表示一个应用,比如,默认配置下webapps下的每个目录都是一个应用,其中ROOT目录中存放着主应用,其他目录存放着别的子应用,而整个webapps是一个站点。假如www.excelib.com域名对应着webapps目录所代表的站点,其中的ROOT目录里的应用就是主应用,访问时直接使用域名就可以,而webapps/test目录存放的是test子应用,访问时需要用www.excelib.com/test,每一个应用对应一个Context,所有webapps下的应用都属于www.excelib.com站点,而blog.excelib.com则是另外一个站点,属于另外一个Host。

Container的启动

Container的启动是通过init和start方法来完成的,在前面分析过这两个方法会在Tomcat启动时被Service调用。Container也是按照Tomcat的生命周期来管理的,init和start方法也会调用initInternal和startInternal方法来具体处理,不过Container和前面讲的Tomcat整体结构启动的过程稍微有点不一样。

主要有三点区别:

1.Container的4个子容器有一个共同的父类ContainerBase,这里定义了Container容器的initInternal和startInternal方法通用处理内容,具体容器还可以添加自己的内容;

2.除了最顶层容器的init是被Service调用的,子容器的init方法并不是在容器中逐层循环调用的,而是在执行start方法的时候通过状态判断还没有初始化才会调用;

3.start方法除了在父容器的startInternal方法中调用,还会在父容器的添加子容器的addChild方法中调用,这主要是因为Context和Wrapper是动态添加的,我们在站点目录下放一个应用的文件夹或者war包就可以添加一个Context,在web.xml文件中配置一个Servlet就可以添加一个Wrapper,所以Context和Wrapper是在容器启动的过程中才动态查找出来添加到相应的父容器中的。

ContainerBase

ContainerBase是Container的基础实现类,initInternal方法主要初始化ThreadPoolExecutor类型的startStopExecutor属性,用于管理启动和关闭的线程.

             

ContainerBase的startInternal方法主要做了5件事:

1.如果有Cluster和Realm则调用其start方法;

2.调用所有子容器的start方法启动子容器;

3.调用管道中Value的start方法来启动管道;

4.启动完成后将生命周期状态设置为LifecycleState.STARTING状态;

5.启用后台线程定时处理一些事情。

 这里首先启动了Cluster和Realm,启动方法是直接调用它们的start方法。Cluster用于配置集群,在server.xml中有注释的参考配置,它的作用就是同步Session,Realm是Tomcat的安全域,可以用来管理资源的访问权限。子容器是使用startStopExecutor调用新线程来启动的,这样可以用多个线程来同时启动,效率更高,具体启动过程是通过一个for循环对每个子容器启动了一个线程,并将返回的Future保存到一个List中(更多线程相关内容会在异步处理中介绍),然后遍历每个Future并调用其get方法。遍历Future主要有两个作用:①其get方法是阻塞的,只有线程处理完之后才会向下走,这就保证了管道Pipeline启动之前容器已经启动完成了;②可以处理启动过程中遇到的异常。

threadStart方法启动的后台线程是一个while循环,内部会定期调用backgroundProcess方法做一些事情,间隔时间的长短是通过ContainerBase的backgroundProcessorDelay属性来设置的,单位是秒,如果小于0就不启动后台线程了,不过其backgroundProcess方法会在父容器的后台线程中调用。backgroundProcess方法是Container接口中的一个方法,一共有3个实现,分别在ContainerBase、StandardContext和StandardWrapper中,ContainerBase中提供了所有容器共同的处理过程,StandardContext和StandardWrapper的backgroundProcess方法除了处理自己相关的业务,也调用ContainerBase中的处理。ContainerBase的backgroundProcess方法中调用了Cluster、Realm和管道的backgroundProcess方法;StandardContext的background-Process方法中对Session过期和资源变化进行了处理;StandardWrapper的backgroundProcess方法会对Jsp生成的Servlet定期进行检查。

Engine

Service会调用最顶层容器的init和start方法,如果使用了Engine就会调用Engine的。Engine的默认实现类StandardEngine中的initInternal和startInternal方法。

它们分别调用了ContainerBase中的相应方法,initInternal方法还调用了getRealm方法,其作用是如果没有配置Realm,则使用一个默认的NullRealm,代码如下:

Host

Host的默认实现类StandardHost没有重写initInternal方法,初始化默认调用ContainerBase的initInternal方法,startInternal方法代码如下:

这里的代码看起来虽然比较多,但功能却非常简单,就是检查Host的管道中有没有指定的Value,如果没有则添加进去。检查的方法是遍历所有的Value然后通过名字判断的,检查的Value的类型通过getErrorReportValveClass方法获取,它返回errorReportValveClass属性,可以配置,默认值是org.apache.catalina.valves.ErrorReportValve,代码如下:

这就是StandardHost的startInternal方法处理的过程。Host的启动除了startInternal方法,还有HostConfig中相应的方法,HostConfig继承自LifecycleListener的监听器(Engine也有对应的EngineConfig监听器,不过里面只是简单地做了日志记录),在接收到Lifecycle.START_EVENT事件时会调用start方法来启动,HostConfig的start方法会检查配置的Host站点配置的位置是否存在以及是不是目录,最后调用deployApps方法部署应用,deployApps方法代码如下:

Context

Context的默认实现类StandardContext在startInternal方法中调用了在web.xml中定义的Listener,另外还初始化了其中的Filter和load-on-startup的Servlet,代码如下:

listenerStart、filterStart和loadOnStartup方法分别调用配置在Listener的contextInitialized方法以及Filter和配置了load-on-startup的Servlet的init方法。Context和Host一样也有一个LifecycleListener类型的监听器ContextConfig,其中configureStart方法用来处理CONFIGURE_START_EVENT事件,这个方法里面调用webConfig方法,webConfig方法中解析了web.xml文件,相应地创建了Wrapper并使用addChild添加到了Context里面。

Wrapper

Wrapper的默认实现类StandardWrapper没有重写initInternal方法,初始化时会默认调用ContainerBase的initInternal方法,startInternal方法代码如下:

这里主要做了三件事情:

用broadcaster发送通知,主要用于JMX;

调用了父类ContainerBase中的startInternal方法;

调用setAvailable方法让Servlet有效。

这里的setAvailable方法是Wrapper接口中的方法,其作用是设置Wrapper所包含的Servlet有效的起始时间,如果所设置的时间为将来的时间,那么调用所对应的Servlet就会产生错误,直到过了所设置的时间之后才可以正常调用,它的类型是long,如果设置为Long.MAX_VALUE就一直不可以调用了。Wrapper没有别的容器那种XXXConfig样式的LifecycleListener监听器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值