调试tomcat9.0.19源码

时间:2020-12-18 15:47:51

本文所用到的环境:

  • IntelliJ IDEA
  • Apache Maven 3.3.9
  • jdk1.8

1. 查看Tomcat版本

$TOMCAT_HOME\bin\version.bat

D:\Program_Files\apache-tomcat-9.0.19\bin>version.bat
Using CATALINA_BASE: "D:\Program_Files\apache-tomcat-9.0.19"
Using CATALINA_HOME: "D:\Program_Files\apache-tomcat-9.0.19"
Using CATALINA_TMPDIR: "D:\Program_Files\apache-tomcat-9.0.19\temp"
Using JRE_HOME: "D:\Program Files\Java\jdk1.8.0_77"
Using CLASSPATH: "D:\Program_Files\apache-tomcat-9.0.19\bin\bootstrap.jar;D:\Program_Files\apache-tomcat-9.0.19\bin\tomcat-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_Files\apache-tomcat-9.0.19\bin>

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

调试tomcat9.0.19源码

2. 下载源码

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

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

调试tomcat9.0.19源码
调试tomcat9.0.19源码

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

3. 创建项目结构

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

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

  • 创建“D:\tomcat9.0.19\pom.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:\tomcat9.0.19\apache-tomcat-9.0.19-src\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>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中

调试tomcat9.0.19源码

5. 编译

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

调试tomcat9.0.19源码

编译:

调试tomcat9.0.19源码

6. 运行

1)创建Application

调试tomcat9.0.19源码
调试tomcat9.0.19源码

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:\tomcat9.0.19\catalina-home\lib], exists: [false], isDirectory: [false], canRead: [false]
30-Mar-2020 17:38:32.017 警告 [main] org.apache.catalina.startup.ClassLoaderFactory.validateFile Problem with directory [D:\tomcat9.0.19\catalina-home\lib], exists: [false], isDirectory: [false], canRead: [false]
30-Mar-2020 17:38:32.017 警告 [main] org.apache.catalina.startup.ClassLoaderFactory.validateFile Problem with directory [D:\tomcat9.0.19\catalina-home\lib], exists: [false], isDirectory: [false], canRead: [false]
30-Mar-2020 17:38:32.017 警告 [main] org.apache.catalina.startup.ClassLoaderFactory.validateFile Problem with directory [D:\tomcat9.0.19\catalina-home\lib], exists: [false], isDirectory: [false], canRead: [false]

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

为何会有乱码:

原因:一般是中文编码不匹配导致;经跟踪,发现是ResourceBundle读取org\apache\catalina\startup\LocalStrings_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-home\webapps”目录中创建“demo\index.html”文件:
<html>
<head><title>Tomcat9.0.19_Test</title></head>
<body> <h1>Hello World!!!</h1> </body>
</html>
 访问: http://127.0.0.1:8080/demo/

调试tomcat9.0.19源码

  1. 在“catalina-home\webapps”目录中创建“demo\index.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

调试tomcat9.0.19源码

调试程序

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("\r\n");
out.write("<html>\r\n");
out.write("<head><title>Test-title</title></head>\r\n");
out.write("<body>\r\n"); 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("\r\n");
out.write("<h3>请求方式:");
out.print(method);
out.write("</h3>\r\n");
out.write("<h3>IP地址:");
out.print(ip);
out.write("</h3>\r\n");
out.write("<h3>访问路径:");
out.print(path);
out.write("</h3>\r\n");
out.write("<h3>上下文名称1:");
out.print(contextPath1);
out.write("</h3>\r\n");
out.write("<h3>上下文名称2:");
out.print(contextPath2);
out.write("</h3>\r\n");
out.write("<h3>真实路径:");
out.print(realPath);
out.write("</h3>\r\n");
out.write("</body>\r\n");
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>

运行结果:

调试tomcat9.0.19源码

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

调试tomcat9.0.19源码

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.

调试tomcat9.0.19源码

  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

调试tomcat9.0.19源码

参考链接: