调试tomcat9.0.19源码

本文所用到的环境:

  • IntelliJ IDEA
  • Apache Maven 3.3.9
  • jdk1.8

1. 查看Tomcat版本

$TOMCAT_HOMEinversion.bat

D:Program_Filesapache-tomcat-9.0.19in>version.bat
Using CATALINA_BASE:   "D:Program_Filesapache-tomcat-9.0.19"
Using CATALINA_HOME:   "D:Program_Filesapache-tomcat-9.0.19"
Using CATALINA_TMPDIR: "D:Program_Filesapache-tomcat-9.0.19	emp"
Using JRE_HOME:        "D:Program FilesJavajdk1.8.0_77"
Using CLASSPATH:       "D:Program_Filesapache-tomcat-9.0.19inootstrap.jar;D:Program_Filesapache-tomcat-9.0.19in	omcat-juli.jar"
Server version: Apache Tomcat/9.0.19
Server built:   Apr 12 2019 14:22:48 UTC
Server number:  9.0.19.0
OS Name:        Windows 10
OS Version:     10.0
Architecture:   amd64
JVM Version:    1.8.0_77-b03
JVM Vendor:     Oracle Corporation
D:Program_Filesapache-tomcat-9.0.19in>

这里使用的是JDK1.8,如果不是JDK1.8,则需要查看tomcat的兼容,选择适合自己的版本

2. 下载源码

根据自己的Tomcat版本,下载对应的源码

选择对应版本的tomcat,这里我使用的tomcat9,选择Archives

下载完成后解压,apache-tomcat-9.0.19-src

3. 创建项目结构

  • 在D盘创建“D: omcat9.0.19”文件夹,然后将解压后的文件拷贝到该目录中,同时创建“catalina-home”目录

  • 拷贝“apache-tomcat-9.0.19-srcconf”,到“catalina-home”目录下

  • 创建“D: omcat9.0.19pom.xml”文件

    <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/maven-v4_0_0.xsd">
     
        <modelVersion>4.0.0</modelVersion>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>Tomcat9.0.19</artifactId>
        <name>Tomcat 9.0 Study</name>
        <version>1.0</version>
        <packaging>pom</packaging>
     
        <modules>
            <module>apache-tomcat-9.0.19-src</module>
        </modules>
    </project>
    
  • 创建“D: omcat9.0.19apache-tomcat-9.0.19-srcpom.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>Tomcat9.0</artifactId>
        <name>Tomcat9</name>
        <version>9.0</version>
    
        <build>
            <finalName>Tomcat9</finalName>
            <sourceDirectory>java</sourceDirectory>
            <resources>
                <resource>
                    <directory>java</directory>
                </resource>
            </resources>
            <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>org.apache.ant</groupId>
                <artifactId>ant</artifactId>
                <version>1.9.5</version>
            </dependency>
            <dependency>
                <groupId>org.apache.ant</groupId>
                <artifactId>ant-apache-log4j</artifactId>
                <version>1.9.5</version>
            </dependency>
            <dependency>
                <groupId>org.apache.ant</groupId>
                <artifactId>ant-commons-logging</artifactId>
                <version>1.9.5</version>
            </dependency>
            <dependency>
                <groupId>javax.xml.rpc</groupId>
                <artifactId>javax.xml.rpc-api</artifactId>
                <version>1.1</version>
            </dependency>
            <dependency>
                <groupId>wsdl4j</groupId>
                <artifactId>wsdl4j</artifactId>
                <version>1.6.2</version>
            </dependency>
            <dependency>
                <groupId>org.eclipse.jdt.core.compiler</groupId>
                <artifactId>ecj</artifactId>
                <version>4.4</version>
            </dependency>
        </dependencies>
    </project>
    

最终的目录结构为:

Administrator@git MINGW64 /d/tomcat9.0.19
$ ls -l                                                                                                                 total 6
drwxr-xr-x 1 Administrator 197121   0 3月  30 17:50 apache-tomcat-9.0.19-src
drwxr-xr-x 1 Administrator 197121   0 3月  30 17:41 catalina-home
-rw-r--r-- 1 Administrator 197121 533 3月  30 17:27 pom.xml

Administrator@git MINGW64 /d/tomcat9.0.19
$ ls -l catalina-home/                                                                                                  total 8
drwxr-xr-x 1 Administrator 197121 0 3月  30 17:38 conf

4. 将该项目导入到IDEA中

5. 编译

由于这里使用是JDK1.8,所以需要注释掉这些代码,否则容易报错:

编译:

6. 运行

1)创建Application

vm option 配置:

-Dfile.encoding=UTF-8 -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

在Bootstrap的main方法当中加入测试代码:

 System.out.println("======================Tomcat Study======================");

下面是部分启动日志:

======================Tomcat Study======================
30-Mar-2020 17:38:32.009 警告 [main] org.apache.catalina.startup.ClassLoaderFactory.validateFile Problem with directory [D:	omcat9.0.19catalina-homelib], exists: [false], isDirectory: [false], canRead: [false]
30-Mar-2020 17:38:32.017 警告 [main] org.apache.catalina.startup.ClassLoaderFactory.validateFile Problem with directory [D:	omcat9.0.19catalina-homelib], exists: [false], isDirectory: [false], canRead: [false]
30-Mar-2020 17:38:32.017 警告 [main] org.apache.catalina.startup.ClassLoaderFactory.validateFile Problem with directory [D:	omcat9.0.19catalina-homelib], exists: [false], isDirectory: [false], canRead: [false]
30-Mar-2020 17:38:32.017 警告 [main] org.apache.catalina.startup.ClassLoaderFactory.validateFile Problem with directory [D:	omcat9.0.19catalina-homelib], exists: [false], isDirectory: [false], canRead: [false]

但是启动日志会有乱码,解决方法:

为何会有乱码:
原因:一般是中文编码不匹配导致;经跟踪,发现是ResourceBundle读取orgapachecatalinastartupLocalStrings_zh_CN.properties文件时没有用utf8解码导致;
解决:修改org.apache.tomcat.util.res.StringManager类中的getString函数;

if (bundle != null) {
str = bundle.getString(key);
}

改为

if (bundle != null) {
str = new String(bundle.getString(key).getBytes(“ISO-8859-1”), “UTF-8”);
}

再次运行,一切正常

引用链接:http://www.itgather.com/archives/54

2)实验

  1. 在“catalina-homewebapps”目录中创建“demoindex.html”文件:
<html>
<head><title>Tomcat9.0.19_Test</title></head>
<body>
    
  <h1>Hello World!!!</h1> 
  
</body>
</html>
 访问: http://127.0.0.1:8080/demo/ 
  1. 在“catalina-homewebapps”目录中创建“demoindex.jsp”文件:
<%@ page contentType="text/html;charset=utf-8" pageEncoding="utf-8"%>
<html>
<head><title>Test-title</title></head>
<body>
<%
    request.setCharacterEncoding("utf-8");
    String method = request.getMethod() ;   // 取得提交方式
    String ip = request.getRemoteAddr() ;   // 取得客户端的IP地址
    String path = request.getServletPath() ;    // 取得访问路径
    String contextPath1= request.getContextPath() ; // 取得上下文资源名称
    String contextPath2 = getServletContext().getContextPath() ;// 取得上下文资源名称
    String realPath=getServletContext().getRealPath("/");//取得虚拟目录所对应的真实路径
%>
<h3>请求方式:<%=method%></h3>
<h3>IP地址:<%=ip%></h3>
<h3>访问路径:<%=path%></h3>
<h3>上下文名称1:<%=contextPath1%></h3>
<h3>上下文名称2:<%=contextPath2%></h3>
<h3>真实路径:<%=realPath%></h3>
</body>
</html>

访问: http://127.0.0.1:8080/demo/index.jsp

调试程序

index.jsp在被编译后会生成“index_jsp.java”和“index_jsp.class”文件,位置如下:

Administrator@git MINGW64 /d/tomcat9.0.19/catalina-home/work/Catalina/localhost/demo/org/apache/jsp
$ pwd
/d/tomcat9.0.19/catalina-home/work/Catalina/localhost/demo/org/apache/jsp

Administrator@git MINGW64 /d/tomcat9.0.19/catalina-home/work/Catalina/localhost/demo/org/apache/jsp
$ ls index_jsp.java
index_jsp.java

Administrator@git MINGW64 /d/tomcat9.0.19/catalina-home/work/Catalina/localhost/demo/org/apache/jsp
$ ls index_jsp.class
index_jsp.class

下面是“index_jsp.java”文件的内容:

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/9.0.x-dev
 * Generated at: 2020-03-30 10:23:23 UTC
 * Note: The last modified time of this file was set to
 *       the last modified time of the source file after
 *       generation to assist with modification tracking.
 */
package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent,
                 org.apache.jasper.runtime.JspSourceImports {

  private static final javax.servlet.jsp.JspFactory _jspxFactory =
          javax.servlet.jsp.JspFactory.getDefaultFactory();

  private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;

  private static final java.util.Set<java.lang.String> _jspx_imports_packages;

  private static final java.util.Set<java.lang.String> _jspx_imports_classes;

  static {
    _jspx_imports_packages = new java.util.HashSet<>();
    _jspx_imports_packages.add("javax.servlet");
    _jspx_imports_packages.add("javax.servlet.http");
    _jspx_imports_packages.add("javax.servlet.jsp");
    _jspx_imports_classes = null;
  }

  private volatile javax.el.ExpressionFactory _el_expressionfactory;
  private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;

  public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
    return _jspx_dependants;
  }

  public java.util.Set<java.lang.String> getPackageImports() {
    return _jspx_imports_packages;
  }

  public java.util.Set<java.lang.String> getClassImports() {
    return _jspx_imports_classes;
  }

  public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
    if (_el_expressionfactory == null) {
      synchronized (this) {
        if (_el_expressionfactory == null) {
          _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
        }
      }
    }
    return _el_expressionfactory;
  }

  public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
    if (_jsp_instancemanager == null) {
      synchronized (this) {
        if (_jsp_instancemanager == null) {
          _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
        }
      }
    }
    return _jsp_instancemanager;
  }

  public void _jspInit() {
  }

  public void _jspDestroy() {
  }

  public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
      throws java.io.IOException, javax.servlet.ServletException {

    if (!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
      final java.lang.String _jspx_method = request.getMethod();
      if ("OPTIONS".equals(_jspx_method)) {
        response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
        return;
      }
      if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
        response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
        response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
        return;
      }
    }

    final javax.servlet.jsp.PageContext pageContext;
    javax.servlet.http.HttpSession session = null;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;


    try {
      response.setContentType("text/html;charset=utf-8");
      pageContext = _jspxFactory.getPageContext(this, request, response,
      			null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

      out.write("
");
      out.write("<html>
");
      out.write("<head><title>Test-title</title></head>
");
      out.write("<body>
");

    request.setCharacterEncoding("utf-8");
    String method = request.getMethod() ;   // 取得提交方式
    String ip = request.getRemoteAddr() ;   // 取得客户端的IP地址
    String path = request.getServletPath() ;    // 取得访问路径
    String contextPath1= request.getContextPath() ; // 取得上下文资源名称
    String contextPath2 = getServletContext().getContextPath() ;// 取得上下文资源名称
    String realPath=getServletContext().getRealPath("/");//取得虚拟目录所对应的真实路径

      out.write("
");
      out.write("<h3>请求方式:");
      out.print(method);
      out.write("</h3>
");
      out.write("<h3>IP地址:");
      out.print(ip);
      out.write("</h3>
");
      out.write("<h3>访问路径:");
      out.print(path);
      out.write("</h3>
");
      out.write("<h3>上下文名称1:");
      out.print(contextPath1);
      out.write("</h3>
");
      out.write("<h3>上下文名称2:");
      out.print(contextPath2);
      out.write("</h3>
");
      out.write("<h3>真实路径:");
      out.print(realPath);
      out.write("</h3>
");
      out.write("</body>
");
      out.write("</html>");
    } catch (java.lang.Throwable t) {
      if (!(t instanceof javax.servlet.jsp.SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try {
            if (response.isCommitted()) {
              out.flush();
            } else {
              out.clearBuffer();
            }
          } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
        else throw new ServletException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

这些是JSP的内置对象,另外还有request和response内置对象,在方法参数上

    final javax.servlet.jsp.PageContext pageContext;
    javax.servlet.http.HttpSession session = null;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;

下面是它们的赋值操作:

      pageContext = _jspxFactory.getPageContext(this, request, response,
      			null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

能够看到它们都是通过pageContext的对应方法来进行赋值的。

实际上我们也可以在界面上将这些内置对象打印出来:

test.jsp

<%@ page contentType="text/html" pageEncoding="GBK"%>
<%@ page import="java.util.*"%>	<!-- 导入java.util包 -->
<html>
<head><title>PageScope</title></head>
    <body>
            <h4>request:<%=request%></h4>
            <h4>session:<%=session%></h4>
            <h4>application:<%=application%></h4>
            <h4>response:<%=response%></h4>
            <h4>out:<%=out%></h4>
            <h4>config:<%=config%></h4>
            <h4>page:<%=page%></h4>

            <h4>pageContext:<%=pageContext%></h4>
            <h4>getException:<%=pageContext.getException()%></h4>
            <h4>getPage:<%=(pageContext.getPage()==page)%></h4>
            <h4>getRequest:<%=(pageContext.getRequest()==request)%></h4>
            <h4>getResponse:<%=(pageContext.getResponse()==response)%></h4>
            <h4>getSession:<%=(pageContext.getSession()==session)%></h4>
            <h4>getServletConfig:<%=(pageContext.getServletConfig()==config) %></h4>
            <h4>getServletContext:<%=(pageContext.getServletContext()==application)%></h4>
    </body>
</html>

运行结果:

再回到调试的问题上来,想要调试“index_jsp.java”,需要创建“org/apache/jsp”文件夹,然后将该文件移动到该文件夹下,打上断点就可以开始调试了:

3)注意

默认情况下解析JSP会报错

  1. 现象:
Type Exception Report

Message java.lang.NullPointerException

Description The server encountered an unexpected condition that prevented it from fulfilling the request.

Exception

org.apache.jasper.JasperException: java.lang.NullPointerException
	org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:638)
	org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:514)
	org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
	org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
Root Cause

java.lang.NullPointerException
	org.apache.jsp.index_jsp._jspService(index_jsp.java:165)
	org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:476)
	org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
	org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
Note The full stack trace of the root cause is available in the server logs.
  1. 问题分析和解决方法

    原因是我们直接启动org.apache.catalina.startup.Bootstrap的时候没有加载org.apache.jasper.servlet.JasperInitializer,从而无法编译JSP。解决办法是在tomcat的源码org.apache.catalina.startup.ContextConfig中手动将JSP解析器初始化:
    ————————————————
    版权声明:本文为CSDN博主「yehong1225」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/yekong1225/article/details/81000446

  2. 再次测试正常

4)切换到Tomcat主目录,将VM options修改为如下配置即可

-Dcatalina.home=apache-tomcat-9.0.19-src -Dcatalina.base=apache-tomcat-9.0.19-src
-Djava.endorsed.dirs=apache-tomcat-9.0.19-src/endorsed -Djava.io.tmpdir=apache-tomcat-9.0.19-src/temp
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=apache-tomcat-9.0.19-src/conf/logging.properties

参考链接:

原文地址:https://www.cnblogs.com/cosmos-wong/p/12608165.html