Tomcat 启动过程源码解析(一)

Tomcat 启动过程源码解析(一)

最近开始研究tomcat6.x的源代码,将读过和遇到的疑问及相关答案记录一下,以便日后回顾和总结。Tomcat的启动的主入口是org.apache.catalina.startup包下的Bootstrap类。在eclipse中右键执行这个java Application就能将tomcat容器启动起来了。这个启动过程主要是初始化了三个类加载器(commonLoader,catalinaLoader,sharedLoader)(通过看源码发现,其实tomat6和tomcat5不一样,三个类加载器其实都是返回了commonLoader,至于为什么,我们分析源码),然后初始化了一个catalina.java 定义Catalina类(这个类继承了Embedded,而Embedded又实现了StandardService接口,而StandardService持有了Container以及Connections数组,可见这个实例就是最外层提供服务的组件了)类的实例。

Bootstrap的成员变量有如下这么几个:


那么先看第一个红框,这里定义了两个配置项变量分别是catalina.home和catalina.base. 这两个变量我们可以在JVM的启动参数里指定例如:


那么这两个变量是干嘛的呢?这个我看代码的时候挺迷茫的,在debug的时候发现catalina.home 和catalina.base被设置成了一样的值,随后我就上网查了一下发现原来是这样的:catalina.home和catalina.base这两个属性仅在你需要安装多Tomcat实例而不想安装多个软件备份的时候使用,Tomcat目录下只有 bin 和lib 目录可以被多个实例共用,其它目录conf、logs、temp、webapps 和work每个Tomcat实例必须拥有其自己独立的备份。Catalina.home指向公共信息的位置,就是我们上面提到的bin和lib目录,catalina.base则需要制定tomcat的私有信息,也就是剩下的那几个目录。

        第二个红框以及第三个红框是其持有了自己的实例以及Catalina的实例。最后一个红框则是其定义了三个类加载器。那么这个三个类加载器有什么区别呢?tomcat6的默认配置只是指定了commonLoader的文件路径,而catalinaLoader 和sharedLoader的文件路径都设置为空。而因为commonLoader为catalinaLoader和sharedLoader的父级类加载器,所以catalinaLoader和sharedLoader就被设置成了和父级类加载器commonLoader一样的值。那么这个类加载器的文件路径是在哪配置的呢?这个是配置在资源文件中的,也就是我们的catalina.properties文件中。


Ps:Tomcat5后两个加载器的配置应该不为空的。

让我再看看里面的方法。当然从main方法开始看:

      

       首先new了一个自己,然后调了初始化方法。初始化方法如下:

      

       首先是初始化了catalinaHome,catalinaBase,以及相关的类加载器,我们看一个setCatalinaHome方法:

      

这个就是从我们系统参数里面读取catalina.home的值,如果catalina.home的值没有配置,就默认使用了user.dir(及我们项目的目录)的属性值。Catalina.base 由于我们没有配置则其默认和catalina.base取值一样。随后是初始化了类加载器。


不过由于后两个类加载器没有配置文件目录,所以直接反回了器父级的类加载器commLoader。那么commonLoader的创建过程是怎么样的呢?因为commonLoader的初始化是来加载上文中提到的catalina.properties中配置的,其配置项是这样的:

common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar前面的占位符 ${catalina.base} ${catalina.home}会用我们配置的JVM参数替换掉。ClassLoader在初始化的时候会将配置的jar包的路径的url数组传给类加载器。由于配置的路径的后缀结尾包含这样三种不同的路径:分别是以*.jar结尾的,这在tomcat6中被定义为IS_GLOB类型,还有IS_DIR,IS_JAR,IS_URL,用来分别定义不同的路径类型以便jar路径的定位。调试的截图如下所示:



每种文件的路径都定义了一种类型,分类别的取得其中jar的路径是tomcat的实现机制。类型的定义是这个:


1就代表这个路径是一个jar文件。2就代表这个路径是以 *.jar结尾的路径。3.代表一个路径的url。有了jar包的路径信息以及相关路径的类型信息,这个传给classloader的工厂就能初始化一个classLoader了:


初始化commonLoader的时候parent设置为null。至于classLoader怎么来加载jar中的class以及什么时候加载的这个会在后续的总结中研究一下写出来,至此就初始化了一个classloader,初始化完毕之后这个类也交给了JMX的bean来管理了,JMX研究的比较少,这个之后会学习以及分享。相关代码如下:

之后我们继续分析,之后的代码是这样的:

      

之后会将初始化好的类加载器设置到当前在跑的主线程上。之后会议一种安全的方式加载一些jar包中的类。SecurityClassLoad.securityClassLoad(catalinaLoader);


这些都完事之后会用catalinaLoader加载我们最重要的Catalina类。并实例化一个Catalina对象并通过反射的方式将java.lang.ClassLoader作为其父级的类加载器。


读到这的时候我产生了一个疑问,为什么用反射设置呢,既然实例已经有了,为什么不直接调用器方法呢,不过试一下我才反应过来,原来此时的实例是Object类型的,所以不用强制转换的话就需要用反射了。至此这个BootstrapDaemon就初始化完成了。

       之后我们继续回到main函数继续解析:

      

              接下来会根据我们传入的命令,来执行这个daemon的操作,当然我们首先传入了start。那么会调用daemon的load以及start方法。那么这个load和start都做了些什么呢?那么我就具体来解析一下。

             

Load方法会通过反射调用catalina实例的load方法,那么看一下catalina的load做了些什么:

      

       首先初始化了目录参数以及命名服务的参数,之后我们主要看一下 digester 类,这个createDigester真实干了很多活:

      

这个digest定义了要要加载的配置文件的一些结构。Digest主要是能将xml文件中的配置转换成对象。那么它解析的是哪个配置文件呢?当然就是我们的server.xml

文件。

      

Server.xml中定义了我们的监听器,连接器,servlet容器等配置信息,这些信息会被Digester转换成相关的对象保存在我们的catalina实例中对外提供服务。Digester我掌握的知识还不够,后面会单独拿出来学习总结一下。不过根据debug发现当执行到这步的时候初始化了我们catalina实例中的 server信息。这说明当调用server的start方式时,我们的所有 服务都能启动起来了。至此这个load就加载完了。

       之后回到我们的main函数:

      

果不其然调用了我们catalina实例的start方法。


       画红框的这部分是启动的主要过程,启动了catalina实例中的server属性。这个Lifecycle是tomcat里面的声明周期管理机制,这里面的内容还是比较多的,具体原理部分还得下次再具体学习下。那么既然知道了是调用了我们server中的start方法,那么就来研究一下server实例中的start方法。这个server其实是StandardService这个类中定义的,Catalina继承了Embedded接口,Embedded继承了StandardService,这样catalina实例就有了server属性。废话不说那就来看看server的start方法干了什么吧。经过debug分析Server这个接口指向的实例是StandardServer

这个实例,这个server是什么接口呢,那么我们就可以看一下我们的server.xml文件了。这个server里面定义了诸多的监听器,JNDI命名服务,定义了名叫catalina的服务,服务里又定义了http连接器(这个服务是StandardService的实例)定义了AJP连接器,以及定义了servlet容器.到底是不是这样,我们可以debug看看:

      

由此可以看到和我们server.xml配置文件中配置的一模一样。

standartServer调用了standardService的start方法,standardService 又依次调用了里面定义的监听器,连接器以及容器的start方法,为什么可以这样逐层调用start方法呢,这个就是tomcat的声明周期lifecycle管理方面的知识了,下回好好学习总结下。至此我们在回到我们的main函数.可以看到main中start这个分支已经结束了。我们的服务也起来了。

        以上就是我从Bootstrap的启动类中看到的东西,当然这个只是我看到,还有许多原理性及结构性的东西没有分析出来,这个我会在下此总结中逐个学习总结。

       PS: 在看代码的时候遇到了一个看不懂的地方,请教了一下其影,也自己研究了一下,就是这个:


类加载器在加载的时候会即在这种类core.ApplicationDispatcher$PrivilegedForward

这种写法的意思是表示ApplicationDispatcher的内部类PrivilegedForward,而对于

core.ApplicationContextFacade$1表示的是core.ApplicationContextFacade中的匿名类。不懂的给大家普及下,懂得就一笑而过吧,亲!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值