当前位置 博文首页 > 少年时未觉悟 ,觉悟时不再年少,还危机四伏!:编译了三个版本

    少年时未觉悟 ,觉悟时不再年少,还危机四伏!:编译了三个版本

    作者:[db:作者] 时间:2021-07-16 15:34

    背景

    一产品是基于多模块开发的 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:
      在这里插入图片描述

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

    3. 进入源码目录,使用 ant 命令编译源码;
      在这里插入图片描述
      此操作耗时较长,耐心等待编译完成:
      在这里插入图片描述

    4. 创建一个 catalina-home 目录,将 output/build 目录下的所有文件拷贝到 catalina-home 目录下,将其作为 Tomcat 的工作目录:
      在这里插入图片描述

    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>
    
    
    1. 导入 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
    

    修改为一个特定值后,又报下面的错误:
    在这里插入图片描述

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

    奇怪的是,它能够编译通过、正常运行:
    在这里插入图片描述
    结论:Tomcat8.5.63 版本的 tar.gz 源码的 jdbc-pool 模块打包生成的 MANIFEST.MF 文件有问题,编译源码的时候应该避免使用该版本。本来以为哪个版本都一样,就随便选了最新版本,结果就踩坑了。

    最后,更换为 Tomcat8.5.59 版本,MAC 下就能正确启动了。
    在这里插入图片描述

    控制台乱码问题

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

    第一步,修改 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 应用,输出信息就正常了:
    在这里插入图片描述

    启示录

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

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