调试时Java 9未命名模块读取重复包问题及依赖树分析
从你提供的项目依赖树可以看到存在重复的包(比如commons-collections同时有3.2.1和3.2.2版本):
[INFO] +- commons-logging:commons-logging:jar:1.2:compile [INFO] +- org.apache.directory.studio:org.apache.commons.collections:jar:3.2.1:compile [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile [INFO] +- xerces:xercesImpl:jar:2.11.0:compile [INFO] | \- xml-apis:xml-apis:jar:1.4.01:compile [INFO] +- org.apache.cxf:cxf-rt-bindings-soap:jar:3.2.2:compile [INFO] | +- org.apache.cxf:cxf-core:jar:3.2.2:compile [INFO] | | +- com.fasterxml.woodstox:woodstox-core:jar:5.0.3:compile [INFO] | | | \- org.codehaus.woodstox:stax2-api:jar:3.1.4:compile [INFO] | | \- org.apache.ws.xmlschema:xmlschema-core:jar:2.2.3:compile [INFO] | +- org.apache.cxf:cxf-rt-wsdl:jar:3.2.2:compile [INFO] | | +- wsdl4j:wsdl4j:jar:1.6.3:compile [INFO] | | \- org.ow2.asm:asm:jar:5.2:compile [INFO] | \- org.apache.cxf:cxf-rt-databinding-jaxb:jar:3.2.2:compile [INFO] +- org.apache.wss4j:wss4j-ws-security-common:jar:2.2.1:compile [INFO] | +- org.slf4j:slf4j-api:jar:1.7.22:compile [INFO] | +- org.apache.santuario:xmlsec:jar:2.1.1:compile [INFO] | | \- commons-codec:commons-codec:jar:1.10:compile [INFO] | +- org.opensaml:opensaml-saml-impl:jar:3.3.0:compile [INFO] | | +- org.opensaml:opensaml-profile-api:jar:3.3.0:compile [INFO] | | | \- org.opensaml:opensaml-core:jar:3.3.0:compile [INFO] | | | \- io.dropwizard.metrics:metrics-core:jar:3.1.2:compile [INFO] | | +- org.opensaml:opensaml-sam...
这正是Java 9+模块系统下触发未命名模块读取重复包错误的核心原因——未命名模块中不允许多个JAR包含相同的包名。下面给你几个实用的解决方案:
1. 直接排除重复依赖
这是最快捷的处理方式,通过Maven的<exclusions>标签移除冗余的依赖版本。针对你依赖树里的commons-collections冲突,可以这么配置:
<dependency> <groupId>org.apache.directory.studio</groupId> <artifactId>org.apache.commons.collections</artifactId> <version>3.2.1</version> <exclusions> <exclusion> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> </exclusion> </exclusions> </dependency>
配置后项目只会保留3.2.2版本的commons-collections,消除包重复问题。
你也可以用Maven命令精准定位所有冲突依赖:
mvn dependency:tree -Dverbose -Dincludes=commons-collections
把commons-collections替换成其他怀疑有冲突的包名,就能快速找到所有重复依赖的来源。
2. 统一管控依赖版本
在Maven的<dependencyManagement>节点中统一指定冲突依赖的版本,强制项目全局使用同一个版本,避免不同依赖引入不同版本的JAR:
<dependencyManagement> <dependencies> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.2</version> </dependency> <!-- 其他有版本冲突的依赖也可以在这里统一配置 --> </dependencies> </dependencyManagement>
这种方式适合多个第三方库都引入同一个基础库不同版本的场景,能从根源上避免版本冲突。
3. 使用Shade插件重命名重复包
如果排除依赖会导致部分功能失效(比如旧代码依赖低版本包的特定逻辑),可以用Maven Shade插件将重复包重命名到自定义的包空间,让不同版本的包共存:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <relocations> <relocation> <pattern>org.apache.commons.collections</pattern> <shadedPattern>com.your.project.shaded.commons.collections</shadedPattern> </relocation> </relocations> </configuration> </execution> </executions> </plugin> </plugins> </build>
插件会将指定版本的重复包重新命名,和其他版本的包名彻底错开,解决冲突问题。
4. 模块路径补丁(模块化项目备选)
如果你的项目已经启用Java模块系统(存在module-info.java),可以用--patch-module参数将重复包合并到同一个模块中,不过这种方式相对复杂,适合熟悉模块系统的场景:
java --patch-module com.your.module=path/to/commons-collections-3.2.2.jar -jar your-app.jar
一般优先用前面几种依赖层面的解决方案,这个作为备选。
内容的提问来源于stack exchange,提问作者jahuer1

