当前位置: 主页 > JAVA语言

java jar 最小依赖包-java jar 执行jar包

发布时间:2023-02-09 07:06   浏览次数:次   作者:佚名

我已经使用 Scala 很长时间了。 SBT是最方便的构建工具,但是我总是遇到一些SBT带来的奇怪问题。 今天,我将通过本文研究SBT的常用用法,并尝试克服大部分坑。

SBT常用命令

clean:清除目标文件夹下生成的所有文件。

编译:编译src/main/scala、src/main/java和项目根目录下的文件。

run:编译代码,运行项目中的main方法。 如果有多个,SBT 会要求您选择一个。

package:将src/main/scala、src/main/java、src/main/resources下的文件打包成jar包。

test:编译并运行所有测试用例。

doc:为您的 Scala 源代码生成相应的 API 文档。

reload:重新加载所有sbt构建相关的文件,比如build.sbt、project/*。

publish:将你的项目发布到远程仓库。

publishLocal:将项目发布到本地ivy仓库。

如何查看执行命令的关键输出

使用last 命令查看上次执行命令的关键输出,如warn 或error 信息。 比如你用compile编译,输出一堆log,最后报错,如果你想看看是哪部分有问题,可以用last compile命令打印出来。

使用 SBT 和 ScalaTest 进行测试

首先在build.sbt中添加scalatest包:

"org.scalatest" %% "scalatest" % "3.0.5" % Test

然后在src/main/scala下创建一个Hello.scala的源文件:

package ink.baixin.scalalearning.sbt
object Hello extends App {
  val p = Person("Ink Bai")
  println("Hello from " + p.name)
}
case class Person(var name: String)

然后在src/test/scala下编写相应的测试用例:

package ink.baixin.scalalearning.sbt
import org.scalatest.FunSuite
class HelloTests extends FunSuite {
  test("the name is set correctly in constructor") {
    val p = Person("Ink Bai")
    assert(p.name == "Ink Bai")
  }

java jar 最小依赖包_java jar依赖原理_java jar 执行jar包

test("a Person's name can be changed") { val p = Person("Will") p.name = "William" assert(p.name == "William") } }

现在就可以使用sbt test命令来执行测试用例了,如下:

$ sbt test
...
[info] HelloTests:
[info] - the name is set correctly in constructor
[info] - a Person's name can be changed
[info] Run completed in 581 milliseconds.
[info] Total number of tests run: 2
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 2, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 6 s, completed Aug 22, 2018 11:55:13 AM

如何管理依赖关系?

build.sbt 中的配置行必须用空行分隔。 这是一个简单但全面的文件,具有一个依赖项:

name := "scala-learning"
version := "0.1"
scalaVersion := "2.12.4"
libraryDependencies ++= "org.scalatest" %% "scalatest" % "3.0.5" % Test

如果要添加多个依赖包,可以定义一个Seq:

libraryDependencies ++= Seq(
  "io.netty" % "netty-all" % "4.1.25.Final",
  "org.apache.calcite" % "calcite-core" % "1.16.0",
  "org.scalatest" %% "scalatest" % "3.0.5" % Test
)

java jar依赖原理_java jar 最小依赖包_java jar 执行jar包

从上面的例子可以推断,build.sbt的入口其实是一个简单的键值对,其中name、version、scalaVersion等是最常见的SBT键。 SBT 通过创建一个大 Map 来构建一个项目。

这种方式是管理依赖的方式(直接把jar包放在项目的lib目录下是一种非管理依赖方式),可以自动下载和管理某个jar包需要的其他依赖包。 SBT 底层使用 Apache Ivy 来管理依赖。 Ivy也可以用于Ant和Maven,这样你就可以通过SBT方便的在Scala项目中使用Java包。

一般添加依赖包有两种方式。 第一个需要指定 groupID、artifactID 和 revision:

libraryDependencies += groupID % artifactID % revision

例子是:

libraryDependencies += "org.scalatest" % "scalatest_2.12" % "3.0.5"

第二个有一个可选的参数配置:

libraryDependencies += groupID % artifactID % revision % configuration

或者上面的例子:

libraryDependencies += "org.scalatest" % "scalatest_2.12" % "3.0.5" % Test

可以看到多了一个Test,这个描述只是为了测试用的,这个配置可以理解为类似于Maven中的scope。 下面是对应的Maven配置:


  org.scalatest
  scalatest_2.12
  3.0.5
  test

可以看到上面的scalatest_2.12有2.12,其实就是Scala版本。 只有基于 Scala 的 Jar 包才会有这个依赖的 Scala 版本号信息。 而我们在build.sbt中通过scalaVersion设置了Scala版本,那么是不是可以省略这个2.12或者使用scalaVersion的参数设置呢? 答案是肯定的,通过以下形式:

libraryDependencies += groupID %% artifactID % revision % configuration

细心的读者应该能看出来,第一个%变成了%%。 在groupID和artifactID之间加上%%后,artifactID不需要指定Scala版本,SBT会自动将自己设置的Scala版本添加到这个值之后,如下:

scalaVersion := "2.12.4"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % Test

相当于:

libraryDependencies += "org.scalatest" % "scalatest_2.12" % "3.0.5" % Test

如何为项目创建子项目

首先创建两个子项目,目录结构如下:

├── subproject1
│   ├── build.sbt
│   └── src
│       └── main
│           └── scala
│               └── Foo.scala
├── subproject2

java jar 执行jar包_java jar依赖原理_java jar 最小依赖包

│ ├── build.sbt │ └── src │ └── main │ └── scala │ └── Bar.scala

子项目的build.sbt如下:

// name := "scala-learning subproject2"
name := "scala-learning subproject1"
version := "0.1"
scalaVersion := "2.12.4"

Foo.scala 如下:

package ink.baixin.scalalearning.subproject1
case class Foo (name: String)

Bar.scala 如下:

package ink.baixin.scalalearning.subproject2
import ink.baixin.scalalearning.subproject1._
case class Bar(name: String)
object HelloFoo extends App {
  println(Foo("Hello Foo"))
}

然后在根目录下的build.sbt中添加如下内容:

lazy val subproject1 = (project in file("subproject1"))
lazy val subproject2 = (project in file("subproject2")).dependsOn(subproject1)
// aggregate: running a task on the aggregate project will also run it on the aggregated projects.
// dependsOn: a project depends on code in another project.
// without dependsOn, you'll get a compiler error: "object bar is not a member of package

java jar 最小依赖包_java jar 执行jar包_java jar依赖原理

// com.alvinalexander". lazy val root = (project in file(".")) .aggregate(subproject1, subproject2) .dependsOn(subproject1, subproject2)

这里可以看到subproject1和subproject2分别代表两个子项目的SBT,然后定义root为父目录的SBT。 其中dependsOn是指依赖关系,其依赖项目的代码可以直接使用; 而aggregate是指联合关系,如果父项目执行compile等任务,那么两个子项目也会相应执行该任务。

我们在父项目中写一个对象来测试是否可以实现代码依赖:

package ink.baixin.scalalearning.sbt
import ink.baixin.scalalearning.subproject2._
import ink.baixin.scalalearning.subproject1._
object MultipleProjectsExample extends App {
  println(Foo("I'm a Foo"))
  println(Bar("I'm a Bar"))
}

结果:

Foo(I'm a Foo)
Bar(I'm a Bar)

以上是在 Scala 中创建多模块项目的示例。 在实际开发中,一些大型项目可能内部业务复杂,模块较多。 那么我们可以考虑把它们分成几个小的部分,将公共的部分分离出来作为一个依赖的项目java jar 最小依赖包,这样可以让我们的代码更加的清晰和条理。

如何指定主类

可以通过添加以下内容来设置主类:

// set the main class for packaging the main jar
mainClass in (Compile, packageBin) := Some("ink.baixin.scalalearning.sbt.Hello")
// set the main class for the main 'sbt run' task
mainClass in (Compile, run) := Some("ink.baixin.scalalearning.sbt.Hello")

如何使用来自github的代码

现在我们想直接使用一个github开源库的代码java jar 最小依赖包,可以这样配置:

lazy val githubProject = RootProject(uri("https://github.com/Trigl/akka-learning.git"))
lazy val root = (project in file(".")).dependsOn(githubProject)

这样设置之后,就可以在代码中直接引用开源库的代码了。

如何在 SBT 中设置 Ivy 存储库

可以使用 build.sbt 中的解析器添加 Ivy 存储库:

java jar 执行jar包_java jar 最小依赖包_java jar依赖原理

resolvers += "repository name" at "location"

举例如下:

resolvers += "Java.net Maven2 Repository" at "http://download.java.net/maven/2/"

当然你也可以用Seq添加多个仓库:

resolvers ++= Seq(
  "Typesafe" at "http://repo.typesafe.com/typesafe/releases/",
  "Java.net Maven2 Repository" at "http://download.java.net/maven/2/"
  )

如何设置SBT的日志级别

这很简单,只需添加以下内容:

logLevel := Level.Info

如何部署一个完整的可执行Jar包

这个问题看起来很容易。 你可以直接使用 sbt 包。 其实并没有用,因为package命令只会保存本项目的src/main/scala、src/main/java、src/main/resources下的文件。 它被打包成Jar包,但是有两个重要且必要的内容不会被打包。 这两个类别是:

那么如何解决这个问题呢,推荐的方法是使用开源工具sbt-assembly来打包。 该项目将包括所有需要的依赖项,并部署一个可以在分布式环境中执行的 Jar 包。

那么如何使用呢,首先在项目目录下添加文件assembly.sbt:

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.7")

然后执行sbt assembly打包,就这么简单,当然你可以在build.sbt中添加一些更详细的配置。

如何发布你的代码

您可以使用命令 publish 或 publishLocal 来发布代码。 发布是将项目发布到远程仓库,所以需要设置远程仓库的地址。 现在我设置一个本地路径作为仓库地址:

// publish project to a remote repository
publishTo in ThisBuild := Some(Resolver.file("file", new File("/Users/will/tmp")))

然后执行sbt publish,结果如下:

...
[info]  published scala-learning_2.12 to /Users/will/tmp/scala-learning/scala-learning_2.12/0.1.part/scala-learning_2.12-0.1.pom
[info]  published scala-learning_2.12 to /Users/will/tmp/scala-learning/scala-learning_2.12/0.1.part/scala-learning_2.12-0.1.jar
[info]  published scala-learning_2.12 to /Users/will/tmp/scala-learning/scala-learning_2.12/0.1.part/scala-learning_2.12-0.1-sources.jar
[info]  published scala-learning_2.12 to /Users/will/tmp/scala-learning/scala-learning_2.12/0.1.part/scala-learning_2.12-0.1-javadoc.jar
[info]  publish commited: moved /Users/will/tmp/scala-learning/scala-learning_2.12/0.1.part
[info]      to /Users/will/tmp/scala-learning/scala-learning_2.12/0.1
[success] Total time: 1 s, completed Aug 22, 2018 4:13:46 PM

另一个发布命令是publishLocal,它将项目发布到本地ivy仓库。 这个包在本机其他项目中是可以直接调用的,只要在其他项目的build.sbt中写好这个包的依赖即可,如下;

libraryDependencies += "scala-learning" %% "scala-learning" % "0.1"

参考

Scala 食谱

build.sbt 例子

项目源码