知识小罐头06(tomcat8简单源码分析 中)
更正上一篇一个小错误,connector中首先是将socket请求过来的信息封装成一个普通的request对象(上一篇我写成httprequest对象,失误失误,根本就木有httprequest这样的对象。。。),然后在adapter中封装成一个httpservletrequest进行处理,再丢给container。。。。
源码中可以清晰的看到:
ok,这一篇我们就来整个的看看tomcat源码,简单过一遍,看一看里面是怎么运行的(我也会删减大量的非核心的代码)
在看源码之前,我想说一点废话,由于我也是菜鸟,所以大神勿喷啊!
我自己在看别人分析源码博客的最深的体会就是,别人博客中分析那一段一段的简洁而明了的源码,也许我们看的时候感觉还是比较容易的,有点懂了,差不多了;但是有没有一种感觉,过不了几天就印象模糊了,再过几天就忘的差不多了,下次几乎又要重新学一遍,贼坑!知道为什么吗?因为我们看的只是别人分析后的结果,没看到别人分析的过程,为什么别人分析的过程这么简洁漂亮而自己分析就是一团糟?为什么要这么分析?从哪个方面切入的?假如我自己去分析源码能找到切入点吗?所以很多新人一想到自己要分析源码就头疼,不知道从哪里入手,脑子很迷糊!
让我想起了一句话:谁知盘中餐,粒粒皆辛苦!我们在看着别人花费了几个小时甚至几天时间才总结出的源码,而我们看起来顶多几十分钟,没有自己亲自去辛苦,当然体会不深啊!所以要养成自己分析源码的能力和适合自己的方法很重要,就好像学习,学习的知识固然重要,但更重要的是学习的方法,因为知识可能会被淘汰,但是方法却能伴随你一生!
咳,废话说多了,继续今天的内容吧!我们先来下载tomcat源码,我下载的版本是7.0.92,下载路径:https://tomcat.apache.org/download-70.cgi
注意:不需要你原来的tomcat版本和这个源码版本一致,随便下载一份源码就好
下载之后解压,路径随意,但是我放在我的tomcat8目录里面
1.搭建idea导入tomcat8源码的环境
我们平常都是运行我们的web项目进行调试,我们是感受不到tomcat的存在的,只有报错的时候才有可能看到tomcat的有关信息!所以我们要想个办法把tomcat变成一个类似我们web项目一样的寻在,我们不就可以愉快的调试并且还可以随意修改其中的内容了嘛!
首先进入上面下载的那个源码文件夹,新建一个catalina-home文件夹和pom.xml文件
其中pom.xml里面的内容如下,可以直接进行复制
<?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.0</modelversion> <groupid>org.apache.tomcat</groupid> <artifactid>tomcat8.5</artifactid> <name>tomcat8.5</name> <version>8.5</version> <build> <finalname>tomcat8.5</finalname> <sourcedirectory>java</sourcedirectory> <testsourcedirectory>test</testsourcedirectory> <resources> <resource> <directory>java</directory> </resource> </resources> <testresources> <testresource> <directory>test</directory> </testresource> </testresources> <plugins> <plugin> <groupid>org.apache.maven.plugins</groupid> <artifactid>maven-compiler-plugin</artifactid> <version>2.3</version> <configuration> <encoding>utf-8</encoding> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupid>junit</groupid> <artifactid>junit</artifactid> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupid>org.easymock</groupid> <artifactid>easymock</artifactid> <version>3.4</version> </dependency> <dependency> <groupid>ant</groupid> <artifactid>ant</artifactid> <version>1.7.0</version> </dependency> <dependency> <groupid>wsdl4j</groupid> <artifactid>wsdl4j</artifactid> <version>1.6.2</version> </dependency> <dependency> <groupid>javax.xml</groupid> <artifactid>jaxrpc</artifactid> <version>1.1</version> </dependency> <dependency> <groupid>org.eclipse.jdt.core.compiler</groupid> <artifactid>ecj</artifactid> <version>4.5.1</version> </dependency> </dependencies> </project>
然后我们打开我们新建的catalina-home目录
然后我们进入idea,导入我们的源码项目(就是我们下载解压之后的那个文件夹)
项目导入之后的目录应该这样的
我们要配置一些运行参数(其实就是指定一下我们那几个复制的和新建的文件夹的路径)
main class(这个类很重要,是tomcat的启动类):org.apache.catalina.startup.bootstrap
vm options(就是指定一些日志、工作文件夹等的路径):-dcatalina.home=catalina-home -dcatalina.base=catalina-home -djava.endorsed.dirs=catalina-home/endorsed -djava.io.tmpdir=catalina-home/temp -djava.util.logging.manager=org.apache.juli.classloaderlogmanager -djava.util.logging.config.file=catalina-home/conf/logging.properties
ok,再点击应用,就可以了,然后你可以右上角运行tomcat,肯定会报错,就是一个test类里面的什么cookie错误,没啥用,你直接把那整个类都注释掉。(ctrl+a全选。ctrl+/注释);注意,可能会有个很低级的错误,就是提示你没有sdk或者jdk,你只需要在左上角file------>project structrue------->project,在这里选择你的jdk版本,没有这个错误更好。
然后你可以正常启动,但是在浏览器访问tomcat的url路径:localhost:8080又会报一个500的异常,原因是空指针异常,是一个什么jasperinitializer没有被加载,这个需要我们自己该一下源码手动让它加载。
在idea中,ctrl+n,搜一个contextconfig的类,在下图的地方加入 context.addservletcontainerinitializer(new jasperinitializer(),null); 这样就可以正常访问tomcat主页了!
其实到这里,基本的调试环境就搭建出来了,有兴趣的小伙伴可以ctrl+n搜一下bootstrap类,找到main方法,在init,load,start三个方法那里打断点进行调试啊,看看tomcat启动原理,我后面有时间的话也会一起看一看的!
2.简单从源码的角度看一看tomcat组成
还记得上一篇说的tomcat的那些组成部分吗?这里还是大概理一下:
tomcat最核心的是conf/server.xml这个配置文件,这个配置文件中每个标签都代表一个tomcat的组成部分,最外面的是一个server标签,其实可以简单的把这个server标签代表我们的tomcat服务器,便于理解。一个tomca实例t只有一个这个server标签
次一级的就是service标签,这个标签可以配置多个,它是由两部分组成,connector和container
其中connector可以配置多个, 分别为处理http协议的和处理ajp协议的(至于还能不能处理其他的协议我也不怎么清楚,有时间研究一下),以http的connector为例,这个connector里面就是一个协议处理器(protocolhandler),这个协议处理器里面由三部分组成:endpoint(底层用socket接收客户端请求,并调用processor处理),processor(将用户的socket请求解析之后,包装成一个普通的request对象和response对象,再调用adaptor),adaptor(就是将普通的request和response对象封装成httpservletrequest对象,再想办法丢到container中)
container里面是一个大容器里面套着小容器,小容器里面还有小容器的这样的一个结构,依次为engine(一个service只有一个),host(可以多个),context(可以多个),wrapper(可以多个),当请求到了之后,会有一个管道---阀门机制,让这个请求从最外面的容器经过一道道阀门到最里面的容器,最后就到servlet的service方法运行,返回!
接下来,我们就站在代码的角度,大概看看这些组成部分用代码是什么样子的,后面再说整个tomcat的运行原理:
首先是connector,最重要的是一个有参构造:
这里不得不说一句,你觉得tomcat是怎么处理通过http协议或者ajp协议发来的请求的?难道每次都是把这个协议拿过来用正则表达式慢慢的拆开,分析吗?这也太lower了吧!而且我们关注的不应该是协议本身,而是之后的逻辑,所以tomcat中就指定了一些处理每个协议的类,假如你用http协议发过来的信息,connector就会用反射去实例化http协议处理器对你进行解析,然后我们还可以对协议处理器里面再进行很多处理,相比之前的用正则表达式慢慢解析,简直不要太牛!
我们再进去协议处理器看看:
同时,在endpoint中有个内部类是acceptor,用于监听客户端请求
我们也来看看connector里面的processor是个什么鬼
进入到process里面
这个service方法又在这个类里进行了重写
最后我们就看看adapter中的service方法是干什么了(貌似就在本篇最前面就截了service的这个图。。。。)
这一篇就到这里了,看起来篇幅比较多,其实就是简单看了看connector中各个组成部分的源码,下一节说说container中的各个部分吧;最后应该会说一下整个tomcat的启动流程!
上一篇: java实现抽奖功能解析
下一篇: 你敢叫