一次请求的调用栈

从上面的线程调用栈,可以看到一次请求的调用过程.

注意中间那些以Valve结尾的类,它是Tomcat的里的责任链的组件.每一个组件,在Tomcat里都有自己对请求时可以进行自定义的处理.比如AccessLogValve就是记录访问日志的Valve.Tomcat自带的Valve,可以在包org.apache.catalina.valves下可以看到所有的列表.

测试代码

HelloServlet

package com.example;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Created by sky on 16-6-30.
 */
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("hello world");
        resp.getWriter().flush();
        resp.getWriter().close();
    }
}

web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
 Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  version="3.1"
  metadata-complete="true">

  <display-name>Welcome to Tomcat</display-name>
  <description>
     Welcome to Tomcat
  </description>

    <servlet>
        <servlet-name>hello-servlet</servlet-name>
        <servlet-class>com.example.HelloServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>hello-servlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

</web-app>

解析HTTP协议

Tomcat8默认使用 NIO 来处理HTTP了.看调用栈,可以知道,它通过org.apache.coyote.http11.AbstractInputBuffer.parseRequestLine()来解析请求头的.

请求头的内容,保存在以下这个字段里.

/**
* Pointer to the current read buffer.
*/
protected byte[] buf;

解析完成后,就创建了一个org.apache.catalina.connector.Request对象.

调用相应的责任链

通过Requset对象,再查找相应的Servlet(应该说是调用链,在Tomcat里,它用StandardWrapper对象表示, 在这里就是hello-servlet)(代码是 StandardWrapper wrapper = (StandardWrapper) getContainer(); ) 最后,Tomcat就会调用名为hello-servlet的Servlet来执行代码了.

Tomcat到SpringMVC

上面就已经执行到了我们自定义的hello-servlet了,只要将这个Servlet,替换成Spring的org.springframework.web.servlet.DispatcherServlet即可.它是SpringMVC的总控制及转发器(路由器,因为SpringMVC并不是使用传统的Servlet来对应每一个请求,而是使用Bean的某个方法来处理).