• 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);
    

    现在就大公告成拉!!!

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

  • 相关阅读:
    迭代加深搜索 codevs 2541 幂运算
    二叉树结构 codevs 1029 遍历问题
    深搜+DP剪枝 codevs 1047 邮票面值设计
    2016.6.10 深度优先搜索练习
    二分+动态规划 POJ 1973 Software Company
    tarjan算法求桥双连通分量 POJ 3177 Redundant Paths
    tarjan算法+缩点:求强连通分量 POJ 2186
    tarjan算法求割点cojs 8
    关键路径 SDUTOJ 2498
    第二章 STM32的结构和组成
  • 原文地址:https://www.cnblogs.com/unrecognized/p/14521440.html
Copyright © 2020-2023  润新知