前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >什么?编译了三个版本Tomcat源码后,我才发现这个bug

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

作者头像
烂猪皮
发布2021-04-02 08:25:46
4170
发布2021-04-02 08:25:46
举报
文章被收录于专栏:JAVA烂猪皮JAVA烂猪皮

背景

一产品是基于多模块开发的 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 添加依赖:

代码语言:javascript
复制
<?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@ 校验错误:

代码语言:javascript
复制
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 后再转码:

代码语言:javascript
复制
try{
     value = new String(value.getBytes("ISO-8859-1"),"UTF-8");
 }catch (Exception e ){
     e.printStackTrace();
 }

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

代码语言:javascript
复制
 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 依赖,还是报类异常?

往期精彩推荐

面试:史上最全多线程面试题 !

JVM难学?那是因为你没认真看完这篇文章

—END—

你点的每个好看,我都认真当成了

看完本文记得给作者点赞+在看哦~~~大家的支持,是作者源源不断出文的动力

作者:毕小宝 链接:https://juejin.cn/post/6925741351971586055 来源:掘金

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JAVA烂猪皮 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Tomcat 源码编译
  • 选错版本的问题
  • 控制台乱码问题
  • 启示录
    • 面试:史上最全多线程面试题 !
      • JVM难学?那是因为你没认真看完这篇文章
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档