自己动手在写一个简单多模块项目,从头到尾遇到不少问题,所以记录下。
这里依赖是拆分的,每个模块有自己的依赖和配置,统一由根项目管理。最终sbt多模块打包成jar并发布到本地maven仓库由benchmark项目引用。
1. 创建一个sbt项目,命名dlsRpc
这是一个根项目,包含配置文件和依赖定义。
2. 创建三个子模块
根项目dlsRpc本身不包含任何代码(忽略benchmark模块,这个是maven项目)
3. 在build.sbt增加构建配置
将几个模块关联起来
import Dependencies.Versions
//工程通用配置
lazy val commonSettings = Seq(
organization := "io.growing",
version := "1.0.13",
scalaVersion := Versions.scala212,
Dependencies.commons
)
//根项目配置,benchmark与本项目无直接依赖关系,所以忽略
//benchmark以jar包的形式依赖dlsRpc
lazy val root = Project(id = "dlsRpc", base = file("."))
.settings(commonSettings).aggregate(consuls, core, commons)
//核心实现,依赖consuls,commons
lazy val core = Project(id = "dlsRpc-core", base = file("dlsRpc-core"))
.settings(commonSettings, Dependencies.core).dependsOn(commons, consuls)
//服务注册发现,可依赖commons但不能反向依赖core
lazy val consuls = Project(id = "dlsRpc-consul", base = file("dlsRpc-consul"))
.settings(commonSettings, Dependencies.consuls).dependsOn(commons)
//通用工具和隐式对象,不可反向依赖core,consuls
lazy val commons = Project(id = "dlsRpc-common", base = file("dlsRpc-common"))
.settings(commonSettings, Dependencies.common)
javacOptions ++= Seq("-encoding", "UTF-8")
javaOptions in run += "-Xmx1G"
//编译路径
//windows不能使用git cmd 命令行打包,需要使用sbt
//发布到本地maven仓库的时候,允许覆盖jar。
//发布到仓库后本地maven才能引入,而不再需要加入lib文件
publishM2Configuration := publishM2Configuration.value.withOverwrite(true)
publishConfiguration := publishConfiguration.value.withOverwrite(true)
publishLocalConfiguration := publishLocalConfiguration.value.withOverwrite(true)
这里四个模块都使用了通用设置commonSettings
,并且根项目dlsRpc依赖下面三个子模块,使用aggregate
设置根项目包含三个模块,每个模块使用dependsOn
设置模块依赖,一般模块的id和文件名是相同的,这里都使用了dlsRpc-*,如果深究这里区别是文件名称对应项目在电脑上存储的文件名称,id是项目导入后显示的模块名称。
settings传入的是可变参数,对于每个模块可以设置自己的依赖,如Dependencies.*
,当然还可以加入其它参数。
两个方法源码如下
可见
SettingsDefinition*
ProjectReference*
均是一个scala的可变长参数,可传入多个值, ss: _*
的作用是将可变长参数以“个体”的形式作为参数传入,如果不使用 :_*
则会以整体的形式传入。 Dependencies是一个object,包含所有依赖。
各个模块依赖的组合方式如下
import sbt.Keys.libraryDependencies
import sbt._
object Dependencies {
object Versions {
val scala212 = "2.12.7"
val log4j = "2.11.1"
val guava = "19.0"
val guice = "3.0"
val netty = "4.1.6.Final"
val logging = "3.9.2"
val slfj = "2.1.2"
val protostuff = "1.0.12"
val consul = "1.4.2"
val log4j_api = "11.0"
val cglib = "3.2.10"
val config = "1.3.4"
}
object Compiles {
lazy val config: ModuleID = "com.typesafe" % "config" % Versions.config
lazy val cglib: ModuleID = "cglib" % "cglib-nodep" % Versions.cglib
lazy val consulAPi: ModuleID = "com.ecwid.consul" % "consul-api" % Versions.consul
lazy val guava: ModuleID = "com.google.guava" % "guava" % Versions.guava
lazy val guice: ModuleID = "com.google.inject" % "guice" % Versions.guice
lazy val log4j2: Seq[ModuleID] = Seq(
"org.apache.logging.log4j" %% "log4j-api-scala" % Versions.log4j_api,
"org.apache.logging.log4j" % "log4j-api" % Versions.log4j,
"org.apache.logging.log4j" % "log4j-core" % Versions.log4j,
"org.apache.logging.log4j" % "log4j-slf4j-impl" % Versions.log4j)
lazy val protostuff: Seq[ModuleID] = Seq(
"com.dyuproject.protostuff" % "protostuff-core" % Versions.protostuff,
"com.dyuproject.protostuff" % "protostuff-runtime" % Versions.protostuff)
lazy val netty: Seq[ModuleID] = Seq(
"io.netty" % "netty-codec-http2" % Versions.netty,
"io.netty" % "netty-handler" % Versions.netty)
lazy val log: ModuleID =
"com.typesafe.scala-logging" %% "scala-logging" % Versions.logging
}
import Compiles._
//RPC调用
val core = libraryDependencies ++= protostuff ++ netty ++ Seq(guice, cglib)
//服务注册发现
val consuls = libraryDependencies ++= Seq(consulAPi)
//配置、工具、常量
val common = libraryDependencies ++= Seq(config)
//通用依赖
val commons = libraryDependencies ++= log4j2 ++ Seq(log, guava)
}
maven项目benchmark中引入多模块jar
maven项目不影响这里的sbt构建,这个项目在哪都可以。
sbt构建时不会编译maven项目,所以maven子项目需要单独编译。这里没有设置仓库,默认使用publishM2
将把jar包发布到/User/userName/.m2/repository/io/growing/,这里IDEA的maven本地仓库也要改成这个。
Scala命令行使用如下
这里如果修改了版本号,那么需要关闭这个窗口重新打开,新版本号才会在打包时生效。
如下图所示,默认携带了Scala的版本后缀
在benchmark的pom.xml文件中引用jar
注意:出现maven找不到jar中的依赖,可能是打包没有将依赖pom打进去,需要使用sbt的publishM2
插件,不然maven仓库无法读取到依赖。
在使用publishM2的时候可能出现无法写入到本地
这是因为相同版本号只能存在一个,虽然配置build.sbt覆盖写,但是好像并不会生效,目前还没解决,所以采用修改版本即可。版本号就是通用配置中的version
属性
完整的项目结构
具体源码查看一个简单的Scala RPC