如何指定/确定运行fat jar所需的最低JDK版本?

huangapple go评论91阅读模式
英文:

How do you specify/figure out which minimum JDK is required for running the fat jar?

问题

我在一个项目中使用了sbt-assembly,其中我有一些Java 14的jar包,我的本地机器默认的JDK是JDK 8。

sbt assembly任务成功执行并生成了一个fat jar。

当我使用JDK 8运行它时,我收到以下错误:

Exception in thread "main" java.lang.UnsupportedClassVersionError: javafx/event/EventTarget has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0

我需要JDK 11(版本55.0)。当我在我的shell中设置JDK 11时,我可以运行这个fat jar。

是否有一种方法可以在build.sbt文件中明确指定目标JDK版本?
此外,尽管我在依赖中有Java 14的jar包,但应用程序在JDK 11上运行得很好。这只是Java卓越的向后兼容性的一个例子吗?我想知道还有什么其他因素可能在起作用。

这是我的build.sbt文件的内容:

name := "scalafx-app"

version := "0.1"

scalaVersion := "2.13.3"

scalacOptions += "-Ymacro-annotations"

useCoursier := false
assemblyMergeStrategy in assembly := {
  case "module-info.class" => MergeStrategy.concat
  case x =>
    val oldStrategy = (assemblyMergeStrategy in assembly).value
    oldStrategy(x)
}
lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.1.1"

lazy val osName = System.getProperty("os.name") match {
  case n if n.startsWith("Linux") => "linux"
  case n if n.startsWith("Mac") => "mac"
  case n if n.startsWith("Windows") => "win"
  case _ => throw new Exception("Unknown platform!")
}

lazy val javaFXModules = Seq("base", "controls", "fxml", "graphics", "media", "web")


lazy val root = (project in file("."))
  .settings(
    libraryDependencies += scalaTest % Test,

    // scalafx
    libraryDependencies += "org.scalafx" %% "scalafx" % "14-R19",
    libraryDependencies ++= javaFXModules.map(m =>
      "org.openjfx" % s"javafx-$m" % "14.0.1" classifier(osName) withJavadoc()
    ),
    libraryDependencies += "org.scalafx" %% "scalafxml-core-sfx8" % "0.5",

    // javafx custom components
    libraryDependencies += "com.jfoenix" % "jfoenix" % "9.0.9",
    libraryDependencies += "org.kordamp.ikonli" % "ikonli-javafx" % "11.4.0",
    libraryDependencies += "org.kordamp.ikonli" % "ikonli-material-pack" % "11.4.0",

    // json parsing
    libraryDependencies += "com.typesafe.play" %% "play-json" % "2.9.0",
    libraryDependencies += "com.squareup.moshi" % "moshi" % "1.9.3",

      // logging
    libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2",
    libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3",

  )
英文:

I used sbt-assembly on a project where I have some java 14 jars, and my local machine has JDK 8 as the default JDK.

The sbt assembly task was successful and produced a fat jar.

When I run it with JDK 8, I get the error:

Exception in thread "main" java.lang.UnsupportedClassVersionError: javafx/event/EventTarget has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0

JDK 11 (version 55.0) is the one I need. And sure enough, when I set JDK 11 on my shell, I can run the fat jar.

Is there a way to be explicit about the target JDK version in the build.sbt file?
Also, I'm surprised that even though I have Java 14 jars in the dependency, the application runs fine on JDK 11. Is it just an example of Java's supreme backwards compatibility in action? I would like to know what else could be at work.

This is what my build.sbt looks like

name := "scalafx-app"
version := "0.1"
scalaVersion := "2.13.3"
scalacOptions += "-Ymacro-annotations"
useCoursier := false
assemblyMergeStrategy in assembly := {
case "module-info.class" => MergeStrategy.concat
case x =>
val oldStrategy = (assemblyMergeStrategy in assembly).value
oldStrategy(x)
}
lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.1.1"
lazy val osName = System.getProperty("os.name") match {
case n if n.startsWith("Linux") => "linux"
case n if n.startsWith("Mac") => "mac"
case n if n.startsWith("Windows") => "win"
case _ => throw new Exception("Unknown platform!")
}
lazy val javaFXModules = Seq("base", "controls", "fxml", "graphics", "media", "web")
lazy val root = (project in file("."))
.settings(
libraryDependencies += scalaTest % Test,
// scalafx
libraryDependencies += "org.scalafx" %% "scalafx" % "14-R19",
libraryDependencies ++= javaFXModules.map(m =>
"org.openjfx" % s"javafx-$m" % "14.0.1" classifier(osName) withJavadoc()
),
libraryDependencies += "org.scalafx" %% "scalafxml-core-sfx8" % "0.5",
// javafx custom components
libraryDependencies += "com.jfoenix" % "jfoenix" % "9.0.9",
libraryDependencies += "org.kordamp.ikonli" % "ikonli-javafx" % "11.4.0",
libraryDependencies += "org.kordamp.ikonli" % "ikonli-material-pack" % "11.4.0",
// json parsing
libraryDependencies += "com.typesafe.play" %% "play-json" % "2.9.0",
libraryDependencies += "com.squareup.moshi" % "moshi" % "1.9.3",
// logging
libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2",
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3",
)

答案1

得分: 3

A JAR就像是一个类的压缩文件,每个类都可以使用javap命令来检查,以查看它们需要的JDK版本,方法是查看"major version"字段的值;请参考这里

如果您想确保类被编译到特定的Java版本,您可以使用releasetarget scalac选项

像这样:

// 确保编译器生成Java 8字节码。
scalacOptions ++= Seq("-release", "8", "-target:8")
  • release用于指定使用哪个Java sdtlib
  • target用于指定生成的字节码版本。
英文:

A JAR is just like a zip of classes, each class is the one that you can check with javap to see which JDK version they need by looking at the value of the "major version" field; see this.

If you want to ensure the classes are compiled to a specific Java version, you can use the release & target scalac options.

Like this:

// Ensure the compiler emits Java 8 bytecode.
scalacOptions ++= Seq("-release", "8", "-target:8")
  • release is used to specify which Java sdtlib is used.
  • target is used t specify which bytcode version is emitted.

huangapple
  • 本文由 发表于 2020年8月13日 22:14:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/63397117.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定