知识小罐头06(tomcat8请求源码分析 中)

   更正上一篇一个小错误,Connector中首先是将socket请求过来的信息封装成一个普通的Request对象(上一篇我写成HttpRequest对象,失误失误,根本就木有HttpRequest这样的对象。。。),然后在Adapter中封装成一个HttpServletRequest进行处理,再丢给Container。。。。

  源码中可以清晰的看到:

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

  ok,这一篇我们就来整个的看看tomcat源码,简单过一遍,看一看里面是怎么运行的(我也会删减大量的非核心的代码)

  在看源码之前,我想说一点废话,由于我也是菜鸟,所以大神勿喷啊!

  我自己在看别人分析源码博客的最深的体会就是,别人博客中分析那一段一段的简洁而明了的源码,也许我们看的时候感觉还是比较容易的,有点懂了,差不多了;但是有没有一种感觉,过不了几天就印象模糊了,再过几天就忘的差不多了,下次几乎又要重新学一遍,贼坑!知道为什么吗?因为我们看的只是别人分析后的结果,没看到别人分析的过程,为什么别人分析的过程这么简洁漂亮而自己分析就是一团糟?为什么要这么分析?从哪个方面切入的?假如我自己去分析源码能找到切入点吗?所以很多新人一想到自己要分析源码就头疼,不知道从哪里入手,脑子很迷糊!

  让我想起了一句话:谁知盘中餐,粒粒皆辛苦!我们在看着别人花费了几个小时甚至几天时间才总结出的源码,而我们看起来顶多几十分钟,没有自己亲自去辛苦,当然体会不深啊!所以要养成自己分析源码的能力和适合自己的方法很重要,就好像学习,学习的知识固然重要,但更重要的是学习的方法,因为知识可能会被淘汰,但是方法却能伴随你一生!

  咳,废话说多了,继续今天的内容吧!我们先来下载Tomcat源码,我下载的版本是7.0.92,下载路径:https://tomcat.apache.org/download-70.cgi

  注意不需要你原来的Tomcat版本和这个源码版本一致,随便下载一份源码就好

知识小罐头06(tomcat8请求源码分析 中)

  下载之后解压,路径随意,但是我放在我的tomcat8目录里面

知识小罐头06(tomcat8请求源码分析 中)

1.搭建IDEA导入Tomcat8源码的环境

  我们平常都是运行我们的web项目进行调试,我们是感受不到Tomcat的存在的,只有报错的时候才有可能看到tomcat的有关信息!所以我们要想个办法把Tomcat变成一个类似我们web项目一样的寻在,我们不就可以愉快的调试并且还可以随意修改其中的内容了嘛!

  首先进入上面下载的那个源码文件夹,新建一个catalina-home文件夹和pom.xml文件

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

  

  其中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目录

知识小罐头06(tomcat8请求源码分析 中)

  然后我们进入IDEA,导入我们的源码项目(就是我们下载解压之后的那个文件夹)

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

  项目导入之后的目录应该这样的

知识小罐头06(tomcat8请求源码分析 中)

  我们要配置一些运行参数(其实就是指定一下我们那几个复制的和新建的文件夹的路径)

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

  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主页了!

知识小罐头06(tomcat8请求源码分析 中)

  其实到这里,基本的调试环境就搭建出来了,有兴趣的小伙伴可以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,最重要的是一个有参构造:

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

 这里不得不说一句,你觉得tomcat是怎么处理通过HTTP协议或者AJP协议发来的请求的?难道每次都是把这个协议拿过来用正则表达式慢慢的拆开,分析吗?这也太lower了吧!而且我们关注的不应该是协议本身,而是之后的逻辑,所以Tomcat中就指定了一些处理每个协议的类,假如你用HTTP协议发过来的信息,Connector就会用反射去实例化HTTP协议处理器对你进行解析,然后我们还可以对协议处理器里面再进行很多处理,相比之前的用正则表达式慢慢解析,简直不要太牛!

  

  我们再进去协议处理器看看:

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

  同时,在Endpoint中有个内部类是Acceptor,用于监听客户端请求

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

  我们也来看看Connector里面的processor是个什么鬼

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

  进入到process里面

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

 这个service方法又在这个类里进行了重写

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

  最后我们就看看Adapter中的service方法是干什么了(貌似就在本篇最前面就截了service的这个图。。。。)

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

知识小罐头06(tomcat8请求源码分析 中)

  这一篇就到这里了,看起来篇幅比较多,其实就是简单看了看Connector中各个组成部分的源码,下一节说说Container中的各个部分吧;最后应该会说一下整个Tomcat的启动流程!

上一篇:iOS KVO & KVC


下一篇:详解Tomcat系列(一)-从源码分析Tomcat的启动