Spark Core 开发环境全链路配置指南:从 JDK 到 IDEA 一文搞定
Spark Core 之 Spark 基于开发工具的详细配置讲解
摘要:JDK → Scala → Spark → Maven → IDEA 全链路安装配置,完整 pom.xml 可直接复制使用,WordCount 源码逐行解析,10 个高频错误排查,4 张原创架构图,四阶段学习路线。面向 Java、大数据及 AI 开发工程师,确保读完即可动手搭建 Spark 开发环境。
一、开发环境四层架构总览
Spark 开发环境从底层到上层分为四个层级,每层对下层有严格的版本约束:

| 层级 | 组件 | 关键约束 |
|---|---|---|
| Layer 0: OS | Linux/macOS/Windows | Windows 需 winutils.exe |
| Layer 1: Runtime | JDK 8 + Scala 2.12.15 | Spark 3.x = Scala 2.12 |
| Layer 2: Build | Maven 3.6+ / SBT 1.5+ | shade-plugin 必配 |
| Layer 3: IDE | IntelliJ IDEA + Scala Plugin | Run Config -Dspark.master=local[4] |
| Layer 4: Deploy | local / YARN / K8s / Standalone | client 调试 / cluster 生产 |
二、JDK 安装配置
Spark 3.x 官方推荐 JDK 8(1.8.0_311+),Spark 3.3+ 开始试验性支持 JDK 11 和 17。生产环境强烈建议 JDK 8。
Linux(Ubuntu/Debian)
# 安装 OpenJDK 8
sudo apt update
sudo apt install openjdk-8-jdk -y
# 验证安装
java -version
# 输出示例: openjdk version "1.8.0_312"
# 确认 JAVA_HOME
echo $JAVA_HOME
# 若为空,追加到 ~/.bashrc:
echo 'export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64' >> ~/.bashrc
source ~/.bashrc
macOS
# 通过 Homebrew 安装
brew install openjdk@8
# 设置 JAVA_HOME(追加到 ~/.zshrc)
echo 'export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)' >> ~/.zshrc
source ~/.zshrc
Windows
1. 下载 [Oracle JDK 8](https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html) 安装包
2. 安装到 C:\Program Files\Java\jdk1.8.0_xxx
3. 系统环境变量:JAVA_HOME = C:\Program Files\Java\jdk1.8.0_xxx
4. Path 追加 %JAVA_HOME%\bin
⚠️ 不要用 JDK 11+ 运行 Spark 3.2 以下版本,否则直接报 `java.lang.NoClassDefFoundError`。版本对照:
>
| Spark 版本 | 推荐 JDK | Scala 版本 |
|-----------|---------|-----------|
| 2.4.x | JDK 8 | 2.11.12 |
| 3.0 - 3.2 | JDK 8 | 2.12.15 |
| 3.3+ | JDK 8 / 11 | 2.12.15 / 2.13.8 |
| 3.4+ | JDK 8 / 11 / 17 | 2.12.17 / 2.13.8 |
三、Scala 安装与版本匹配
铁律:你代码中的 Scala 版本 **必须与 Spark 编译时的 Scala 版本完全一致**。
Linux 安装
# 方式1:apt 安装(推荐)
sudo apt install scala -y
# 方式2:手动下载
wget https://downloads.lightbend.com/scala/2.12.15/scala-2.12.15.tgz
tar -xzf scala-2.12.15.tgz -C /opt/
echo 'export SCALA_HOME=/opt/scala-2.12.15' >> ~/.bashrc
echo 'export PATH=$PATH:$SCALA_HOME/bin' >> ~/.bashrc
source ~/.bashrc
# 验证
scala -version
# 输出: Scala code runner version 2.12.15
如何确定正确的 Scala 版本?
# 启动 spark-shell,看第一行日志
spark-shell
# 日志第一行会显示:
# Using Scala version 2.12.15 (OpenJDK 64-Bit Server VM, Java 1.8.0_312)
四、Spark 安装配置
4.1 下载与解压
# 下载 Spark 3.3.0(预编译 Hadoop 3.x 版本)
wget https://archive.apache.org/dist/spark/spark-3.3.0/spark-3.3.0-bin-hadoop3.tgz
# 解压到 /opt
tar -xzf spark-3.3.0-bin-hadoop3.tgz -C /opt/
# 创建软链接(方便版本切换)
sudo ln -s /opt/spark-3.3.0-bin-hadoop3 /opt/spark
# 环境变量
echo 'export SPARK_HOME=/opt/spark' >> ~/.bashrc
echo 'export PATH=$PATH:$SPARK_HOME/bin:$SPARK_HOME/sbin' >> ~/.bashrc
echo 'export PYSPARK_PYTHON=python3' >> ~/.bashrc
source ~/.bashrc
# 验证
spark-shell --version
4.2 spark-defaults.conf(全局默认配置)
# $SPARK_HOME/conf/spark-defaults.conf
# ===== 序列化 =====
spark.serializer org.apache.spark.serializer.KryoSerializer
# 注册自定义类(避免 Kryo "Class not registered" 错误)
spark.kryo.classesToRegister com.example.MyClass
# ===== Shuffle =====
# Shuffle 分区数(默认 200,大集群可调大)
spark.sql.shuffle.partitions 200
# ===== 动态资源分配(YARN/K8s) =====
spark.dynamicAllocation.enabled true
spark.dynamicAllocation.minExecutors 2
spark.dynamicAllocation.maxExecutors 50
# ===== History Server =====
spark.eventLog.enabled true
spark.eventLog.dir hdfs://namenode:8020/spark-history
spark.history.fs.logDirectory hdfs://namenode:8020/spark-history
# ===== 压缩 =====
spark.sql.adaptive.enabled true
spark.sql.adaptive.coalescePartitions.enabled true
spark.io.compression.codec snappy
4.3 spark-env.sh(环境级配置)
# $SPARK_HOME/conf/spark-env.sh
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export HADOOP_CONF_DIR=/opt/hadoop/etc/hadoop
export SPARK_WORKER_MEMORY=8g
export SPARK_WORKER_CORES=4
export SPARK_MASTER_HOST=master-node
五、Maven 安装与完整 pom.xml
5.1 安装 Maven
# Linux
sudo apt install maven -y
# macOS
brew install maven
# 验证
mvn -version
# Apache Maven 3.8.6
5.2 完整 pom.xml(可直接复制使用)
以下是一个 生产级 Spark 项目 pom.xml,涵盖 Spark Core + Spark SQL、Kryo 序列化、maven-shade-plugin 打包等配置:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spark-project</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- ===== 统一版本管理(关键!避免传递依赖冲突) ===== -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<scala.version>2.12.15</scala.version>
<scala.binary.version>2.12</scala.binary.version>
<spark.version>3.3.0</spark.version>
<jackson.version>2.13.4</jackson.version>
<!-- maven 插件版本 -->
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<!-- ===== 依赖管理 ===== -->
<dependencies>
<!-- Spark Core — scope=provided(集群已有,不打入 fat jar) -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
<scope>provided</scope>
</dependency>
<!-- Spark SQL — scope=provided -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
<scope>provided</scope>
</dependency>
<!-- Scala 标准库 — scope=provided(集群已有) -->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
<scope>provided</scope>
</dependency>
<!-- ===== 第三方依赖 — scope=compile(集群没有,需打入 fat jar) ===== -->
<!-- MySQL JDBC -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- JSON 处理 — 锁定版本避免与 Spark 内置版本冲突 -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-scala_${scala.binary.version}</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>org.scalatest</groupId>
<artifactId>scalatest_${scala.binary.version}</artifactId>
<version>3.2.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- ===== 构建插件 ===== -->
<build>
<plugins>
<!-- 1. Scala 编译插件(必须先于 Java 编译) -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>4.6.3</version>
<executions>
<execution>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>scala-test-compile</id>
<phase>process-test-resources</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<scalaVersion>${scala.version}</scalaVersion>
<args>
<arg>-target:jvm-1.8</arg>
</args>
</configuration>
</plugin>
<!-- 2. Maven 编译插件(Java 编译) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<!-- 3. maven-shade-plugin — 打包 Fat JAR -->
<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>
<!-- 排除签名文件(否则报 SecurityException) -->
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<!-- 合并 SPI services(Kryo/FST 等序列化框架依赖) -->
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/services/org.apache.spark.sql.sources.DataSourceRegister</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.example.WordCount</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
5.3 pom.xml 配置逐段说明
5.4 构建与打包
| 配置区 | 关键点 | 常见错误 |
|---|---|---|
| ` | 统一管理版本号,避免传递依赖冲突 | 版本号分散在 ` |
| `spark-*` 依赖 | **scope=provided**(集群已有) | 写成 compile → 打包后与集群冲突 → ClassNotFoundException |
| 第三方依赖 | scope 默认 compile,打入 fat jar | 把 provided scope 的依赖也打进去 → 签名冲突 |
| `scala-maven-plugin` | **必须先于 java 编译**(process-resources 阶段) | 用 maven-compiler-plugin 编译 scala → 编译失败 |
| `shade-plugin` filters | 排除 `META-INF/*.SF/*.DSA/*.RSA` | 不排除 → `SecurityException: Invalid signature file` |
| `shade-plugin` transformers | `AppendingTransformer` 合并 services 文件 | 不配置 → Kryo/FST 序列化 SPI 不工作 |
# 清理 + 编译 + 打包
mvn clean package
# 只编译不测试(加速)
mvn clean package -DskipTests
# 查看依赖树(排查冲突)
mvn dependency:tree
# 查看合并后的有效 POM
mvn help:effective-pom
六、IDEA 项目搭建与本地调试
6.1 创建项目
IntelliJ IDEA → File → New → Project from Existing Sources
→ 选择包含 pom.xml 的目录 → Import as Maven Project
→ 等待依赖下载完成
6.2 配置 Run Configuration(断点调试 Spark)
Run → Edit Configurations → + → Application
Name: WordCount (local)
Main class: com.example.WordCount
VM options: -Dspark.master=local[4] -Xms512m -Xmx1024m
Program args: data/input.txt data/output/
Environment: HADOOP_HOME=/opt/hadoop
`-Dspark.master=local[4]` 表示本地模式,4 个线程模拟 Spark 集群。此时 Driver 和 Executor 在同一 JVM 中,可以直接在 `flatMap` / `reduceByKey` 内部打断点!
6.3 Windows 额外配置
// 在 main() 方法开头添加(仅 Windows 需要)
System.setProperty("hadoop.home.dir", "C:\\hadoop");
并下载 [winutils.exe](https://github.com/steveloughran/winutils) 放到 C:\hadoop\bin\。
七、完整代码示例:WordCount + 生产级 ETL 模板
7.1 WordCount(Scala 版本,逐行注释)
package com.example
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
/**
* Spark Core WordCount 示例
*
* 提交命令:
* spark-submit --master yarn --deploy-mode cluster \
* --executor-memory 2G --executor-cores 2 --num-executors 5 \
* --class com.example.WordCount spark-project-1.0.jar \
* hdfs:///input/words.txt hdfs:///output/wc
*/
object WordCount {
def main(args: Array[String]): Unit = {
// 1. 参数校验
if (args.length < 2) {
System.err.println("Usage: WordCount <inputPath> <outputPath>")
System.exit(1)
}
val Array(inputPath, outputPath) = args
// 2. 创建 SparkConf(生产环境所有配置通过 spark-submit --conf 传入)
val conf = new SparkConf()
.setAppName("WordCount") // 作业名(显示在 Web UI)
// .setMaster("local[4]") // 本地调试时取消注释
.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
.registerKryoClasses(Array(classOf[WordCount])) // 注册自定义类
// 3. 创建 SparkContext(一个 JVM 只能有一个)
val sc = new SparkContext(conf)
try {
// 4. 读取输入文件 → RDD[String]
// textFile 是 Transformation,惰性求值,此处不触发计算
val lines: RDD[String] = sc.textFile(inputPath)
// 5. 核心计算逻辑 — Transformation 链
val wordCounts: RDD[(String, Int)] = lines
.flatMap(line => line.split("\\s+")) // 分词 → RDD[String]
.filter(word => word.nonEmpty) // 过滤空字符串
.map(word => (word.toLowerCase, 1)) // 转小写并计数 → RDD[(String, Int)]
.reduceByKey(_ + _) // 按 key 聚合(Map 端预聚合)→ RDD[(String, Int)]
// 6. Action — 触发计算,将结果写入文件
wordCounts.saveAsTextFile(outputPath)
// 7. 打印 Top 10 到 Driver 日志
val top10: Array[(String, Int)] = wordCounts
.sortBy(_._2, ascending = false) // 按计数降序
.take(10) // Action — 取前 10 到 Driver
println("=" * 50)
println("Top 10 Words:")
top10.foreach { case (word, count) =>
println(f" $word%-20s $count%5d")
}
println("=" * 50)
} finally {
// 8. 无论成功或异常,必须关闭 SparkContext 释放资源
sc.stop()
}
}
}
7.2 代码要点逐行解析
7.3 生产级 ETL 模板(Spark SQL + DataFrame)
| 行/段 | 说明 | 易错点 |
|---|---|---|
| `SparkConf()` | 配置对象,链式调用 | `.setAppName()` 必调,否则 UI 显示混乱 |
| `.registerKryoClasses()` | 注册自定义类到 Kryo | 不注册 → `Class is not registered` 异常 |
| `new SparkContext(conf)` | 每个 JVM 只能创建一个 | 重复创建 → `Only one SparkContext may be running` |
| `sc.textFile()` | **Transformation**,惰性求值 | 此时不读取数据,只构建 DAG |
| `flatMap → filter → map` | 三个连续的窄依赖 Transformation | Pipeline 在同一个 Stage 内执行,无 Shuffle |
| `reduceByKey(_ + _)` | **宽依赖 Transformation**,触发 Shuffle | Map 端先本地聚合(Combine),减少网络 IO |
| `saveAsTextFile()` | **Action** — 触发整个 DAG 执行 | 输出目录不能已存在,否则报错 |
| `sortBy + take(10)` | Action — 将结果拉到 Driver | 数据量大时慎用 `collect()`,用 `take(N)` |
| `sc.stop()` | 放在 `finally` 块 | 不调用 → 资源泄露 → Web UI 端口不释放 |
package com.example
import org.apache.spark.sql.{SparkSession, DataFrame}
import org.apache.spark.sql.functions._
/**
* Spark SQL ETL 模板
* 场景:从 MySQL 读取数据 → 清洗转换 → 写入 HDFS Parquet
*/
object ETLJob {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder()
.appName("Daily ETL Job")
.config("spark.sql.shuffle.partitions", "200")
.enableHiveSupport() // 启用 Hive 支持(可选)
.getOrCreate()
try {
// 1. 从 MySQL 读取源数据
val sourceDF: DataFrame = spark.read
.format("jdbc")
.option("url", "jdbc:mysql://mysql-host:3306/mydb")
.option("dbtable", "orders")
.option("user", "etl_user")
.option("password", sys.env("MYSQL_PASSWORD")) // 密码从环境变量取
.load()
// 2. 数据清洗与转换
val cleanedDF = sourceDF
.filter(col("amount") > 0) // 过滤无效金额
.filter(col("status").isin("PAID", "SHIPPED")) // 只保留有效状态订单
.dropDuplicates("order_id") // 按 order_id 去重
.withColumn("year_month", date_format(col("created_at"), "yyyy-MM"))
.withColumn("amount_taxed", col("amount") * 0.9) // 计算税后金额
// 3. 按月份汇总
val summaryDF = cleanedDF
.groupBy("year_month")
.agg(
count("order_id").as("order_count"),
sum("amount_taxed").as("total_amount"),
avg("amount_taxed").as("avg_amount")
)
.orderBy("year_month")
// 4. 写入 HDFS(Parquet 格式,Snappy 压缩)
summaryDF.write
.mode("overwrite")
.format("parquet")
.option("compression", "snappy")
.save("hdfs:///data/warehouse/order_summary/")
} finally {
spark.stop()
}
}
}
八、部署模式全景对比

spark-submit 生产命令模板
| 模式 | --master | 适用场景 | Driver 位置 | 资源管理 |
|---|---|---|---|---|
| **Local** | `local[4]` | 开发、调试、单元测试 | 本地 JVM | 无 |
| **Standalone** | `spark://host:7077` | 小团队、不依赖 Hadoop | 提交节点(client) 或 Worker(cluster) | Spark 自带 |
| **YARN** | `yarn` | **生产 Hadoop 集群首选** | 提交节点(client) 或 NodeManager(cluster) | YARN RM |
| **Kubernetes** | `k8s://https://api:6443` | 云原生、容器化 | Pod | K8s Scheduler |
spark-submit \
--master yarn \
--deploy-mode cluster \
--name "Daily-ETL-Job" \
--class com.example.ETLJob \
--executor-memory 4G \
--executor-cores 2 \
--num-executors 10 \
--driver-memory 2G \
--conf spark.serializer=org.apache.spark.serializer.KryoSerializer \
--conf spark.sql.shuffle.partitions=200 \
--conf spark.dynamicAllocation.enabled=true \
--conf spark.eventLog.enabled=true \
--jars mysql-connector-java-8.0.30.jar \
--files log4j.properties \
spark-project-1.0-SNAPSHOT.jar \
arg1 arg2
⚠️ `--deploy-mode cluster`:Driver 运行在 YARN 集群内,避免提交节点宕机导致整个作业失败。生产环境必须用 cluster 模式。
九、10 个高频错误排查
排查工具速查
| # | 错误信息 | 根因 | 解决方案 |
|---|---|---|---|
| 1 | `NoClassDefFoundError: SparkConf` | pom.xml 中 scope 写成 compile | 改为 **provided** |
| 2 | `Task not serializable` | 闭包引用未实现 Serializable 的对象 | 方案A: 实现 Serializable;方案B: 使用 `mapPartitions` |
| 3 | `Failed to locate winutils` | Windows 缺少 hadoop 二进制 | 下载 winutils.exe 到 `%HADOOP_HOME%\bin` |
| 4 | `scala compilation error` | mixed compile 顺序错误 | scala-maven-plugin 设在 process-resources 阶段 |
| 5 | `SecurityException: invalid signature` | jar 包签名文件未排除 | shade-plugin 排除 `META-INF/*.SF,*.DSA,*.RSA` |
| 6 | `NoSuchMethodError` | 多版本依赖冲突 | `mvn dependency:tree` + shade relocation |
| 7 | `Container exited with exit code 137` | Executor OOM 被 YARN kill | 加大 `--executor-memory` 或 `spark.executor.memoryOverhead` |
| 8 | `Class is not registered: com.example.MyClass` | Kryo 未注册自定义类 | `spark.kryo.classesToRegister = com.example.MyClass` |
| 9 | `Driver 退出无日志` | cluster 模式日志在 YARN Logs | 用 `yarn logs -applicationId |
| 10 | `cache() 后数据没有缓存` | cache 是 lazy 的 | 必须调 Action(如 `count()`)才会触发缓存 |
# 1. 查看依赖树(找冲突)
mvn dependency:tree | grep -E "jackson|guava|netty"
# 2. 查看完整合并 POM
mvn help:effective-pom > effective-pom.xml
# 3. YARN 日志查看
yarn logs -applicationId application_xxx
# 4. 进制 jar 包内容
jar tf spark-project-1.0.jar | head -20
# 5. 检查是否有 provided 依赖被打入
jar tf spark-project-1.0.jar | grep spark-core
十、最佳实践清单
| # | 实践 | 理由 |
|---|---|---|
| ✅ 1 | ` | 避免传递依赖冲突,一处改全改 |
| ✅ 2 | Spark/Hadoop/Scala scope=provided | 集群已有,打包冲突 |
| ✅ 3 | 生产启用 Kryo 序列化 | 比 Java 序列化快 10 倍 |
| ✅ 4 | 配置通过 `--conf` 传入 | 不同环境不同配置,不硬编码 |
| ✅ 5 | 日志用 `--files log4j.properties` | 不同环境不同级别,独立于代码 |
| ✅ 6 | 先在 `local[4]` 测试 | 节省集群排错时间 |
| ✅ 7 | shade 排除签名 + AppendingTransformer | 必备配置,否则 SPI 不工作 |
十一、学习路线图

| 阶段 | 时长 | 核心内容 | 输出目标 |
|---|---|---|---|
| **Phase 1 基础入门** | 2-3 周 | JDK/Scala/Spark 安装 → IDEA 项目搭建 → RDD 算子 → WordCount | 能独立写第一个 Spark 程序 |
| **Phase 2 核心原理** | 4-6 周 | RDD 血统 + DAG → Shuffle 机制 → 内存管理 → spark-submit → Web UI 调优 | 能调优生产作业 |
| **Phase 3 生态拓展** | 6-8 周 | Spark SQL + DataFrame → Catalyst → Structured Streaming → MLlib → PySpark | 成为全栈 Spark 工程师 |
| **Phase 4 架构调优** | 持续 | DAGScheduler/TaskScheduler 源码 → 内核调优 → 平台化建设 | Spark 专家/架构师 |
Phase 2 和 Phase 3 可以并行——学 Shuffle 的同时就可以写 Spark SQL,理论 + 实战交替是最快路径。
写在最后
Spark 开发环境配置是 第一道门槛,也是最容易被跳过的一步。很多人照着网上的 pom.xml 复制粘贴,出了问题不知道从哪里排查。
本文从 JDK → Scala → Spark → Maven → IDEA 全链路拆解,配合完整 pom.xml 模板(可直接复制)、WordCount 源码逐行注释、10 个高频错误排查、4 张架构图、四阶段学习路线。希望能帮你一次性搞定 Spark 开发环境。
环境稳了,代码才能飞。
starzy · AI Data Engineer · blog.starzy.cn · GitHub: starzy1990.github.io

浙公网安备 33010602011771号