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
操作是在哪一处进行的,才好真正的解决问题。
选择 lookup
按 Ctrl + 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);
现在就大公告成拉!!!
- END -我收集有众多的 计算机电子书籍,有需要的小伙伴自提哦~