You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Maven多模块项目JDK11/17下ClassNotFoundException问题求助

Maven多模块依赖打包与JDK兼容问题分析

项目背景

我有一个Maven多模块项目,模块A依赖模块B:

  • 模块A的pom依赖配置:
<dependency>
     <groupId>com.xxx.xxxx</groupId>
     <artifactId>module-B</artifactId>
     <version>${project.version}</version>
</dependency>
  • 模块B的pom依赖配置:
<dependency>
    <groupId>com.sybase.jconnect</groupId>
    <artifactId>jconn4</artifactId>
    <version>7.07-27307</version>
</dependency>

使用maven-assembly-plugin对模块A和B进行JAR打包,包含这两个JAR的lib文件夹已通过-classpath参数加入类路径。模块A作为Web服务启动,调用模块B的功能,而模块B会动态加载com.sybase.jconnect依赖中的类。

遇到的疑问及问题:

  1. 为何模块B的依赖com.sybase.jconnect会被打包进模块A的JAR中?
  2. 为何该依赖没有被打包进模块B的JAR中?
  3. 为何在JDK8环境运行正常,但在JDK11或JDK17下会出现模块B动态加载类时的ClassNotFoundException?

问题解答

1. 模块B的依赖被打包进模块A的原因

这是Maven依赖传递特性的正常表现:

  • 模块B对jconn4的依赖默认是compile范围,该范围的依赖会自动传递给所有依赖模块B的上游项目(包括模块A),进入模块A的依赖树。
  • 你使用的maven-assembly-plugin如果配置了打包模块A的依赖集合(比如在assembly描述符中指定包含runtime或compile范围的依赖),就会把模块A依赖树中所有符合条件的依赖(包括传递而来的jconn4)一并打包到模块A的输出产物中。

2. 该依赖未被打包进模块B的原因

这取决于模块B的maven-assembly-plugin打包配置:

  • 默认情况下,maven-assembly-plugin仅打包当前模块自身编译生成的class文件和资源文件,不会自动包含模块的依赖项。
  • 若没有为模块B专门配置assembly描述符(比如添加dependencySet节点来引入自身依赖),模块B的JAR只会包含自身代码,不会带上jconn4这类依赖。
  • 通常模块B作为被依赖的基础模块,不需要打包成包含所有依赖的胖JAR,因此默认配置下不会包含依赖。

3. JDK版本差异导致异常的原因

核心是JDK9及以上引入的**JPMS(Java平台模块化系统)**改变了类加载逻辑,和JDK8的类加载机制存在本质差异:

  • 类加载器可见性变化:JDK8采用传统双亲委派模型,类路径上的所有JAR由应用类加载器统一加载,模块A和模块B共享同一类加载器,jconn4的类可全局访问;而JDK11+中,即使在类路径下运行,非模块化JAR(如jconn4)的类加载上下文更严格,若jconn4被模块A的类加载器加载,但模块B的类加载器(比如Web容器的层级类加载器)无法访问该类,动态加载时就会抛出异常。
  • 未命名模块的访问限制:jconn4属于无module-info.class的未命名模块,JDK11+对未命名模块的类访问权限有约束,若模块B的动态加载逻辑没有正确处理未命名模块的类访问,就会触发ClassNotFoundException。
  • 动态加载API行为调整:JDK9+对Class.forName()等动态加载API的默认类加载器选择逻辑做了调整,若模块B动态加载时未指定加载jconn4的类加载器,就可能找不到目标类。

解决建议

针对JDK11+的异常问题,可尝试以下方案:

  • 将jconn4单独放置在模块A和模块B都能访问到的共享类路径下,而非仅被模块A的JAR包含。
  • 启动Web服务时添加JVM参数:若涉及Java EE API依赖,添加--add-modules java.se.ee;若涉及反射访问基础模块类,添加--add-opens java.base/java.lang=ALL-UNNAMED。
  • 调整maven-assembly-plugin配置,将jconn4打包到共享lib目录,确保所有相关模块的类加载器都能访问到该JAR。
  • 若使用Web容器(如Tomcat),将jconn4放置在容器的共享类路径(如CATALINA_HOME/lib),而非单个Web应用的WEB-INF/lib。

内容的提问来源于stack exchange,提问作者newStack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 08:50:19