tomcat的启动过程分析

  说起Tomcat,从事java开发的程序猿同学们必然不陌生,那只不太可爱的猫嘛。我们怎么用的呢,部署程序,设置参数。。然后去tomcat bin目录下启动。那启动tomcat的时候,他又做了什么呢?让我们从源码的角度了解下tomcat的启动过程。

  • 先来回忆下tomcat架构

      在这之前,我先去百度一张tomcat架构图。

搁着放着吧,tomcat的架构一目了然,与server.xml 层次一一对应,暂且不提,让我们接着说tomcat的启动

  • Tomcat启动脚本做了什么?

从哪里启动我们就从哪里开始,好,我们来看下startup.bat脚本

虽然不是很熟悉批处理命令,但是这个脚本还看的懂,干了啥呢?就是执行了catalina.bat命令,参数只有一个start,因为我们平时启动也不会加参数。那接着再看下catalina.bat脚本,这个脚本有些复杂,看不懂,嘿嘿。那咱们就找看的懂地方。

首先前面一大段检查设置各种参数环境变量等等。我们从这里看,我们看到如果第一个参数是start,然后去执行doStart.

 

接着看下doStart要执行的命令:

  设置了一个变量,然后判断第二个参数是不是-security,咱们没有第二个参数因此忽略,那设置了一个什么变量呢,看起来好像是执行某一个程序。%_RUNJAVA%是何物呢?其实就是下面这个

哦,恍然大悟,这是要执行class文件啊,执行哪个呢,接着往下看。

    前面几行是设置参数,将所有参数用一个变量代替,然后下面两个判断,这两个是不成立的,可以看下上面变量设置。然后刚才设置的_EXECJAVA来了,前面各种乱七八糟的参数,有个变量MAINCLASS,这就是要执行的类。

到此真相大白,执行了Bootstrap,参数为start,你以为这篇博客就此结束了吗?没有,这仅仅是开始。

  • 从Bootstarp入手

首先说明一点tomcat启动源码不少,我们从重点来看tomcat启动过程。

 

 

首先调用了bootstrap初始化方法,下面是init方法全部代码。

    首先设置了catalinahome,和catalinabase,这两个是什么呢?Catalinahome是咱们tomcat安装目录,catalinabase是工作目录。Tomcat是可以是多实例的,如果不指定catalinahome=calinabase,脚本也可以看的出来,初始化这两个是为了找到咱们启动tomcat,然后初始化了3个classloader,这个classloader呢详细说起来就复杂了,这不是今天讨论的重点,他们就是用来加载tomcat和web应用类的。

接着看下面,我们传入的参数是start,所以要执行的是下面三行:

这三行分别通过反射调用了catalina类中setAwait,load,init,我们先看load,看重点

再看createStartDigest

     

      这个方法做了什么呢?实例化了一个Digester,然后一直再一次的在调用addObjectCreate,addSetProperties,addSetNext,addRule...这些方法,最终返回这个对象。虽然里面有些东西我认识,但完全看不懂这个类是啥,这是干嘛呢?于是乎,我们去找它爸爸问问。

    噫,好像在哪里见过?想想哪里见过呢?实在想不起来就百度!然后,就明白了,原来这是咱们sax解析xml要继承的类,这是要进行xml解析啊。那问题又来了,解析在哪呢?解析 的哪个xml呢?解析的时候又做了啥呢?带着问题接着往下看。

  下面构造了一个InputSource,看下构造过程,首先看下有没有configFile文件

    又是熟悉的味道,如果找不到,再去寻找server-embed.xml,注释也说了,这个文件再catalina.jar,这个包启动时被加入到classpath,很明显我们一般不需要这个。

如果没有配置文件,则报错,如果有,接下来最终调用digester.parse()进行解析,最终以sax的方式解析xml

  • 分析解析server.xml过程

我们接着看下解析server.xml时最重要的回调函数,首先来看startElement,该方法解析到元素开头调用:

   方法updateAttributes是替换取配置文件属性值的属性值,形如${xxxx},bodyText是指文本内容,忽略因为server.xml没有文本。Match为匹配层次,比如解析到service元素时,match就为Server/Service.最重要的一部分来了,通过match,获取了一个叫rules的list,然后分别调用了Rule的begin的方法,那么这个list在哪里来的呢?

接着看lookup

我们可以看到是通过match在一个叫cache的变量查找的,这个cache是个map,很明显通过key叫value.在看下cache什么时候put元素的呢?

可以看到在add中添加了元素。那就再找下add什么时候调用的,我们发现在Digester类中调用了add,

  又回到这个类了,再接下去找发现调用地方有点多,咋办呢?我们猜想是不是在构造这个对象的时候调用的呢?好,让我们回去再看看。

点开几个方法看看.:

    果不其然,规则太多了,不一个个说了,挑几个看看,道理都是一样的,由上面的方法可以看到pattern实际为cache的key,你会发现加入的key,就是各个元素在server.xml层次关系,后面加入的对象都继承了Rule接口,看ObjectCreateRule中begin方法

  很明显这个方法利用反射创建了一个对象,以digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer",

 "className");为例,首先查找xml  server元素中有没有className属性,如果有创建className对应属性值的对象,如果没有则创建org.apache.catalina.core.StandardServer对象。然后把该对象放在一个列表中维护,你可以把他理解为栈,解析完元素,再出栈,为何要维护呢?是为了元素能找到父容器或自身并调用其中方法。

看下一个规则SetPropertiesRule

   这名字起的不给点挑战啊,没错,这个是设置相应对象属性的,设置谁的属性呢?之前维护的栈便起了作用。比如这个 digester.addSetProperties("Server");在实例化Server之后,将 port等属性设置<Server port="8005" shutdown="SHUTDOWN">到相应实例。怎么做到的呢?通过digester取出最上次对象,server实例化完以后放入了栈顶还记得不?然后反射调用其中set方法,调用之前会检查是不是FakeAttribute,这里就是className .digester.isFakeAttribute(top, name) .

   最后看SetNextRule,SetNextRule没有重写begin方法,所以解析元素开始时,不会又任何操作,但是重写了end.在解析到元素尾时调用。

   同样在endElement中,找到节点元素对应的规则,然后调用规则的body和end方法,最后完成解析,有兴趣的自己看吧,挺有意思的。

整个server.xml解析到此结束,实例化了各个组件并设置相关属性及关联关系。

解析完了,咱们load方法到哪里了应该忘了吧。

   看图,还有一个重要方法getServer().init().tomcat的server实现类为StandardServer,既解析完成后调用了StandardServer的init()方法,做了什么呢?有时间再一起学习下。晚安!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值