Spark Java代码Eclipse运行正常,Maven打包后执行Jar报ClassNotFoundException
解决Spark连接NATS时ClassNotFoundException问题
问题根源
- Spring Boot可执行Jar的类加载隔离:当使用
java -jar运行Spring Boot打包的可执行Jar时,JVM会忽略-cp参数指定的外部类路径,转而使用Spring Boot自带的LaunchedURLClassLoader,仅加载Jar内部BOOT-INF/lib目录下的依赖,你通过-cp指定的NATS相关Jar不会被加载。 - spark.jars参数的适用场景:这个配置是为Spark集群模式设计的,用于将Jar分发到集群节点;本地模式下,Spark会尝试从指定路径加载Jar,但路径如果是相对路径,可能与运行命令的工作目录不匹配,且Spark的类加载器无法访问Spring Boot类加载器之外的外部Jar。
解决方案
方案一:将NATS依赖打包到Spring Boot可执行Jar中(推荐)
这个方法能彻底避免类加载隔离问题,步骤如下:
- 将NATS本地Jar安装到本地Maven仓库
执行以下命令,把libs目录下的两个Jar安装到本地仓库:mvn install:install-file -Dfile=libs/nats-spark-connector-balanced_2.12-1.1.4.jar -DgroupId=external.group -DartifactId=nats-spark-connector-balanced_2.12 -Dversion=1.1.4 -Dpackaging=jar mvn install:install-file -Dfile=libs/jnats-2.17.1.jar -DgroupId=external.group -DartifactId=jnats -Dversion=2.17.1 -Dpackaging=jar - 修改pom.xml依赖配置
取消之前注释的NATS依赖,移除system作用域和systemPath,改为普通依赖:<dependency> <groupId>external.group</groupId> <artifactId>nats-spark-connector-balanced_2.12</artifactId> <version>1.1.4</version> </dependency> <dependency> <groupId>external.group</groupId> <artifactId>jnats</artifactId> <version>2.17.1</version> </dependency> - 重新打包并运行
执行打包命令:
运行生成的可执行Jar:mvn clean packagejava -jar spark-learning-0.0.1-SNAPSHOT.jar
方案二:打包为普通Jar,通过-cp指定全量类路径
如果不想将NATS依赖打包到可执行Jar中,可以修改打包方式:
- 修改pom.xml的打包配置
移除spring-boot-maven-plugin的repackage执行目标,或者改用普通Maven打包插件,确保生成的是普通Jar而非Spring Boot可执行Jar。 - 运行时指定完整类路径
运行时将所有依赖(Spark、NATS、自身Jar)都加入类路径:# Linux/macOS java -cp "../libs/*.jar:spark-learning-0.0.1-SNAPSHOT.jar" com.test.MinIOTester # Windows java -cp "../libs/*.jar;spark-learning-0.0.1-SNAPSHOT.jar" com.test.MinIOTester
方案三:调整SparkSession配置,指定本地Jar路径
如果坚持使用外部Jar,需要修改SparkSession配置,让Spark的类加载器能找到Jar:
SparkSession spark = SparkSession.builder() .appName("spark-with-nats") .master("local") // 指定驱动端额外类路径,本地模式生效 .config("spark.driver.extraClassPath", "../libs/nats-spark-connector-balanced_2.12-1.1.4.jar:../libs/jnats-2.17.1.jar") .config("spark.sql.streaming.checkpointLocation","tmp/checkpoint") .config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension") .config("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog") .getOrCreate();
注意:Windows系统下路径分隔符使用分号
;,且路径要确保与运行命令的工作目录匹配。
内容的提问来源于stack exchange,提问作者VGH
相关产品推荐
相关产品推荐

