LifecycleBase生命周期抽象类
在分析启动流程之前,有必要对于实现了LifecycleBase这个类的所有类进行分析。结论是只要调用生命周期方法,都会先调用这个LifecycleBase的父类方法。以StandardServer和StandardEngine容器类为例
1、StandardServer
在具体分析之前需要看下StandardServer的继承关系图
StandardServer继承了生命周期抽象类LifecycleMBeanBase,LifecycleMBeanBase继承了LifecycleBase抽象类。由于StandardServer没有init、start、stop、destroy等方法,所以会到父类寻找这些方法。
①、init
org.apache.catalina.util.LifecycleBase#init
调用setStateInternal方法
发布LifecycleState.INITIALIZING是before_init的生命周期事件
②、setStateInternal
org.apache.catalina.util.LifecycleBase#setStateInternal
这个方法最重要的实现逻辑就是发布当前事件
③、fireLifecycleEvent
org.apache.catalina.util.LifecycleBase#fireLifecycleEvent
根据传入类型type = "before_init"获取当前事件,然后调用当前容器包含的LifecycleListener的lifecycleEvent方法。
④、initInternal
initInternal方法会调用到当前容器的initInternal方法。
2、StandardEngine继承图
继承了ContainerBase,ContainerBase继承了LifecycleMBeanBase并实现了Container。
LifecycleMBeanBase继承了LifecycleBase抽象类。
①、start
org.apache.catalina.util.LifecycleBase#start
发布LifecycleState.STARTING_PREP是before_start事件
②、startInternal
然后调用startInternal方法
Lifecycle有哪些LifecycleState事件
before_init、after_init、start、before_start、after_start、stop、before_stop、after_stop、after_destroy、before_destroy、periodic、configure_start、configure_stop
所以,任何容器类StandardService、StandardEngine、StandardHost、StandardContext、StandardWrapper及其他实现了LifecycleBase调用生命周期方法都是这样的调用流程
Bootstrap启动Tomcat
Tomcat启动通过Bootstrap类main方法启动
org.apache.catalina.startup.Bootstrap#main
main方法主要步骤有三步:
①、init()
②、load()
③、start()
1、init
org.apache.catalina.startup.Bootstrap#init()
①、初始化类加载器
org.apache.catalina.startup.Bootstrap#initClassLoaders
commonLoader、catalinaLoader、sharedLoader类加载器都是URLClassLoader
当前线程设置类加载器catalinaLoader
②、启动类加载并创建实例对象Catalina
设置Catalina类的父加载器setParentClassLoader是URLClassLoader
setAwait
catalina设置Await是阻塞,true表示阻塞
2、load
org.apache.catalina.startup.Bootstrap#load
org.apache.catalina.startup.Catalina#load()
①、创建Digester
获取file文件conf\server.xml,创建Digester对象。这个类的目的就是把server.xml中的层级结构和生命周期实现类都创建出来,包括容器类和监听器类。
org.apache.catalina.startup.Catalina#createStartDigester
digester对象是解析server.xml文件
fakeAttributes通过键值对添加
digester的规则添加到Rules对象
digester解析文件reader = SAXParserImpl$JAXPSAXParser
②、server.init初始化
设置server的Catalina属性
开始server的init
容器对象都实现了LifecycleBase声明周期方法,所以容器对象都会调用到org.apache.catalina.util.LifecycleBase#init,然后调用到容器的initInternal方法
org.apache.catalina.core.StandardServer#initInternal
③、services.init()
org.apache.catalina.util.LifecycleBase#init
org.apache.catalina.core.StandardService#initInternal
④、engine.init()
org.apache.catalina.core.StandardEngine#initInternal
org.apache.catalina.core.StandardEngine#getRealm
org.apache.catalina.core.ContainerBase#initInternal
org.apache.catalina.core.ContainerBase#reconfigureStartStopExecutor
创建内嵌的startStopExecutor执行器,用来处理部分容器类
⑤、executor.init()和mapperListener..init()
如果有自定义executors将会进行初始化
⑥、connector.init()
初始化连接
初始化adapter
org.apache.catalina.connector.Connector#initInternal
protocolHandler = Http11NioProtocol验证是否有效
org.apache.coyote.http11.AbstractHttp11Protocol#init
org.apache.coyote.AbstractProtocol#init
org.apache.tomcat.util.net.AbstractEndpoint#init
org.apache.tomcat.util.net.AbstractEndpoint#bindWithCleanup
org.apache.tomcat.util.net.NioEndpoint#bind
org.apache.tomcat.util.net.NioEndpoint#initServerSocket
创建serverSock对象,绑定端口,设置为非阻塞。服务端sockets启动
到这里便开启服务端Socket等待客户端连接
3、start
org.apache.catalina.startup.Bootstrap#start
3.1、StandardServer启动start
org.apache.catalina.startup.Catalina#start
这里也会调用到父类LifecycleBase#start方法
org.apache.catalina.util.LifecycleBase#start
org.apache.catalina.core.StandardServer#startInternal
3.2、StandardService启动start
开启services
开启一个startPeriodicLifecycleEvent,用来后台执行进行热加载和热部署
此时处于StandardService阶段,这里的lifecycleListeners 数量是0,所以setState方法什么都没做
3.3、StandardEngine启动start
开启engine
开启engine后
主要步骤是:executor、mapperListener、connector的开启
org.apache.catalina.core.StandardEngine#startInternal
在StandardEngine阶段,lifecycleListeners有EngineConfig监听器
org.apache.catalina.core.ContainerBase#startInternal
步骤包括:cluster、realm的start调用、获取孩子容器StandardHost
主要分析获取的孩子容器StandardHost
把得到的孩子容器封装成StartChild任务类,使用startStopExecutor线程池开启任务
3.3.1、executor启动start
executors线程池为null这里没有做什么
3.3.2、mapperListener启动start
org.apache.catalina.mapper.MapperListener#startInternal
org.apache.catalina.mapper.MapperListener#addListeners
listeners和lifecycleListeners添加MapperListener监听器,然后递归调用为孩子容器都添加这个MapperListener监听器
org.apache.catalina.mapper.MapperListener#registerHost
org.apache.catalina.mapper.MapperListener#registerContext
org.apache.catalina.mapper.MapperListener#prepareWrapperMappingInfo
对每个wrapper寻找对应的映射关系并添加到wrappers中
3.3.3、connector启动start
org.apache.catalina.core.StandardService#startInternal
org.apache.catalina.connector.Connector#startInternal
org.apache.coyote.AbstractProtocol#start
org.apache.tomcat.util.net.AbstractEndpoint#start
org.apache.tomcat.util.net.NioEndpoint#startInternal
创建executor工作线程池
poller线程启动
acceptor线程启动
3.4、StandardHost启动start
org.apache.catalina.core.ContainerBase.StartChild#call
lifecycleListeners包含HostConfig,所以在执行start之前
org.apache.catalina.util.LifecycleBase#start
org.apache.catalina.util.LifecycleBase#setStateInternal
发布一个lifecycleEvent = "before_start"事件
org.apache.catalina.util.LifecycleBase#fireLifecycleEvent
HostConfig监听器处理before_start事件
org.apache.catalina.startup.HostConfig#lifecycleEvent
check()方法是periodic事件,用来热部署。beforeStart就是before_start事件
org.apache.catalina.startup.HostConfig#beforeStart
得到webapps和conf/Catalina/loaclhost文件夹
org.apache.catalina.core.StandardHost#startInternal
pipeline添加阈值通过addValve
调用父类方法
StandardHost[localhost]寻找孩子容器,与StandardEngine过程相同
封装成StartChild交给startStopExecutor线程池处理
3.5、StandardContext启动start
此时lifecycleListeners包括(CopyOnWriteArrayList 3个):ContextConfig、StandardHost$MemoryLeakTrackingListener 、ThreadLocalLeakPreventionListener
org.apache.catalina.util.LifecycleBase#setStateInternal
发布LifecycleState.STARTING_PREP事件是before_start
org.apache.catalina.util.LifecycleBase#fireLifecycleEvent
对三个监听器LifecycleListener调用lifecycleEvent
org.apache.catalina.startup.ContextConfig#lifecycleEvent
org.apache.catalina.startup.ContextConfig#fixDocBase
调整docBase
org.apache.catalina.startup.ContextConfig#antiLocking
对antiResourceLocking是true的处理,默认是false
另外两个LifecycleListener什么都没做
StandardContext.startInternal()
org.apache.catalina.core.StandardContext#startInternal
①、设置resources
org.apache.catalina.core.StandardContext#setResources
把传入的resources赋值给this(StandardContext)的resources
②、resources的启动
org.apache.catalina.core.StandardContext#resourcesStart
StandardRoot实现了LifecycleBase,所以直接到startInternal方法
org.apache.catalina.webresources.StandardRoot#startInternal
org.apache.catalina.webresources.StandardRoot#createMainResourceSet
创建包含当前Context的路径的WebResourceSet并返回
org.apache.catalina.webresources.StandardRoot#processWebInfLib
jar包资源resources也添加到StandradRoot的allResources中
③、创建webappLoader类,并设置到当前Context的loader中
④、当前应用加载器loader,启动start
WebappLoader实现了LifecycleBase
org.apache.catalina.loader.WebappLoader#startInternal
org.apache.catalina.loader.WebappLoader#createClassLoader
根据ParallelWebappClassLoader类的构造方法,传入父类加载器参是URLClassLoader。创建一个WebappClassLoaderBase对象。传入的resources是StandardRoot对象
org.apache.catalina.loader.WebappClassLoaderBase#start
把resources中的/WEB-INF/classes和/WEB-INF/lib目录添加到localRepositories中
⑤、解析web.xml文件
org.apache.catalina.startup.ContextConfig#lifecycleEvent
org.apache.catalina.startup.ContextConfig#webConfig
org.apache.tomcat.util.descriptor.web.WebXmlParser#parseWebXml(org.xml.sax.InputSource, org.apache.tomcat.util.descriptor.web.WebXml, boolean)
org.apache.tomcat.util.digester.Digester#parse(org.xml.sax.InputSource)
reader = SAXParserImpl$JAXPSAXParser
解析到servlets、servletMappings、 filters、 filterMaps等属性中
⑥、寻找ServletContainerInitializer实现
org.apache.catalina.startup.ContextConfig#processServletContainerInitializers
加载后的实现放入到detectedScis集合中
得到HandlesTypes注解
⑦、processClasses、合并web-fragment.xml、合并tomcat-web.xml
⑧、JSP处理、把web.xml配置到Context
org.apache.catalina.startup.ContextConfig#configureContext
配置filter、filterMap、listener
SecurityConstraint、role、ContextService
servlet设置
ServletMapping、sessionConfig
配置PostConstruct、PreDestroy方法
ServletContainerInitializer实现添加到context中
⑨、回调ServletContainerInitializers的onStartup方法
3.6、部署应用
发布LifecycleState.STARTING是start的事件
org.apache.catalina.core.ContainerBase#startInternal
这里是StandardHost容器阶段。时机是在开始Host,把Host的孩子容器节点封装到StartChild任务类,在线程池startStopExecutor调用的,然后调用results的get回调方法,pipeline开始方法之后调用的
org.apache.catalina.startup.HostConfig#lifecycleEvent
设置copyXML(false)、deployXML(true)、unpackWARs(true)、contextClass(org.apache.catalina.core.StandardContext)的参数值
org.apache.catalina.startup.HostConfig#start
deployApps()部署应用
org.apache.catalina.startup.HostConfig#deployApps()
①、deployDescriptors
org.apache.catalina.startup.HostConfig#deployDescriptors
处理部署在conf/Catalina/localhost/文件夹下XML形式的context
如果没有部署过则封装成DeployDescriptor任务类放到InlineExecutorService执行
②、deployWARs
org.apache.catalina.startup.HostConfig#deployWARs
部署war文件,webapps目录下以.war结尾的
如果没有部署过则封装成DeployWar任务类放到InlineExecutorService执行
③、deployDirectories
处理webapps目录下的文件夹
org.apache.catalina.startup.HostConfig#deployDirectories
如果没有部署过则封装成DeployDirectory任务类放到InlineExecutorService执行
总结:
Tomcat的启动时通过启动类的main方法,主要有三步:初始化(init)、加载(load)、启动(start)
1、初始化类加载器和Catalina类
2、根据server.xml创建Digester,所定义的节点都会生成对应的对象,节点所处的层级也会解析出来,设置不同容器的pipeline基础的Value阈值。server、services、engine、executor、mapperListener、connector的初始化调用,其中最重要的是endpoint的serverSocket的创建
3、StandardServer、StandardService和StandardEngine、StandardHost、StandardContext等容器的启动。
①、由于都实现了生命周期抽象类,所以会先调用LifecycleBase方法。在LifecycleBase方法内会使用调用setStateInternal发布一个事件,根据当前所处的容器阶段会调用相对应的监听器实现类。之后再调用所处容器的生命周期方法。
②、StandardHost和StandardContext都是在父类容器查找孩子容器然后封装成一个任务类放入到线程池中执行启动的。
③、在容器启动中,其中StandardContext的启动是最核心一个也是复杂的一个。主要包括WebappLoader类加载器创建和启动、解析web.xml文件(把解析后的值放到WebXML对象)、ServletContainerInitializers的查找及启动、把WebXML对象封装到当前Context对象等过程
④、容器启动后,就要部署应用。有三种部署形式,也都是封装成任务类放入到线程池内执行。部署应用分为部署server.xml中定义的Context和部署webapp文件夹下的Context