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

Gradle构建GeoTools FatJar遇服务类实例化失败问题

GeoTools FatJar构建后ServiceConfigurationError问题排查与解决

问题描述

项目通过FatJar运行,引入GeoTools依赖后,已在shadowJar任务中使用mergeServiceFiles()合并META-INF/services文件,但仍触发以下错误:

WARNING: Can't load a service for category "Operation". Cause is "ServiceConfigurationError: org.geotools.api.coverage.processing.Operation: Provider org.geotools.coverage.processing.operation.ShadedRelief could not be instantiated".

经确认:

  • org.geotools.api.coverage.processing.Operation(来自gt-api)和org.geotools.coverage.processing.operation.ShadedRelief(来自gt-coverage)均存在于FatJar内
  • META-INF/services下的对应配置文件已包含该服务提供者条目
  • 通过-classpath直接指定依赖Jar运行时项目正常,仅FatJar模式报错

Gradle配置完全遵循GeoTools官方FatJar指南,使用shadowJar插件,配置如下:

plugins {
    id("com.gradleup.shadow") version "8.3.0"
    java
}

group = "test.it"
version = "0.0.1"

application {
    mainClass.set("test.it.ApplicationKt")
}

repositories {
    maven { url = uri("https://repo.osgeo.org/repository/release/") }
    mavenCentral()
}

dependencies {
    implementation("org.geotools:gt-coverage:31.2")
    implementation("org.geotools:gt-main:31.2")
    implementation("org.geotools:gt-api:31.2")
    implementation("org.geotools:gt-geotiff:31.2")
    implementation("org.geotools:gt-tile-client:31.2")
    implementation("org.geotools.xsd:gt-xsd-kml:31.2")
    implementation("org.geotools:gt-epsg-hsql:31.2")
    implementation("org.osgeo:proj4j:0.1.0")
}

tasks {
    shadowJar {
        mergeServiceFiles()
        isZip64 = true
        archiveFileName.set("${project.name}-${project.version}.jar")
        manifest {
            attributes["Main-Class"] = "test.it.ApplicationKt"
            // 修复imageio vendor!=null错误
            attributes["Specification-Title"] = "Java Advanced Imaging Image I/O Tools"
            attributes["Specification-Version"] = version
            attributes["Specification-Vendor"] = "Sun Microsystems, Inc."
            attributes["Implementation-Title"] = "com.sun.media.imageio"
            attributes["Implementation-Version"] = version
            attributes["Implementation-Vendor"] = "Sun Microsystems, Inc."
        }
    }
}

已尝试但无效的方案:

  • 切换GeoTools版本20.0~31.2
  • 切换Java版本11/17/21
  • 更新Gradle至8.10

临时解决方案:在shadowJar任务中添加exclude("META-INF/services/org.geotools.api.coverage.processing.*")可规避该错误,但需更根本的解决办法。

根本原因分析

问题核心是FatJar构建过程中出现服务配置冲突或类加载异常,具体可能包括:

  1. mergeServiceFiles()合并时,不同依赖的服务配置文件存在重复条目,导致ServiceLoader加载实例时异常
  2. Shaded过程中GeoTools内部依赖类被重命名或类加载顺序异常,导致ShadedRelief实例化时无法找到依赖类
  3. 合并后的META-INF/services文件存在格式错误(如多余空格、换行),导致ServiceLoader无法正确解析

根本解决办法

方法1:指定服务文件去重合并策略

修改shadowJar任务,针对GeoTools服务文件添加严格去重规则,确保每个服务提供者仅保留一个有效条目:

tasks {
    shadowJar {
        mergeServiceFiles()
        // 针对META-INF/services文件配置去重策略
        transform(com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer) {
            setPath("META-INF/services")
            // 排除重复条目,保留最后出现的实例(可根据需求调整为保留第一个)
            duplicatesStrategy = DuplicatesStrategy.EXCLUDE
        }
        isZip64 = true
        archiveFileName.set("${project.name}-${project.version}.jar")
        manifest {
            attributes["Main-Class"] = "test.it.ApplicationKt"
            attributes["Specification-Title"] = "Java Advanced Imaging Image I/O Tools"
            attributes["Specification-Version"] = version
            attributes["Specification-Vendor"] = "Sun Microsystems, Inc."
            attributes["Implementation-Title"] = "com.sun.media.imageio"
            attributes["Implementation-Version"] = version
            attributes["Implementation-Vendor"] = "Sun Microsystems, Inc."
        }
    }
}

方法2:显式指定GeoTools类加载优先级

在manifest中添加类路径配置,确保GeoTools核心依赖优先加载:

tasks {
    shadowJar {
        mergeServiceFiles()
        isZip64 = true
        archiveFileName.set("${project.name}-${project.version}.jar")
        manifest {
            attributes["Main-Class"] = "test.it.ApplicationKt"
            attributes["Specification-Title"] = "Java Advanced Imaging Image I/O Tools"
            attributes["Specification-Version"] = version
            attributes["Specification-Vendor"] = "Sun Microsystems, Inc."
            attributes["Implementation-Title"] = "com.sun.media.imageio"
            attributes["Implementation-Version"] = version
            attributes["Implementation-Vendor"] = "Sun Microsystems, Inc."
            // 配置类路径优先级,确保GeoTools核心包优先加载
            attributes["Class-Path"] = configurations.runtimeClasspath.files.join(" ")
        }
    }
}

方法3:排查并排除冲突依赖

检查项目依赖树,找出与GeoTools冲突的重复依赖(如重复的gt-api)并排除:

dependencies {
    implementation("org.geotools:gt-coverage:31.2") {
        exclude group: 'org.geotools', module: 'gt-api' // 若存在重复依赖则排除
    }
    // 其他依赖配置...
}

方法4:Relocate重定位GeoTools包

若为类名冲突导致加载异常,可在shadowJar中重定位GeoTools包:

tasks {
    shadowJar {
        mergeServiceFiles()
        // 重定位GeoTools包,避免与其他依赖类名冲突
        relocate 'org.geotools', 'your.project.shaded.org.geotools'
        isZip64 = true
        archiveFileName.set("${project.name}-${project.version}.jar")
        manifest {
            // 保持原manifest配置
        }
    }
}

验证方案

修改配置后重新构建FatJar,运行时添加-Djava.util.logging.config.file=logging.properties启用调试日志,查看ServiceLoader加载日志,确认服务类是否正常实例化。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 17:20:59