什么?编译了三个版本Tomcat源码后,我才发现这个bug

背景

一产品是基于多模块开发的 SpringBoot 项目,发布时导出多个 war 包部署在同一个 Tomcat 。模块有五六个,发布时最大的问题就是每个包都很大,主要是各个模块的 WEB-INF/lib 下包含大量相同 jar ,因此有必要将公共包摘出来放到 Tomcat 的共享目录下。

然而,在捣鼓了两天后,我对 Tomcat 的多应用部署时共享公共包的能力产生了怀疑。理论上,同一个 Tomcat 下部署多个应用时,可以将所有共享 jar 放在 shared/lib 目录下,然后配置 shared.loader 就可以了。

实践的时候,抽取了公共包后,多个 war 部署时始终报错, WEB-INF/lib 下明明有对应的 Spring 框架包,还是报 Caused by: java.lang.NoClassDefFoundError: org/springframework/beans/factory/FactoryBean 异常,反复测试,还是无法确定哪些包应该作为公共包。

所以,决定从源头来搞明白这个类加载过程,在编译的 Tomcat 版本下断点跟踪一下到底是怎么回事儿。第一件事儿,先编译 Tomcat 源码。

Tomcat 源码编译

Tomcat 源码导入 IDEA 的过程比较简单,步骤为:

1.下载 ANT 工具,最新版本为 1.10.9,配置环境变量 ANT_HOME:

什么?编译了三个版本Tomcat源码后,我才发现这个bug

 

2.下载 tomcat 源码,根据操作系统选择 zip 或者 tar.gz 文件;

3.进入源码目录,使用 ant 命令编译源码;

什么?编译了三个版本Tomcat源码后,我才发现这个bug

 

此操作耗时较长,耐心等待编译完成:

什么?编译了三个版本Tomcat源码后,我才发现这个bug

 

4. 创建一个 catalina-home 目录,将 output/build 目录下的所有文件拷贝到 catalina-home 目录下,将其作为 Tomcat 的工作目录:

什么?编译了三个版本Tomcat源码后,我才发现这个bug

 

5.进入源码目录,创建 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>Tomcat8.0</artifactId>
    <name>Tomcat8.0</name>
    <version>8.0</version>
 
    <build>
        <finalName>Tomcat8.0</finalName>
        <sourceDirectory>java</sourceDirectory>
        <!--<testSourceDirectory>test</testSourceDirectory>-->
        <resources>
            <resource>
                <directory>java</directory>
            </resource>
        </resources>
        <!--<testResources>-->
            <!--<testResource>-->
                <!--<directory>test</directory>-->
            <!--</testResource>-->
        <!--</testResources>-->
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
 
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.easymock</groupId>
            <artifactId>easymock</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.7.0</version>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>javax.xml</groupId>
            <artifactId>jaxrpc</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>4.5.1</version>
        </dependency>
    </dependencies>
</project>

6.导入 IDEA 源码,配置启动参数 -Dcatalina.home="F:\Study\TomcateStudy\2021Tomcat\catalina-home" 。

总的来说,编译源码流程比较简单,但最开始因为随便选择了最新版本,导致无法启动,这可能是官网版本发布时的缺陷吧,反正换个版本就没有问题。

选错版本的问题

首先,在 MAC 操作系统下,随便选择了最新版本 tomcat8.5.63 的 tar.gz 版本,下载后,编译时报错。先报 @Version@ 校验错误:

Error:osgi: [Tomcat8.5] Invalid value for Bundle-Version, @VERSION@ does not

修改为一个特定值后,又报下面的错误:

什么?编译了三个版本Tomcat源码后,我才发现这个bug

 

最初怀疑是 IDEA 配置问题,于是又在 windows 操作系统下,还是用最新版本的 tomcat8.5.63 的 zip 版本下载源码,配置参数如下:

什么?编译了三个版本Tomcat源码后,我才发现这个bug

 

奇怪的是,它能够编译通过、正常运行:

什么?编译了三个版本Tomcat源码后,我才发现这个bug

 

结论:Tomcat8.5.63 版本的 tar.gz 源码的 jdbc-pool 模块打包生成的 MANIFEST.MF 文件有问题,编译源码的时候应该避免使用该版本。本来以为哪个版本都一样,就随便选了最新版本,结果就踩坑了。

最后,更换为 Tomcat8.5.59 版本,MAC 下就能正确启动了。

什么?编译了三个版本Tomcat源码后,我才发现这个bug

 

控制台乱码问题

最后解决控制台乱码问题,主要涉及到两个类,它们使用了默认编码导致输出乱码的。具体操作如下,详细参考:

第一步,修改 org.apache.tomcat.util.res.StringManager 类的 getString(final String key, final Object... args) 方法,获得 value 后再转码:

try{
     value = new String(value.getBytes("ISO-8859-1"),"UTF-8");
 }catch (Exception e ){
     e.printStackTrace();
 }

第二步,修改 org.apache.jasper.compiler.Localizer 类的 getMessage(String errCode) 方法,在返回之前进行转码:

 try{
     errMsg = new String(errMsg.getBytes("ISO-8859-1"),"UTF-8");
 }catch (Exception e ){
     e.printStackTrace();
 }

再重启 Tomcat 应用,输出信息就正常了:

什么?编译了三个版本Tomcat源码后,我才发现这个bug

 

启示录

到现在为止,还没找到答案的几个问题是:

  1. 多应用共享 lib 的原则是什么?
  2. 哪些包应该放在 shared/lib 下?【我是将纯第三方 jar ,无相关依赖的】
  3. 为什么只 WEB-INF/lib 下只保留某个应用自身的 jar 而且包含 SpringBoot 的 starter 依赖,还是报类异常?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
OSZAR »