使用Apache Spark打包Jar运行时出现ClassNotFoundException问题
这个问题我之前帮同事排查过好几次,本质就是Spark的CSV数据源模块没被正确打包进你的fat jar里。在Eclipse里运行时,IDE会自动把所有依赖(包括Spark的CSV扩展模块)加载到类路径,但打包成单一jar时,如果依赖配置不对,这个非核心的数据源模块就会被漏掉——毕竟Spark核心包本身不包含CSV、JSON这类第三方数据源的实现,它们是通过Java的ServiceLoader机制动态加载的。
下面分几种场景给你具体的解决方案:
方案1:Maven项目(最常用)
如果你用Maven管理项目,需要两步调整:
1. 确保依赖配置正确
在pom.xml里添加Spark SQL的依赖,不要设置为provided(因为你是本地用java -jar直接运行,不是提交到Spark集群):
<dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.12</artifactId> <version>你的Spark版本,比如3.3.0</version> </dependency> <!-- 如果你用的是Spark 1.x版本,需要替换成databricks的spark-csv依赖: <dependency> <groupId>com.databricks</groupId> <artifactId>spark-csv_2.11</artifactId> <version>1.5.0</version> </dependency> -->
2. 修改Shade插件配置(关键!)
Maven的maven-shade-plugin默认会覆盖META-INF/services目录下的文件,而Spark正是通过这个目录下的配置文件来发现数据源的。所以必须添加合并规则:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <!-- 替换成你的主类全路径 --> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.yourcompany.YourSparkMainClass</mainClass> </transformer> <!-- 合并ServiceLoader配置文件,这一步不能少 --> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build>
修改完后执行mvn clean package,用生成的jar包重新运行java -jar命令即可。
方案2:Gradle项目
如果是Gradle项目,同样要注意依赖和打包配置:
1. 配置依赖
在build.gradle里添加Spark SQL依赖:
dependencies { implementation 'org.apache.spark:spark-sql_2.12:3.3.0' // Spark 1.x版本用这个: // implementation 'com.databricks:spark-csv_2.11:1.5.0' }
2. 配置Shadow插件
使用Gradle的Shadow插件来打包fat jar,并且开启服务文件合并:
plugins { id 'java' id 'com.github.johnrengelman.shadow' version '7.1.2' } shadowJar { // 替换成你的主类全路径 mainClassName = 'com.yourcompany.YourSparkMainClass' // 合并ServiceLoader配置文件 mergeServiceFiles() }
执行./gradlew shadowJar,然后运行java -jar build/libs/你的项目名称-all.jar就可以解决问题。
方案3:手动排查打包问题
如果以上配置都做了还是报错,可以手动检查你的fat jar:
- 解压jar包,确认里面存在
org/apache/spark/sql/execution/datasources/csv/CSVDataSource.class(对应Spark 2.x+版本) - 查看
META-INF/services/org.apache.spark.sql.sources.DataSourceRegister文件,确认里面包含org.apache.spark.sql.execution.datasources.csv.CSVDataSource这一行
另外提醒一句:如果你是把jar包提交到Spark集群运行,那依赖可以设为provided,但本地直接用java -jar运行时,必须把Spark的核心依赖和数据源模块都打包进去。
内容的提问来源于stack exchange,提问作者user9642332

