为什么Groovy编译器似乎会生成Java 1.5版本的代码?

5

在遇到不同JSE版本之间的问题后,我正在尝试记录用于编译的Java编译器版本(实际上是Groovy 2.1.9、Grails 2.3.8、Java 1.7.0_60)。

经过一番搜寻,我构建了以下代码片段来读取类的前导字节 - 参见/http://en.wikipedia.org/wiki/Java_class_file#General_layout

(将路径更改为与包名称匹配的类):

class CompilerVersionSupport {

  public static String getVersion() {
    String classAsPath = 'com/my/organisation/CompilerVersionSupport.class';
    InputStream stream = (new CompilerVersionSupport()).getClass().getClassLoader().getResourceAsStream(classAsPath);
    DataInputStream ins = new DataInputStream (stream)

    assert( ins.readUnsignedShort() == 0xcafe )    
    assert( ins.readUnsignedShort() == 0xbabe )
    int minor = ins.readUnsignedShort();
    int major = ins.readUnsignedShort();
    ins.close();
    int javaVersion = major - 44 
    return "1.$javaVersion"
    }     
}

问题在于它返回了1.5。

可能出了什么问题?

  • 查尔斯

你是如何编译的?你指定了目标版本吗? - Syam S
在 BuildConfig.groovy 文件中:grails.project.target.level = 1.7 - CharlesW
这个问题之前已经出现过(http://stackoverflow.com/questions/14176878/forcing-javacs-target-version-while-building-a-grails-application),但并没有得到真正的答案。我只是随便猜测,你是否尝试将*grails.project.source.level*和*grails.project.target.level*都设置为1.7? - Keegan
哦,还有你是直接使用 Grails 吗?还是将 Grails 与其他构建工具(Gradle、Maven 等)集成在一起?如果是这样,你可能需要在构建工具和 Grails 本身中都设置所需的字节码。 - Keegan
@Keegan - 感谢你的建议。但是,是的,这两个项目都已设置好,并且是直接运行Grails。 - CharlesW
3个回答

6
默认的Groovy行为是不会使用与所使用的JDK相同的字节码版本编译代码。出于兼容性原因,默认值为1.5。如果您想让编译器输出更新的字节码,您需要明确设置。
例如,如果您正在使用Maven编译代码,可以使用GMavenPlus插件。请参见targetBytecode参数的描述。
如果您没有使用Maven,则可以使用-Dgroovy.target.bytecode=1.7或研究适用于您特定构建工具的可能性。

有没有关于如何在Grails中实现这个的建议?我在BuildConfig.groovy文件中添加了_groovy.target.bytecode=1.7_,但我不认为它起作用了,因为它对一个愚蠢的值也没有抱怨。 - CharlesW
这并不完全正确,它取决于您的环境。1.5是GMavenPlus的默认值(遵循Maven编译器插件建立的模式)。Gradle默认使用您用于编译的JDK。我认为Gant会采用Groovyc设置的默认值(根据Groovy版本而异)。Grails似乎除了Gant的默认值之外还有自己的默认值。 - Keegan
@xwid 我在句末加了“IMHO” :) 所以我并不是说这是绝对的真相。但对我来说,这是最合理的解释 - 除非绝对必要,否则不要切断运行旧版Java的人。当他们决定无法再使用1.5字节码时,他们会向更高版本迁移,我认为。 - Daniel Kvasnicka
进一步澄清:最新版本的Groovy和2.1.9(@CharlesW使用的版本)在没有手动覆盖时都将1.5作为默认值。请查看Groovy源代码。 - Daniel Kvasnicka
@CharlesW 你试过使用“-D” JVM 表示法来传递配置吗?这个方法可行吗? - Daniel Kvasnicka
显示剩余6条评论

0

如果您正在使用Maven作为构建工具,那么很有可能它正在使用gmavenplus-plugin来编译Groovy。要找出生成的字节码的目标Java版本,我查看了应用程序使用的gmavenplus-plugin的pom文件:~/.m2/repository/org/codehaus/gmavenplus/gmavenplus-plugin/1.5/gmavenplus-plugin-1.5.pom

在该文件中,我看到了这个,注意<javaVersion/>

 <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <mavenVersion>2.2.1</mavenVersion>
    <coberturaPluginVersion>2.7</coberturaPluginVersion>
    <javadocPluginVersion>2.10.1</javadocPluginVersion>
    <!-- these are properties so integration tests can use them -->
    <javaVersion>1.5</javaVersion>
    <dependencyPluginVersion>2.10</dependencyPluginVersion>
    <compilerPluginVersion>3.2</compilerPluginVersion>
    <junitVersion>4.12</junitVersion>
    <surefirePluginVersion>2.18.1</surefirePluginVersion>
    <pluginPluginVersion>3.4</pluginPluginVersion>
    <!-- this is a property so that site generation can use it -->
    <sourcePluginVersion>2.4</sourcePluginVersion>
    <!-- this is a property so that site generation and integration tests can use it -->
    <groovyVersion>2.4.1</groovyVersion>
  </properties>
...
<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${compilerPluginVersion}</version>
        <configuration>
          <source>${javaVersion}</source>
          <target>${javaVersion}</target>
        </configuration>
      </plugin>

我使用IntelliJ作为IDE。 IntelliJ会自动将语言级别设置为Java 1.5。 即使我更改它,当我重新导入项目时,它也会重置回Java 1.5(我已模糊敏感信息)。

enter image description here


-1

我认为问题出在你用来查找类版本的程序上。如果断言未启用,则流不会读取前两个无符号短整数,因此后续的次要和主要读取语句将分别导致0Xcafe和0xbabe。尝试启用断言或尝试使用if检查。

public static String getVersion() throws Exception {
    String classAsPath = "com/my/organisation/CompilerVersionSupport.class";
    InputStream stream = (new CompilerVersionSupport()).getClass().getClassLoader().getResourceAsStream(classAsPath);
    DataInputStream ins = new DataInputStream(stream);

    if(ins.readUnsignedShort() != 0xcafe) throw new AssertionError("Invalid Class");
    if(ins.readUnsignedShort() != 0xbabe) throw new AssertionError("Invalid Class");
    int minor = ins.readUnsignedShort();
    int major = ins.readUnsignedShort();
    ins.close();
    int javaVersion = major - 44;
    return "1." + javaVersion;
}

谢谢回复,但如果那是真的,我认为我会得到0xbabe作为值,而不是49。 - CharlesW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接