使用maven集成java和scala开发环境
git地址:https://gitee.com/jyq_18792721831/sparkmaven.git
创建项目
我们首先创建一个普通的maven项目
创建项目后接着创建一个hello的模块
也是普通的maven模块
增加scala依赖
我们在父项目中是不会写代码的,父项目只是为了管理子项目的,所以父项目的src目录可以删除
我们在父项目的pom.xml中增加依赖
如下
<?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.study.sparkmaven</groupId>
<artifactId>sparkmaven</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
<modules>
<module>hello</module>
</modules>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<!-- scala 的版本 -->
<version.scala>2.12.15</version.scala>
</properties>
<dependencyManagement>
<dependencies>
<!-- scala 库 -->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${version.scala}</version>
</dependency>
<!-- scala 编译 -->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-compiler</artifactId>
<version>${version.scala}</version>
</dependency>
<!-- scala 映射 -->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-reflect</artifactId>
<version>${version.scala}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
接着在hello模块的pom.xml中将scala相关的依赖引入
创建目录
我们需要在src目录下创建我们的源码目录和资源目录
并使用右键标注为源码和资源
标注完成后如下
接着创建我们的包目录
安装scala插件
首先打开设置
在插件处查询scala插件并安装,可能需要多试几次
而且scala插件比较大的,下载不一定能一次成功。如果确实无法下载,可以去idea的插件市场中离线下载,然后离线安装。
scala的hello world
我们在scala的源码下进行开发
此时在新建文件的时候,是找不到scala的选项的
我们刷新整个maven项目,让maven下载依赖
当然,刷新完还是无法创建scala的项目
我们需要告诉idea,我们的项目需要支持scala,所以我们需要把scala的sdk加入
首先保证你的全局的sdk有scala,如果没有需要点击+增加
比如
当然,最最前提是你需要安装scala的插件,只有安装了scala的插件,才能开发与scala有关的内容。
我们在模块设置将scala加入
选择我们的scala版本的sdk,如果你有多个版本的scala的sdk,一定注意版本
我们也可以把scala的sdk加入到根项目中
当然,你在加入到根项目中后,在子项目中还是需要增加一次
我们这里增加实际上与maven增加scala的依赖是没有任何冲突的
我们在这里增加依赖,只是告诉idea,在我们的项目中需要用到scala的一些功能而已
我们选择增加一个object,写如下内容
package com.study.sparkmaven.hello
object Hello {
def main(args: Array[String]): Unit = {
println("hello scala")
}
}
然后点击运行
如下
maven 插件
我们虽然开发了scala的hello world,但是这没啥意义,只是说明我们的idea目前支持了scala的语法,我们还需要安装一些maven的插件,用于帮助我们做其他的处理。
还是和依赖一样的管理方式,我们在父项目的pom.xml中定义依赖和插件,包括一些插件的通用配置,然后在子项目中增加子项目个性化的依赖,以及子项目个性化的插件配置。
约定所有的版本号都在properties
中统一配置。
插件都在build-pluginManage-plugins
下配置
配置仓库
如果不配置仓库,默认是从maven的中央仓库下载依赖和插件,会比较慢,我们可以配置国内镜像,加快下载速度
<!-- 私有仓库配置 -->
<pluginRepositories>
<pluginRepository>
<id>maven-net-cn</id>
<name>Maven China Mirror</name>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
</pluginRepository>
</pluginRepositories>
<repositories>
<repository>
<id>central</id>
<name>Maven China Mirror</name>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
</repository>
</repositories>
我们在父项目的pom.xml中配置即可。
maven-compile-plugin
我们在父项目的pom.xml中加入maven-compile-plugin插件
首先定义插件的版本
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<!-- scala 的版本 -->
<version.scala>2.12.15</version.scala>
<!-- maven-compile-plugin 的版本号 -->
<version.maven.compile.plugin>3.9.0</version.maven.compile.plugin>
</properties>
接着配置插件
<build>
<pluginManagement>
<plugins>
<!-- maven-compile-plugin 插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${version.maven.compile.plugin}</version>
<!-- 配置信息 -->
<configuration>
<!-- 源码 -->
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<!-- 编码方式 -->
<encoding>UTF-8</encoding>
<!-- 支持调试 -->
<debug>true</debug>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
或许你可能会比较疑惑,我们怎么知道我可以使用哪些插件,以及这些插件的版本是什么呢?
你可以在Maven – Available Plugins (apache.org)查询所有可用的插件,点击插件名字可以进入到插件的文档界面,里面会有详细的版本号,以及如何使用等信息
详细信息
千万记得,我们在父项目中配置插件后,还需要在子项目中引入
我们在hello项目的pom.xml中引入
这样做的好处是在一个pom.xml中统一管理项目中的插件的版本等信息。一些通用的配置也可以在父项目的pom.xml中配置。
maven-scala-plugin
我们接着配置scala的插件
定义版本
<!-- maven-scala-plugin 的版本号 -->
<version.maven.scala.plugin>2.15.2</version.maven.scala.plugin>
接着引入和配置插件
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>${version.maven.scala.plugin}</version>
<configuration>
<!-- scala的版本号 -->
<scalaVersion>${version.scala}</scalaVersion>
</configuration>
<!-- 配置监听器 -->
<executions>
<!-- 监听器 -->
<execution>
<!-- 如果有多个监听器则必须设置id,而且不能重复 -->
<id>scala-compile</id>
<!-- 监听的操作 -->
<phase>compile</phase>
<!-- 监听器触发后执行的操作 -->
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>scala-test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
接着在子项目中引入插件,并配置执行(子项目的pom.xml)
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<!-- 子项目个性化配置 -->
<configuration>
<!-- 配置执行 -->
<launchers>
<!-- 可以配置多个执行 -->
<launcher>
<!-- 可以使用 run id 选择不同的执行 -->
<id>hello</id>
<!-- 定义执行的入口,这个是核心 -->
<mainClass>com.study.sparkmaven.hello.Hello</mainClass>
<!-- 执行的附加参数等 -->
<jvmArgs>
<jvmArg>-Xmx128m</jvmArg>
<jvmArg>-Djava.library.path=...</jvmArg>
</jvmArgs>
</launcher>
</launchers>
</configuration>
</plugin>
我们此时刷新maven
如果一切正常,此时会有scala的一些操作
因为我们只配置了一个scala的执行,所以可以直接使用这个run,在只有一个执行的时候,是不需要指定执行的id,如果有多个,不指定执行id,也是会取第一个。
我们双击运行scala:run
第一次会比较慢,因为需要下载依赖。
此时虽然可以运行,但是也是基于idea的,如果我们直接打jar包,并在java环境运行是不行的。
会提示我们没有配置主类
maven-jar-plugin
这个插件就是用于指定主类的,打包出来的jar包可以运行
指定版本
<!-- maven-jar-plugin 的版本号 -->
<version.maven.jar.plugin>3.2.2</version.maven.jar.plugin>
配置插件
<!-- maven-jar-plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${version.maven.jar.plugin}</version>
<executions>
<execution>
<!-- 监听打包操作 -->
<phase>package</phase>
<goals>
<!-- 打包后执行 jar 操作 -->
<goal>jar</goal>
</goals>
<configuration>
<!-- 生成的jar包的附加字段,为了防止重复 -->
<classifier>client</classifier>
</configuration>
</execution>
</executions>
</plugin>
子项目中引入
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<!-- 增加运行时环境 -->
<addClasspath>true</addClasspath>
<!-- 指定依赖的位置为lib目录 -->
<classpathPrefix>lib/</classpathPrefix>
<!-- 指定主类 -->
<mainClass>com.study.sparkmaven.hello.Hello</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
我们刷新项目,并clean后执行package
执行后会生成两个jar包
第二个jar包就是插件生成的,尝试执行
我们依次执行两个jar包,发现还是无法执行,但是至少不是找不到主类的错误了,而是找不到scala的相关库。
这是因为我们虽然指定了依赖在lib
目录,但是现在lib目录可是空的。
也就是没有依赖。
此时就需要下面这个插件了。
maven-dependency-plugin
依赖拷贝插件,可以将项目的依赖拷贝到指定位置,为打包做准备。
前面我们打包,因为lib
目录为空,导致打包的jar中缺少相关的依赖。
我们首先定义版本
<!-- maven-dependency.plugin 的版本号 -->
<version.maven.dependency.plugin>3.2.0</version.maven.dependency.plugin>
接着配置插件
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>${version.maven.dependency.plugin}</version>
<executions>
<execution>
<id>dependency-copy</id>
<!-- 监听打包操作 -->
<phase>package</phase>
<goals>
<!-- 打包操作前执行依赖拷贝操作 -->
<goal>copy-dependencies</goal>
</goals>
<configuration>
<!-- 依赖拷贝的目的目录 -->
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<!-- 是否可以覆盖release版本的依赖 -->
<overWriteReleases>false</overWriteReleases>
<!-- 是否可以覆盖snapshots版本的依赖 -->
<overWriteSnapshots>false</overWriteSnapshots>
<!-- 是否可以覆盖,当有新版本时 -->
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
我们在子项目中引入
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
因为依赖拷贝算是一个通用的,所以这里把相关的设置放到父项目中的pom.xml中了
刷新maven,先后执行clean和package
接着尝试执行jar包
maven-assembly-plugin
在上面中,实际上是两个插件配合使用,第一个插件用于拷贝依赖,第二个插件用于打包。
但是打包只是能打包jar包,如果你想打war包,或者其他的就不行了。
所以还有一个插件,不仅仅能打jar包,还能打其他的包,而且是集成了这两个插件的功能。
就是大名鼎鼎的assembly
插件。
版本定义
<!-- maven-assembly-plugin 的版本号 -->
<version.maven.assembly.plug>3.3.0</version.maven.assembly.plug>
插件定义
<!-- maven-assembly-plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>${version.maven.assembly.plug}</version>
<!-- 配置 -->
<configuration>
<!-- 启动的功能 -->
<descriptorRefs>
<!-- 打jar包自动拷贝依赖 -->
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<!-- 监听器 -->
<execution>
<id>make-assembly</id>
<!-- 监听 打包命令 -->
<phase>package</phase>
<goals>
<!-- 目前只有这个操作 -->
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
子项目依赖,我们去掉之前的maven-jar-plugin
和maven-dependency-plugin
插件
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-jar-plugin</artifactId>-->
<!-- <configuration>-->
<!-- <archive>-->
<!-- <manifest>-->
<!-- <!– 增加运行时环境 –>-->
<!-- <addClasspath>true</addClasspath>-->
<!-- <!– 指定依赖的位置为lib目录 –>-->
<!-- <classpathPrefix>lib/</classpathPrefix>-->
<!-- <!– 指定主类 –>-->
<!-- <mainClass>com.study.sparkmaven.hello.Hello</mainClass>-->
<!-- </manifest>-->
<!-- </archive>-->
<!-- </configuration>-->
<!-- </plugin>-->
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-dependency-plugin</artifactId>-->
<!-- </plugin>-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.study.sparkmaven.hello.Hello</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
刷新项目并执行clean和package
我们尝试执行
因为我们注释了maven-jar-plugin
的配置,所以打包的默认的包是找不到主类的。
而maven-assembly-plugin
因为我们配置了主类,而且还启动了依赖拷贝功能,所以打出来的jar不仅仅有主类,还有scala的依赖,可以直接运行。
spark 开发环境
我们创建一个空的maven项目
别忘记告诉idea我们需要scala环境
创建目录
包目录
主类
加入spark依赖和scala依赖,以及相关的插件
spark的版本
我在定义properties时遵循一个习惯:
依赖的版本号以.version
结尾
插件的版本号以version
开头,.plugin
结尾
<!-- spark 依赖的版本 -->
<spark.version>3.2.0</spark.version>
增加spark-core_2.12
依赖,因为scala二进制不兼容,所以需要注意哦
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.12</artifactId>
<version>${spark.version}</version>
<!-- idea运行需要注释掉,打包需要放开 -->
<!-- <scope>provided</scope>-->
</dependency>
因为spark打的包最终是提交给spark执行的,本身就有spark相关的依赖,所以我们打包的时候不需要将spark的依赖打进去,否则会非常大,而且因为spark本身的依赖关系比较复杂,处理依赖的传递等问题会比较麻烦。
子项目的插件
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<configuration>
<launchers>
<launcher>
<id>wordcount</id>
<mainClass>com.study.spark.maven.wordcount.WordCount</mainClass>
</launcher>
</launchers>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.study.spark.maven.wordcount.WordCount</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
刷新maven项目后就可以在主类中编写spark代码了
如果你在开发代码的时候,发现scala的相关关键词无法识别,请重新刷新idea的缓存
选择这个重启idea即可
wordcount的代码如下
package com.study.spark.maven.wordcount
import org.apache.spark.{SparkConf, SparkContext}
object WordCount {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local").setAppName("wordcount").set("spark.testing.memory","2147480000")
val sc = new SparkContext(conf)
sc.textFile("E:\\java\\sparkmaven\\pom.xml")
.flatMap(_.split(" "))
.map(_->1)
.reduceByKey(_+_)
.collect()
.foreach(println(_))
}
}
我们首先直接点击运行
接着使用scala:run
执行
接着放开spark-core的scope注释,进行打包
第一次运行会比较慢,而且我们基本上都是用的最新的spark的依赖,镜像库可能还没有同步,所以更慢。
30多分钟,我是楞是等它结束了
看看打的包能不能运行
也是可以运行的
maven-assembly-plugin活生生的把spark打包打进去了
如果我们不需要将spark打包,那么应该会快很多的
不过会提示找不到spark的类
不过第二次打包就会快很多了
而且不管我们有没有把spark的依赖打包,提交给spark执行一般不会有问题。
但是需要注意jar包内的spark和spark环境的版本之间可能会存在冲突,所以不建议把spark的依赖进行打包。
我们尝试把打的jar包,提交到spark集群中运行
首先启动集群环境
然后使用xshell链接(家庭版免费)
启动hdfs集群
接着启动spark集群
启动spark历史记录服务
这个wordcount是在sbt构建中执行的,详见spark源码编译和集群部署以及idea中sbt开发环境集成_a18792721831的博客-CSDN博客
因为我们的hdfs上面已经有文件了,所以需要修改代码,使用hdfs的文件,而不是本地的文件,而且使用集群的spark而不是本地的spark
修改后的WordCount如下
package com.study.spark.maven.wordcount
import org.apache.spark.{SparkConf, SparkContext}
object WordCount {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("spark://hadoop01:4040").setAppName("wordcount.maven")
val sc = new SparkContext(conf)
sc.textFile("hdfs://hadoop01:8020/input/build.sbt")
.flatMap(_.split(" "))
.map(_->1)
.reduceByKey(_+_)
.collect()
.foreach(println(_))
}
}
打包并将jar包上传到服务器,我们不需要spark的依赖
使用如下命令提交
spark-submit --class com.study.spark.maven.wordcount.WordCount --master spark://hadoop01:4040 wordcount-1.0-jar-with-dependencies.jar
成功执行
从spark历史记录中也能查看
其实我也有个疑问,如果我们把spark的依赖打包了,还能执行吗?
试试!
含有spark依赖的包,128M,😆
执行:
没啥区别
这是因为我服务器也是3.2.0版本,而且是使用最新的源码编译的,详见spark源码编译和集群部署以及idea中sbt开发环境集成_a18792721831的博客-CSDN博客,本地的依赖也是3.2.0,所以jar包中有没有spark依赖都是可以的。
如果你无法准确的知道服务器的spark的版本,以及服务器的spark的scala版本,建议还是不要把spark依赖打包。