一. 源码下载
PS: 多图预警
在开始阅读源码之前,我们需要先构建一个环境,这样才能便于我们对源码进行调试,具体源码我们可以到官网进行下载(这里我以8.5.63版本为例)。
二. 项目导入
下载并解压 apache-tomcat-8.5.63-src.zip。
然后进入 apache-tomcat-8.5.63-src 目录,新增一个 pom.xml 文件
4.0.0
org.apache.tomcat
apache-tomcat-8.5.63-src
8.5
Tomcat8.5
javax.xml
jaxrpc
1.1
javax.xml.soap
javax.xml.soap-api
1.4.0
wsdl4j
wsdl4j
1.6.2
org.eclipse.jdt.core.compiler
ecj
4.5.1
ant
ant
1.7.0
org.easymock
easymock
3.4
Tomcat8.5
java
java
org.apache.maven.plugins
maven-compiler-plugin
3.1
1.8
UTF-8
通过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);
现在就大公告成拉!!!
我收集有众多的 计算机电子书籍,有需要的小伙伴自提哦~