SBT是类似maven和gradle的自动构建和包依赖管理工具,SBT是Scala技术体系下的包管理工具,都是Lightbend公司开发的,官方文档并没有介绍SBT的特点,文档中自称相对于maven和gradle有更少的概念但是如果不看文档将会遇到一些细节问题。个人的感受是,SBT类似scala技术体系的特点,就是设计思路上有一种发烧友的那种设计者自己觉得很酷但是我却感觉华而不实的那种烦躁。
本文的目的是编写一个SBT的实操项目,你不必需要非要理解SBt的原理,也能快速的使用SBT进行日常的工作。
一、安装
1.下载地址https://www.scala-sbt.org/download.html,使用最新版即可
本文使用的是windows操作环境,安装的目录是C:optsbt
2.SBT是scala开发的,所以安装SBT之前需要安装JDK
3.maven使用xml来配置,gradle使用grove脚本,SBT使用scala语言
4.安装完之后通过sbt sbtVersion查看版本信息
$ sbt sbtVersion [warn] No sbt.version set in project/build.properties, base directory: C:Program FilesGit [info] Loading settings for project global-plugins from idea.sbt ... [info] Loading global plugins from C:Userssesa515905.sbt1.0plugins [info] Set current project to git (in build file:/C:/Program%20Files/Git/) [info] 1.2.6
说明:sbtVersion这种参数看着就很丑,一般命令行通过-v --v --version来查看版本信息。另外,version参数好像用来看sbt当前管理的项目的版本号了
二、SBT的配置
SBT的配置一般有两个地方,一个是文件C:optsbtconfsbtconfig.txt,另一个是~/.sbt/repositories文件(没有后缀名)
1.sbtconfig.txt
这个文件安装sbt以后就有
#这个配置文件大部分是JAVA进程启动时候的配置,保留了JAVA进程启动配置的一些命名习惯 -Dfile.encoding=UTF8 #设置SBT运行集成的的资源配置,SBT很慢增加资源可以让SBT快一些 -Xmx1024M -XX:MaxPermSize=512m -XX:ReservedCodeCacheSize=256m #设置SBT的代理设置,从远程仓库下载依赖包的时候使用 #说明,这个代理地址的设置只适应依赖包的下载,不适应SBT插件的下载,目前没有找到SBT插件下载的代理设置,插件你就使用手机流量下载吧 -Dhttp.proxyHost=xxx.xxx.xxx.xxx -Dhttp.proxyPort=80 -Dsbt.log.format=true
2.repositories
这个文件默认是没有的,一般你需要配置私有仓库或者国内源的时候才需要使用
repositories主要配置依赖jar包的仓库服务器,特别是你有私服或者国内源的时候,对应的文件是"~/.sbt/repositories"(没有后缀名)
[repositories] local my: http://xxx.xxx.xxx.xxx:8081/repository/maven-public/ aliyun: http://maven.aliyun.com/nexus/content/groups/public/ typesafe: http://repo.typesafe.com/typesafe/ivy-releases/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext], bootOnly maven-central
三、build.sbt配置
sbt项目的根目录下会有一个build.sbt文件,用来配置包依赖和项目信息,具体内容如下
name := "my-project" javacOptions ++= Seq("-encoding", "UTF-8") ThisBuild / organization := "com.xxxx" ThisBuild / version := sys.props.getOrElse("PACKAGE_VERSION", "2.3") ThisBuild / scalaVersion := "2.12.8" lazy val akkaVersion = "2.5.16" lazy val akkaHttpVersion = "10.1.5" //依赖一个包通过%连接 libraryDependencies +="mysql" % "mysql-connector-java" % "5.1.47" //%%表示包是依赖scala版本号的,scala下的jar包会给每个scala版本都打一个包,这是scala是特色说明scala版本兼容很差 libraryDependencies +="com.typesafe.akka" %% "akka-actor" % akkaVersion // 当出现包依赖冲突时使用excludeDependencies排除一些依赖包,一般之后log4j有这个问题 excludeDependencies += "org.apache.logging.log4j" % "log4j-slf4j-impl" excludeDependencies += "org.apache.logging.log4j" % "log4j-core" excludeDependencies += "org.apache.logging.log4j" % "log4j-api"
四、多项目配置
假定你开发一个上课系统(school),这个项目结构如下
|--school
|--|--school-core
|--|--school-entity
|--|--school-business
|--|--school-service
|--|--school-server
那么你会有一个school根目录以及5个子目录,一共6个目录,这6个目录下都有一个build.sbt文件,即一共6个文件
/school/build.sbt配置
name := "school" javacOptions ++= Seq("-encoding", "UTF-8") ThisBuild / organization := "com.school" ThisBuild / version := sys.props.getOrElse("PACKAGE_VERSION", "2.3") ThisBuild / scalaVersion := "2.12.8" // root 表示跟目录 // aggregate表示根项目包括哪些子项目,这些子项目的名称要跟下面声明的变量名称一样 lazy val root = project.in(file(".")).aggregate(core,entity,business,service,server) lazy val core = project.in(file("school-core")) //通过dependsOn(core)来管理项目依赖关系,如果依赖多个项目则格式为:dependsOn(core,xx1,xx2) lazy val entity = project.in(file("school-entity")).dependsOn(core) //business项目依赖了entity项目,会自动传递对core项目的依赖,不需要代码配置 lazy val business = project.in(file("school-business")).dependsOn(entity) lazy val service = project.in(file("school-service")).dependsOn(business) //配置mainclass的方式:mainClass in Compile := Some("com.school.server.App") lazy val server = project.in(file("school-server")).dependsOn(service).settings( mainClass in Compile := Some("com.school.server.App") )
/school/school-core/build.sbt配置
name := "school-core" lazy val akkaVersion = "2.5.16" lazy val akkaHttpVersion = "10.1.5" //依赖一个包通过%连接 libraryDependencies +="mysql" % "mysql-connector-java" % "5.1.47" //%%表示包是依赖scala版本号的,scala下的jar包会给每个scala版本都打一个包,这是scala是特色说明scala版本兼容很差 libraryDependencies +="com.typesafe.akka" %% "akka-actor" % akkaVersion // 当出现包依赖冲突时使用excludeDependencies排除一些依赖包,一般之后log4j有这个问题 excludeDependencies += "org.apache.logging.log4j" % "log4j-slf4j-impl" excludeDependencies += "org.apache.logging.log4j" % "log4j-core" excludeDependencies += "org.apache.logging.log4j" % "log4j-api"
五、SBT常用命令
maven和gradle通过mvn build或者gradle build方式执行,sbt这样也是可以的比如sbt compile,他们的特点是是启动一个java进程,命令执行结束之后,进程自动结束
SBT官方很得意的告诉你,他还有另外的方式,就是先执行SBT命令,进入了SBT的不退出的进程中(也可以称之为SBT容器,提供了一个SBT的命令行窗口),在这个容器中再执行compile命令,这样SBT的进程就持续存在,直到你输入exit命令退出进程,所以SBT官方说就更快了,本节主要使用容器这种方式
SBT我们常用的命令有
1.exit
退出sbt进程,或者叫退出sbt容器
2.tasks
这个命令可以查看,你都有哪些命令可以使用,本节说的常用命令他都会列出来
3.projects
输入这个命令,sbt会列出sbt的所有项目,当然多项目的时候这个命令才有意义,比如本文多项目配置章节,当你输入这个命令的时候,他会把root、core、entity、business、service、server六个项目都列出来
默认root项目前面会有一个*符号,表示他是当前的默认项目,你如果想改默认项目为server,那么需要输入project server,这个时候server就成为默认项目了,再输入projects命令,那么*就跑到server项目前头了
4.compile
编译当前项目,生成*.class文件到目录:projectName argetscala-2.12classes
注意要确认谁是当前项目,如果当前项目是root的话,他会把所有子项目都编译
5.clean
把compile生成的class文件都删除
6.run
运行当前项目的main class,如果项目有多个main class 他会列一个列一个所有mainClass的列表而且前头给你加1、2、3编号。你再输入编号回车,他就执行对应的mainClass,这个挺好
7.package
当前项目生成jar包到projectName argetscala-2.12projectName_version.jar
说明:这个命令只生成当前项目的jar包,不会复制依赖的jar包,如果要想把依赖的包都打进来需要使用SB插件,参见SBT打包章节
sbt命令执行说明
1.执行sbt命令的时候,如果不通过容器方式实现,可以直接输入命令 sbt projects,sbt执行命令完成后sbt进程自动结束
2.多项目模式,如果再容器内,可以设置当前项目在执行命令,可以通过"projectName/run"来执行
六、SBT打包(生成JAR包且有依赖包)
背景说明
1.生产环境需要把项目打包发布到docker容器或者服务器中,打包的时候也需要添加依赖包
2.sbt package命令只生成当前项目的jar包,不能添加依赖包
3.sbt assembly是一个解决这个问题的插件(对应的命令也是assembly),他可以添加依赖包但是我没有使用。
因为他有两个问题,首先是sbt assembly版本跟scala的版本匹配的乱七八糟老是有问题;另外他老师是有jar包里.class文件的冲突,特别是日志很烦人需要手工配置build.sbt文件挨个的去解决这些冲突。
sbt assembly参考资料https://www.jianshu.com/p/460dcb709ff6
4.本文使用sbt-native-packager插件,可以解决这些问题
sbt-native-packager官方文档参见:https://www.scala-sbt.org/sbt-native-packager/
1.启用sbt-native-packager插件
参见文档:https://www.scala-sbt.org/sbt-native-packager/gettingstarted.html
1)插件引用声明
首先在项目根目录下新增"project"文件夹,在这个文件夹下新增一个"plugins.sbt"文件,即新增一个文件"projectplugins.sbt",然后再这个文件里添加一行
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.9")
2)根项目build.sbt配置
子项目不需要做这些配置
修改根目录下的build.sbt文件,在项目设置上新增"enablePlugins(JavaAppPackaging)"调用,本文以上文的school为例来看root项目的build.sbt设置
/school/build.sbt配置
name := "school" javacOptions ++= Seq("-encoding", "UTF-8") ThisBuild / organization := "com.school" ThisBuild / version := sys.props.getOrElse("PACKAGE_VERSION", "2.3") ThisBuild / scalaVersion := "2.12.8" //enablePlugins(JavaAppPackaging)就是对项目启用了sbt-native-packager插件,每个项目都需要这么设置一下 lazy val root = project.in(file(".")).enablePlugins(JavaAppPackaging).aggregate(core,entity,business,service,server) lazy val core = project.in(file("school-core")) lazy val entity = project.in(file("school-entity")).dependsOn(core).enablePlugins(JavaAppPackaging) lazy val business = project.in(file("school-business")).dependsOn(entity).enablePlugins(JavaAppPackaging) lazy val service = project.in(file("school-service")).dependsOn(business).enablePlugins(JavaAppPackaging) lazy val server = project.in(file("school-server")).dependsOn(service).enablePlugins(JavaAppPackaging).settings( mainClass in Compile := Some("com.school.server.App") )
2 stage命令行执行打包
执行打包使用stage命令,下面举例对这个命令的执行过程
sbt
project server
stage
exit
3 查看打包结果
打完的包放到这个目录下"projectName argetuniversalstage",在这个目录下有两个目录bin和lib,
1)lib目录下就是当前项目的jar包和所有依赖的jar包
2)bin里是stage给生成的一些shell脚本和bat脚本,用来启动打包项目,即执行"java -jar xxx.jar"命令
4.sbt-native-packager相关说明
1.通过tasks命令找不到stage命令,但是不影响使用
2.一般打包的jar里MANIFEST.MF如果添加依赖的jar包名称,那么可以直接使用"java -jar xxx.jar"命令,但是stage没有这么做,所以才大费周章使用脚本启动,具体如何修改不知道,脚本能启动也行
3.sbt-native-packager的代理不知道怎么设置,可能sbt不支持插件的设置
七、参考资料
1.maven,sbt,gradle 对classifier支持