调试分析
由于源码一步一步调试效率太低了,所以我这里就直接在自定义的Filter类上的doFilter方法中打上断点
先通过线程栈来大致看一下,请求的过程中执行了哪些步骤
每一个方法点进去,然后给它整理成一张表格,如下:
分析:
- 上图执行执行次数最多的方法是invoke
- 所有调用invoke方法的类都以Valve结尾
- 调用invoke方法都是通过getNext和getFirst
第一步
首先从invoke方法执行多的地方开始,我们来分析一个Valve这个到底是什么东西?
通过上面,我们可以看出,调用invoke方法的所有的类,都是以Valve结尾的,说明这些类都有valve特征?那到底valve有什么特征呢?
Valve下面有一个ValveBase基类,所有的类都继承于该基类,并且Valve下面有两个重要的方法,getNext 和 invoke
public interface Valve { public Valve getNext(); public void setNext(Valve valve); public void invoke(Request request, Response response) throws IOException, ServletException; // 省略其它... }
根据类中的描述,valve所有的方法都和pipeline有关,那么pipeline到底是什么东西呢?
pipeline意为管道,valve为阀门,这两个东西当然密不可分了。但是还是不明白这两个东西是什么?具体有什么功能?
我们先来看一下Pipeline接口
public interface Pipeline { public Valve getBasic(); public void setBasic(Valve valve); public void addValve(Valve valve); public Valve[] getValves(); public void removeValve(Valve valve); public Valve getFirst(); public boolean isAsyncSupported(); public Container getContainer(); public void setContainer(Container container); }
既然有管道、有阀门,那是不是应该也有一个容器的东西,那这样一来就好理解了,每一个请求好比像是水,它想要进入到容器里是需要通过管道,然后经过阀门的层层过滤,最后到达容器。从这也体现了Tomcat的设计者们将现实生活中的实例引入到程序中的设计思想。
第二步
了解了valve这个是什么东西,我们接下来看看它底下是如何层层过滤的?
Valve过滤顺序从上至下
- StandardEngineValve
- AccessLogValve
- ErrorReportValve
- StandardHostValve
- NonLoginAuthenticator
- StandardContextValve
- StandardWrapperValve
从上面标红的几个关键字能看出什么来不?
依次向下传递:Engine --> Host --> Context --> Wrapper 这个是不是和我们的server.xml 文件Engine组件的层级关系一样的
即,当通过http请求 http://localhost:8080/jsp-web/test/1 访问tomcat的时候执行如下步骤:
- 通过localhost主机名称找到Host
- /jsp-web找到对应的Context
- 通过/test/1 找到对应的Wrapper,最终就能访问到你写的Filter
备注:上面的AccessLogValve,为访问日志,记录每次访问的url。
第三步
在Http11NioProcessor类中的process方法中,通过 getAdapter() 获得CoyoteAdapter对象,然后调用service方法
接下来我们来看一下CoyoteAdapter这个类是什么东西?coyote 是丛林狼的意思,为什么tomcat设计者要把这个命名为一种动物的名称?
关于官网对coyote介绍:
Coyote HTTP / 1.1连接器元件代表一个支持HTTP / 1.1协议的连接器组件。除了执行servlet和JSP页面的功能外,它还能够充当独立的Web服务器。该组件的特定实例侦听服务器上特定TCP端口号上的连接。可以将一个或多个此类连接器配置为单个服务的一部分,每个连接器都转发到关联的引擎以执行请求处理并创建响应。
- CoyoteAdapter这个类位于org.apache.catalina.connector 包下
- tomcat也有一个tomcat-coyote.jar包
- tomcat的Request有两个,一个是coyote包下的,另一个是connector包下的。getAdapter().service(request, response) 方法中request参数属于coyote包下的,它下一步要执行的invoke(request, response) 方法中request参数属于connector包下的
- Http11NioProtocol 和 Http11NioProcessor 在tomcat-coyote.jar包下
从上面的几个特点得出结论:
- CoyoteAdapter 与connector连接有关
- CoyoteAdapter 是一个适配器
- CoyoteAdapter 是tomcat-coyote框架的桥梁
总结
通过上面步骤,给它简单画了一个流程图:
Tomcat请求流程如下:
当用户发送HTTP请求http://localhost/context/wrapper/1
- NioEndPoint接收到请求(执行其内部类SocketProcessor的run方法);
- Http11NioProtocol$Http11ConnectionHandler处理
- Http11NioProcessor处理
- CoyoteAdapter将coyote类型Request/Response转换成Servlet类型的Request/Response对象,把请求交给Pipeline管道
- Engine阀门开始处理,拿到对应的Host,开始将请求交给Host处理;
- Host阀门开始处理,拿到对应的Context,开始将请求交给Context处理;
- Context阀门开始处理,拿到对应的Wrapper,开始将请求交给Wrapper处理;
- Wrapper阀门开始处理,拿到对应的FilterChain,交给FilterChain处理;
- FilterChain中包含了一个过滤器filters的数组,从该数组中的第一个Filter开始依次执行(filters是在加载web.xml文件的时候设置进去的,这也是过滤器的执行顺序会按照web.xml配置的顺序的执行原因);