Tomcat源码学习

一. 源码下载

PS: 多图预警

在开始阅读源码之前,我们需要先构建一个环境,这样才能便于我们对源码进行调试,具体源码我们可以到官网进行下载(这里我以8.5.63版本为例)。

二. 项目导入

下载并解压 apache-tomcat-8.5.63-src.zip。

然后进入 apache-tomcat-8.5.63-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>apache-tomcat-8.5.63-src</artifactId>
    <version>8.5</version>
    <name>Tomcat8.5</name>

<!-- Tomcat基础依赖包 -->
<dependencies>
	<!-- 远程过程调用工具包 -->
    <dependency>
        <groupId>javax.xml</groupId>
        <artifactId>jaxrpc</artifactId>
        <version>1.1</version>
    </dependency>
	<!-- soap协议处理工具包 -->
    <dependency>
        <groupId>javax.xml.soap</groupId>
        <artifactId>javax.xml.soap-api</artifactId>
        <version>1.4.0</version>
    </dependency>
	<!-- 解析webservice的wsdl文件工具 -->
    <dependency>
        <groupId>wsdl4j</groupId>
        <artifactId>wsdl4j</artifactId>
        <version>1.6.2</version>
    </dependency>
	<!-- Eclipse Java编译器 -->
    <dependency>
        <groupId>org.eclipse.jdt.core.compiler</groupId>
        <artifactId>ecj</artifactId>
        <version>4.5.1</version>
    </dependency>
	<!-- ant管理工具 -->
    <dependency>
        <groupId>ant</groupId>
        <artifactId>ant</artifactId>
        <version>1.7.0</version>
    </dependency>
	<!-- easymock辅助单元测试 -->
    <dependency>
        <groupId>org.easymock</groupId>
        <artifactId>easymock</artifactId>
        <version>3.4</version>
    </dependency>
</dependencies>

    <build>
        <!-- 指定源目录 -->
		<finalName>Tomcat8.5</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>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

通过IDEA引入项目

打开项目之后,全局搜索一下 main 方法,找到 tomcat 的入口

一共有6个 main 方法,其中有一个 Bootstrap 类,翻译过来就是引导程序的意思,我们直接跳转到这个类过去,跑一下看看有没有什么问题。

项目启动成功了,控制台输出的信息有乱码,暂时不管,我们看看能不能访问到首页。


显示500状态码,给出的信息也很多是乱码,那我们来看一下是什么原因导致的吧。

三. 乱码的原因

找到最开始出现乱码的地方,选中这个类名,双击 Shift 键查找并跳转到对应方法处。

打个断点,然后开始 DEBUG,先一层层进入,跟踪下看看首次出现乱码的地方是在哪

在这里我们可以看到,通过 handleGetObject() 方法得到的对象是有乱码的情况,在这里打上一个断点,然后继续深入

先一层层进入,跟踪下看看首次出现乱码的地方是在哪

// Debug跳转线路
VersionLoggerListener.java
private void log(){ ... }

	↓

StringManager.java
public String getString(String key){ ... }

	↓

ResourceBundle.java
public final Object getObject(String key){ 
	Object obj = handleGetObject(key);
	...
}

	↓

protected abstract Object handleGetObject(String key);

	↓

PropertyResourceBundle.java
public Object handleGetObject(String key){
	return lookup.get(key);
}

protected abstract Object handleGetObject(String key); 这是一个抽象方法,点击 Setp into 按钮跳转进到对应的具体实现方法中

在这个方法中我们可以看到他实际上是在一个 HashMap 中根据传入的 key 来获取对应的 value,选择该代码块,按 AIT + F8 进行查看
在这里插入图片描述

发现起因了,在这个 HashMap 中存储的 value 都是乱码的,才会导致后面调用到的地方显示的都是乱码

起因发现了,那么我们就得来找一下这个 HashMap 对象的 put 操作是在哪一处进行的,才好真正的解决问题。

选择 lookupCtrl + F 查找一下,找到一下两个方法,都给他打上断点,然后重启项目,看看具体是由哪个方法来实现的。

在这里我们可以看到他是通过输入流的形式来实现的,在下方的 Variables 窗口中可以看到 Properties 加载出来的数据已经是乱码的了

Frames 窗口点击上一栈帧,找到当前方法的调用入口,回溯上去找到 stream 的来源


按照上面给出的路径找到对应的文件,可以看到里面是 UTF-8 编码的中文字符。

文件编码没有问题,那么问题就是出在 is = classLoader.getResourceAsStream(resourceName); 这个方法上了,这时候我们想对他进行一个修改,发现该类是锁定状态,无法修改,只能看不能碰这有点难度呀~

四. 解决乱码

我们还得重新来一遍,看看这个配置信息再哪个可编辑的类中有使用,然后再进行修改

// Debug跳转线路
VersionLoggerListener.java
private void log(){ ... }

	↓

StringManager.java
public String getString(String key){ ... }

我们可以看到再 StringManager 这个类中有一个获取的方法,再往下走就是加锁的类了,那么我们尝试着在这一层对该结果进行编码转换,看看效果如何

有效果,控制台这边显示的信息已经没有乱码了,再看一下页面这边如何

页面这边还是有一部分显示是乱码的,看来还有地方需要修改,继续找下看

通过查看控制台的错误信息,全局搜索一下该提示出现位置进行定位

跳转到对应位置,Ctrl + 左键 定位到调用的地方,在这里可以看到 error() 方法中有具体的一些信息参数,我们用老方法对这 e.getMessage 这个参数修改一下看

控制台的乱码解决了,可是页面这边的还是乱码,看来是找错地方了,再来!

根据页面的提示找到 JspCompilationContext 类的 compile 方法,逐行查看,找到出错的地方

看到这里有个 Localizer.getMessage() 方法用来获取信息的,跳进来看一下,果然是这里,找到地方了,直接办他!

重启项目,再次刷新查看效果,终于搞定了,不容易呀~

五. 解决 ‘无法为JSP编译类’ 异常

导致页面显示500状态码的原因是 Tomcat 源码中 jsp 引擎 Jasper 没有被初始化,从而无法编译处理 jsp (以为 jsp 是需要被转换成 servlet 进一步编译处理的),我们只需要在 Tomcat 的源码 ContextConfig 类的 configureStart 方法中把该引擎进行初始化即可,代码如下:

context.addServletContainerInitializer(new JasperInitializer(), null);

现在就大公告成拉!!!

我收集有众多的 计算机电子书籍,有需要的小伙伴自提哦~

原文地址:https://www.cnblogs.com/unrecognized/p/14521440.html