Wrapper:
SingleThreadModel:marker interface,表明servlet实例的service方法不会被多线程同时执行,这种情况使用Servlet对象池,不绝对保证线程安全(访问static变量)
StandardWrapper:实现ServletConfig接口,
allocate方法:
非SingleThreadModel:没有实例调用loadServlet()生成实例,countAllocated增加
SingleThreadModel:若分配出去实例数为总实例数(无剩余),若未达实例上限loadServlet()生成,否则在Servlet对象池上wait方法等待deallocate方法唤醒,可分配后countAllocated增加,对象池取出。
loadServlet():
1 非SingleThreadModel且存在实例返回实例
2 实例化Servlet
3 为ContainerServlet将Wrapper对象(即本身)传入
4 判断是否为SingleThreadModel(instanceof),是否是SingleThreadModel至少要实例化一次才知道
5 调用Servlet的init方法,传入StandardWrapperFacade对象
StandardWrapperValve:
1 Wrapper的allocate方法获取servlet
2 创建ApplicationFilterChain
3 filterChain.doFilter(内部最后调用servlet的service方法)
4 filterChain释放
5 Wrapper的deallocate方法,countAllocated(已对外分配计数)减少,若为SingleThreadModel还需交还Servlet实例并通知
6 Wrapper/Servlet不再有效(起始有效时间为Long.MAX_VALUE),调用Wrapper的unload方法(内部调用Servlet的destroy方法)
Context的start方法发出configure_start事件,ContextConfig收到事件后创建WebXml对象,并根据web.xml文件填充WebXml对象,WebXml将自身的FilterDef和FilterMap放入Context
Context的start方法内根据FilterDef生成(Application)FilterConfig并内部缓存。
Context的start方法内对其所有loadOnStartup >=0的Wrapper调用load方法,load方法调用loadServlet生成Servlet
创建ApplicationFilterChain:
1 filterMap含多个dispatcherType(forward…) , 若请求的dispatcherType(从属性取)能匹配上
2 filterMap含多个urlPattern,若requestPath或serlvetName能匹配上
若1和2都满足,找到此filterMap对应的FilterConfig并将其加入FilterChain
ApplicationFilterChain的doFilter方法:
pos<n(小于内部过滤器总数):pos++, 取对应的FilterConfig调用getFilter方法(内部已有直接返回,没有就创建并初始化)得到Filter,调用Filter的doFilter方法
pos=n:已到末尾,调用servlet的service方法
StandardHost: appBase , autoDeploy(默认true),deployOnStartup(默认true) ,unpackWars(碰到war是解压后再部署还是直接部署war) ,多个别名
StandardHost的start方法发出start事件,HostConfig(Listener)根据deployOnStartup决定是否部署(appBase下的目录和war)
StandardHost的backgroundProcess发出periodic事件,HostConfig根据autoDeploy决定是否检查有新的应用需要部署(或者已部署应用有更新,这个还不能确定)
StandardHost:start方法加入ErrorReportValve
Engine:defaultHost(设置入Mapper内,当Mapper匹配不到合适的host时使用),jvmRoute(就是一个id,从系统属性jvmRoute中获取,保证集群内唯一,用于负载均衡),Service(互相引用)
Server:含shutdown的address,port和命令String,含多个service,关联Catalina对象,await方法在address和port上监听command,启动时Bootstrap的main方法命令行参数最后一个是start(默认也是start)就会await,若是startd,不await,主线程启动后退出(一般tomcat主线程是唯一的非守护线程),导致进程退出
Service:关联Server,多个Connector,Container,可关联多个Executor(扩展jdk的Executor,扩展Lifecycle)
三个classloader的加载路径分别看sun.boot.class.path,java.ext.dirs,java.class.path这三个系统属性。
那么PATH,JAVA_HOME,CLASSPATH这些环境变量有什么用呢,PATH结合JAVA_HOME会决定你默认的java等命令运行的是什么,说实话你不在命令行敲不用配,CLASSPATH说实话没有什么用了,不用配了
Digester:解析xml,事件驱动
碰到某个元素(用模式来表示)的开始标签(依次运行匹配rule的start方法),结束标签(依次运行匹配rule的end方法)
依次:按照加入digester的顺序
ObjectCreateRule:
start:按照某个元素属性(未指定属性或元素无此属性按默认)确定类名,加载类(使用的classLoader请看代码),实例对象(调用无参构造器), 压入对象栈
end:栈顶对象出栈
SetPropertiesRule:
start:碰到某元素开始标签,按照其属性设置栈顶对象的内容,end:无操作
SetNextRule:
start:无操作 end:调用栈顶对象下一对象的某方法,将栈顶对象作为参数传入,需声明方法名,参数类型
为Digester增加规则可直接加,也可使用Digester的addRuleSet方法(内部会调用RuleSet的addRuleInstances方法)
ContextConfig的configure_start事件处理会解析web.xml(包括默认的,应用程序内部的,还有一些jar内的web-fragment.xml等)
关闭钩子:
正常退出:最后一个非守护线程退出,System.exit
非正常退出:ctrl+c, oom, 操作系统关闭
上述正常非正常退出都会并发执行关闭钩子,极少数情况(断电,kill -9)不执行
Catalina的stop方法:若await收到终止命令会调用,关闭钩子会调用
Catalina的stop方法:调用Server的stop和destory方法,为避免stop两次调用,内部需取消关闭钩子并且状态判断
tomcat启动关闭:
目前使用SystemClassLoader,其加载路径bin/bootstrap.jar(含Bootstrap,CatalinaProperties 类和下面的配置文件)和bin/tomcat-juli.jar
入口:Bootstrap的main方法,不存在Bootstrap实例,创建Bootstrap实例并初始化,存在则设置catalinaLoader为当前线程的contextClassLoader,
然后根据命令行最后一个参数进行判断(没有默认start)
start:反射调用Catalina的设置await方法,load方法,start方法
startd:反射调用load方法,start方法
stopd:stop方法
stop:stopServer方法
为什么反射调用至今不清楚,contextClassloader为catalinaLoader,Catalina方法内需要加载类应该使用加载Catalina类的catalinaLoader,看上去没有必要反射调用啊?
初始化Bootstrap:
1 确保系统属性catalina.home存在,若不存在,若系统属性user.dir下存在bootstrap.jar(即user.dir已经是bin目录了),取user.dir的上一级目录作为catalina.home,其他情况(默认)user.dir
2 确保catalina.base存在,若不存在,若存在catalina.home,用catalina.home,否则用user.dir
3 初始化3个classLoader
首先确定配置文件:系统属性catalina.config指定路径,其次conf/catalina.properties, 最后bootstrap.jar里的org/apache/catalina/startup/catalina.properties
首先commonLoader:查看配置文件中common.loader是否有值,无值SystemClassLoader作为commonLoader,有值则拆分值作为commonLoader类加载路径,且system作为common的parent(传null即是system)
catalinaLoader:server.loader无值则commonLoader作为catalinaLoader,有值就创建且commonLoader作为parent,sharedLoader同理(shared.loader)
实际配置如下,只有commonLoader,加载/lib目录
common.loader=
c
a
t
a
l
i
n
a
.
b
a
s
e
/
l
i
b
,
{catalina.base}/lib,
catalina.base/lib,{catalina.base}/lib/.jar,
c
a
t
a
l
i
n
a
.
h
o
m
e
/
l
i
b
,
{catalina.home}/lib,
catalina.home/lib,{catalina.home}/lib/.jar
server.loader=
shared.loader=
4 设置catalinaLoader为当前线程的contextClassLoader
5 使用catalinaLoader加载Catalina类,实例化Catalina对象,反射调用Catalina的setParentClassLoader方法且将sharedLoader传入,我们知道sharedLoader就是给WebappClassLoader做parent的,那是怎么实现的呢?实际上从Catalina到各级容器都有parentClassLoader成员变量和setter,getter方法,常用getter实现(自己成员变量有,取自己的,否则向上级容器(或者service向server)要,实在取不到用SystemClassLoader)
Catalina的load方法
解析server.xml, 内部Server及其附属对象构建,Server初始化(带动所有的初始化)
Catalina的start方法
Server的start方法(带动所有),可能注册ShutdownHook,可能await并await返回后调用stop方法
Catalina的stopServer方法
不存在Server解析server.xml创建Server(Digester只处理Server标签),存在Server则调用Server的stop方法,然后根据Server
的address,port发送命令以停止原jvm的主线程。
HostConfig,ContextConfig是通过LifecycleListenerRule进行设置的。
HostConfig接start事件,若deployOnStartup,进行部署(xml,war,目录)
HostConfig接periodic事件,若autoDeploy,进行修改或新增的部署(xml,war,目录)
部署xml:Host的xmlBase下可能存在一些xml文件,文件描述了一个Context的docBase和其他属性,通过它新增Context到Host内
部署war:Host的appBase下可能有一些war,unpackWARs决定是否解压war包,部署相应webapp
部署目录:Host的appBase下可能有一些目录,部署相应webapp
ManagerServlet 实现ContainerServlet(设置了Wrapper),访问ManagerServlet(部署相应Context, /* 将Context内所有请求导入ManagerServlet,根据pathInfo和请求参数调用相应方法,如list 列举Host下所有Context)