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

多JDBC驱动场景下正确的DuplicatesStrategy应如何选择?

冲突文件覆盖的影响

你遇到的重复文件分为两类,覆盖后的影响各不相同:

  • META-INF/services/java.sql.Driver是Java SPI(服务提供者接口)机制的注册文件,每个JDBC驱动都会在这个文件里声明自己的驱动实现类全限定名。文件被覆盖后,只有最后打包进jar的驱动会被SPI自动发现,你当前场景下Oracle的驱动文件覆盖了SQL Server的,如果你代码里没有手动通过Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver")加载驱动,使用DriverManager获取SQL Server连接时会抛出「找不到驱动」的异常。
  • META-INF/native-image/下的文件是GraalVM原生镜像的编译配置文件,如果你用GraalVM打原生镜像,这类文件覆盖会导致部分依赖的编译配置缺失,轻则原生镜像运行时报错,重则直接编译失败;如果你只在标准JVM环境运行程序,这类文件覆盖暂时不会产生影响。

普通的业务类、第三方依赖的类文件只有全限定名完全相同时才会被覆盖,这种属于真正的依赖冲突,你当前遇到的都是元数据配置文件冲突,不用过度担忧。

这类冲突的普遍性

这类冲突非常常见,属于fat jar(将所有依赖打进同一个jar包)打包场景的典型问题。只要多个依赖用到了SPI机制、或者自带同名的元数据配置文件,打包时就会出现路径重复的问题,几乎所有多依赖的fat jar打包都会遇到这类问题。

解决方案

可以通过合并配置的方式保留所有文件的有效内容,常用方案有三种:

1. 优先推荐:不打fat jar

不要把所有依赖都打进同一个jar,把第三方依赖统一放在单独的lib目录下,启动时用以下命令启动即可,完全不会出现文件冲突问题:

java -cp 你的主jar包名.jar:lib/* com.example.App

2. 自定义打包逻辑合并冲突文件

如果必须打fat jar,可以修改jar任务的配置,针对不同类型的冲突文件做合并处理,修改后的配置如下:

plugins {
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.oracle.database.jdbc:ojdbc11-production:21.3.0.0'
    implementation 'com.microsoft.sqlserver:mssql-jdbc:9.4.0.jre8'
}

tasks.named('jar') {
    // 先设置重复策略为排除,避免默认覆盖,后续针对需要合并的文件单独处理
    setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE)
    manifest {
        attributes 'Main-Class': 'com.example.App'
    }

    // 合并SPI服务注册文件
    def mergedServicesDir = file("${buildDir}/tmp/jar-merged/services")
    doFirst {
        mergedServicesDir.deleteDir()
        mergedServicesDir.mkdirs()
        // 遍历所有依赖的服务文件,合并内容
        configurations.compileClasspath.each { depJar ->
            if (depJar.file && depJar.name.endsWith(".jar")) {
                zipTree(depJar).matching { include "META-INF/services/**" }.each { serviceFile ->
                    def targetFile = new File(mergedServicesDir, serviceFile.name)
                    targetFile.append(serviceFile.text + System.lineSeparator())
                }
            }
        }
    }

    from {
        configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
    // 引入合并后的SPI服务文件,覆盖原有分散的文件
    from(mergedServicesDir) {
        into "META-INF/services"
    }

    // 不需要GraalVM原生镜像打包的话可以直接排除该目录下的所有文件,避免冲突
    exclude "META-INF/native-image/**"

    // 如果需要保留GraalVM配置,可以参考SPI文件的合并逻辑,按配置文件的格式写对应的合并规则即可
}

3. 用Shadow插件简化处理

可以直接用Gradle的Shadow插件,内置了常见冲突文件的合并策略,不需要自己写合并逻辑,只需要在配置里开启服务文件、原生镜像配置文件的合并开关即可自动处理所有冲突。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 08:36:00