儿时读三国,纯属看热闹,只懂英雄豪气。不想更多的,到后来明白了一些道理,读懂却已是中年人。天下大势,分久必合,合久必分。向往曹操:挥师百万,号令天下。佩服司马懿:一路隐忍,最后给予致命一击,重拾人生巅峰。刘备那身在局中,却始终能看清局势的本事。吕布被称为三姓家奴,投靠人更多的刘备为何没有冠以如此称呼?因为他不曾背叛过自己投靠的势力。
相信大家都听过水镜先生司马徽预言的这句话,卧龙凤雏,得一人可以得天下,这个“卧龙”便是诸葛亮,这个“凤雏”则是庞统。刘备卧龙和凤雏都得到了,为什么最后还是得不到天下呢?其实,这句话的后面还有一句,子初孝直,若亡一人则汉室难兴。
我们在上一节中已经介绍了Tomcat功能及架构分析,本篇主要是对其源码的调试及其分析纪要:
源码调试搭建
首先需要下载tomcat的源码及其构建包,可以在其官网自行下载,如下我用的8.5.58版本的:
然后分别解压,看下src目录结构:
接下来我们会使用maven来构建工程,所以需要在src根目录下新建一个pom文件,添加如下内容:
xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0modelVersion><groupId>com.yilimaizigroupId><artifactId>tomcat8artifactId><version>8.5version><name>tomcat源码调试name><properties><configuration.version>1.9configuration.version><maven.compiler.target>1.8maven.compiler.target><maven.compiler.source>1.8maven.compiler.source><java.version>1.8java.version><project.build.sourceEncoding>UTF-8project.build.sourceEncoding>properties><dependencies><dependency><groupId>javax.xmlgroupId><artifactId>jaxrpcartifactId><version>1.1version>dependency><dependency><groupId>javax.xml.soapgroupId><artifactId>javax.xml.soap-apiartifactId><version>1.4.0version>dependency><dependency><groupId>wsdl4jgroupId><artifactId>wsdl4jartifactId><version>1.6.2version>dependency><dependency><groupId>org.eclipse.jdt.core.compilergroupId><artifactId>ecjartifactId><version>4.5.1version>dependency><dependency><groupId>antgroupId><artifactId>antartifactId><version>1.7.0version>dependency><dependency><groupId>org.easymockgroupId><artifactId>easymockartifactId><version>3.4version>dependency>dependencies><build><finalName>tomcat8.5finalName><sourceDirectory>javasourceDirectory><resources><resource><directory>javadirectory>resource>resources><plugins><plugin><groupId>org.apache.maven.pluginsgroupId><artifactId>maven-compiler-pluginartifactId><version>3.1version><configuration><encoding>utf-8encoding>configuration>plugin>plugins>build>project>
接着在src根目录下新建source目录,把下载的构建包解压后的conf和webapps目录整个copy到其source目录下;
然后在idea中配置其启动类BootStrap的启动参数:
在VM options 中添加配置:
相关路径请以实际的环境更改即可。
最后我们便可启动应用程序,启动完后打开浏览器访问8080端口
会发现报错了,这是因为jsp转servlet的引擎没有初始化导致的,此时需要在
ContextConfig类中的
configureStart
方法里添加如下的代码即可:
context.addServletContainerInitializer(new JasperInitializer(),null);
重新启动,再次访问即可,熟悉的页面出现了:
源码分析-启动
还记得上一节中Tomcat功能及架构分析提到了很多的组件吗?tomcat要启动,肯定要把组件实例化(如实例创建-->实例销毁)。tomcat中那么多组件,为了统一规范他们生命周期,tomcat抽象出了LifeCycle生命周期接口。
我们一起来看下这个接口:
果不其然,定义了一系列的生命周期的接口,在右侧看到了我们熟悉的组件都实现了LifeCycle接口。
首先我们来看下构建包bin目录下的startup.sh的内容
发现最后shell脚本调用的是catalina.sh的脚本并传入了start参数,我们继续跟踪,在catalina的脚本里,最终执行的是:
以上变量参数就不带大家分析了,有兴趣可以自行查看~我们主要看
Bootstrap
那一行内容,及其开头的 $_RUNJAVA变量,其他是辅助参数,在
setclasspath.sh 脚本中我们看到了$_RUNJAVA变量的定义,实际是java命令
所以startup.sh启动命令最终就等效于
java org.apache.catalina.startup.Bootstrap start
由此我们也知道了在
Bootstrap
类中一定有个main方法:
由此,我们正式进入了启动类的流程分析。
初始化类加载器,然后实例化Catalina对象,并赋值给catalinaDaemon
继续回到主函数:(把bootstrap实例赋值给daemon)
接下来就分两步骤走了,一个是初始化加载过程,一个start启动过程。我们先看下load方法:
发现通过反射调用了catalina的load方法,因内容较多,说下主要做了哪些动作:通过
Digester
来解析server.xml内容
然后初始化server
接下来就涉及到之前提到的生命周期的管理了,LifeCycle
其抽象类实现
LifeCycleBase
定义了生命周期的初始化模版方法:
所有实现Lifecycle接口的组件,初始化先调用Lifecyclebase的init方法,然后在调用各自组件实现的
initInternal
方法,我们看其server的实现
通过server组件,拿到其子组件service,我们在上一节也分析过,一个server下可以有多个service组件,所以这边用的是循环去初始化。按照我们刚才说的模版方法,会调用lifecyclebase的init方法,然后到standardservice的
initInternal
方法
这边分两个组件各自初始化了,在上节中我们知道service下面有connector和container子组件,所以这边分别对其初始化。我们先看engine的初始化过程,还是会遵循上面的模版方法过程,我们直接到
StandardEngine
继续进入super.initInternal方法
会发现此时并没有初始化容器,只是new了一个线程池,便于在start过程中快速的创建需要的容器。
我们继续回到service里的connector的初始化过程,流程依旧按照模版方法最终进入到connector的
initInternal
首先会把协议处理器会adapter绑定,然后协议处理器初始化,tomcat8默认是http11的协议
到其父类中
来到
最终来到
我们看到,此时绑定了端口,用的是java的NIO的socket知识,这里面就涉及到tomcat的IO模型了,BIO,AIO,NIO,NIO2等等,有兴趣可以了解下。
至此主函数load过程基本结束了,最后用一幅图来总结下:
总结
今天学习的内容较多,也只是把主流程介绍了下,涉及到的细节还需要多动手实践,算是揭开tomcat神秘面纱的起步,接下来还会有start过程分析,一个http请求进来,tomcat是怎么处理的等两篇的内容分析。希望带有兴趣的小伙伴入门。
长按关注,欢迎一起探讨技术