上文说到了tomcat中的main函数,那找到main函数之后很自然我们就有了一个疑问,那就是main函数究竟做了什么事,下面就让我们跟着main继续往下走
init方法
走进main 函数可以看到,除去try-catch,main函数的先进行了bootstrap.init()
;
在init方法里面我们可以看到,主要是反射的一些操作,反射加载了org.apache.catalina.startup.Catalina
类,反射调用了该类的setParentClassLoader
方法,代码如下图所示。然后我们找到这个方法跟着往下走可以看到,如名字所示,只是设置一个父 类加载器。这里要注意最后一行的: CatalinaDaemon 下面的启动方法就会用到。
load
回到main函数中,我们继续往下看,可以看到下面开始进行两步关键的步骤,load,start。点到load, start方法里面看的时候,可以看到,都是通过反射调用的catalinaDaemon里面的load 和 start方法。
我们跟着反射来到catalinaDaemon
的load
方法,从load
方法中排除掉寻找配置文件的过程,代码精简为以下主要几步:
public void load(){
initDirs();
initNaming();
Digester digester = createStartDigester();
digester.push(this);
digester.parse(inputSource);
getServer().setCatalina(this);
}
initDirs(),initNaming()
同学们可以点进去看一下,就是设置一些环境的属性,load方法的关键在于digester , 这是解析server.xml生成server的关键! 我们可以点进createStartDigester()
方法 ,如下图,可以看到里面对server.xml的格式进行的解析,中间我注释了一些代码,在图中所示的代码中,Server节点, Server下面的GlobalNamingResources节点,Server下面的 Listener节点,
有些小伙伴可能已经想到了,打开server.xml一看,果然!都是一一对应起来的,可以上下图对比着看。
首先push(this),把this当做根节点,即可以理解为,解析出来的server是挂在当前的catalina实例下,我们在解析完成时打个断点可以看到this下的server已经由xml转到了实体里面,至此load结束
start
在上一小节提到过,start()
方法,也是通过反射调用catalinaDaemon
里面的start
方法,将其简化之后,其实就一行代码
getServer().start();
即获取当前的server, 并调用当前server的start方法, 通过代码
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
可以看到,Server的实现类默认是 StandradServer
,调用Server的start方法,通过代码追踪可以看到,是先调用LifecycleBase
中的start
在LifecycleBase
中的start
方法中我们可以看到,调用了抽象的startInternal()
方法,代码如下图所示:
然后在StandradServer
中的startInternal()
中调用 service
的start
方法,如上分析类似,在StandardService
的startInternal()
我们可以看到调用了engine.start(),executor.start(),mapperListener.start()等等,如下图所示:
总结
本文只是简单深入了源码,随着深入,概念越来越多, Catalina,server , service , listener,lifecycle等等,直接从代码中很难看出这些概念(类)之间的关系,下一节我们一起来把tomcat源码导入idea。边debug,边看这个类之间的系统以及启动加载解析的过程